Skip to main content

pymecheye

CI codecov

Documentation

简体中文

High-performance Python bindings for Mech-Eye area-scan 3D cameras, built with nanobind and the vendor C++ SDK.

pymecheye removes the dependency on vendor-precompiled Python SDKs: installation compiles a thin native binding for your current CPython version and threading ABI. It also provides convenient camera lifecycle management, NumPy capture, metadata access, and error handling. NumPy is the only Python runtime dependency.

Install the distribution as pymecheye and keep importing mecheye:

from mecheye.area_scan_3d_camera import Camera

The area-scan API targets source compatibility with mecheyeapi 2.6.0: keep your existing mecheye imports and camera calls, and change the installed distribution to pymecheye. Laser profiler interfaces are excluded. Install only one distribution providing mecheye in each environment.

Features

  • Native C++ capture and point cloud conversion through a thin nanobind layer.
  • Blocking SDK calls release Python thread state. Camera and SDK access use explicit locks, supporting regular and free-threaded CPython without relying on the GIL. SDK calls are serialized, including across camera instances.
  • Independent NumPy arrays that remain valid after another capture or camera closure; point clouds preserve their organized pixel grid.
  • Camera discovery, connections, capture variants, frame containers, parameters, user sets, point cloud processing and exports, events, and hand-eye calibration, preserving SDK status returns.
  • NumPy capture and lifecycle conveniences on the same Camera object, including context management and optional-IP lazy connection. SDK methods never retry implicitly.
  • Device information, camera intrinsics, depth-source 2D exposure configuration, and NumPy grayscale conversion.

The design targets efficient native data capture; it does not claim a measured speedup over the vendor Python bindings. Arrays are copied into owned buffers rather than exposing SDK frame memory.

Requirements

Requirement Support
Platform Linux or Windows, x86_64
Python CPython 3.10+; free-threaded CPython 3.13+
Vendor SDK Mech-Eye C++ SDK 2.6.0, installed separately
Linux build tools C++17 GCC or Clang; glibc, libstdc++, libgcc, libgomp
Windows build tools MSVC x64 C++17 toolchain, Windows SDK, VC++ 2015-2022 x64 Redistributable

The extension is compiled for each Python minor version and threading ABI; there is no single binary shared across Python versions. The nanobind FREE_THREADED option is always enabled and takes effect on free-threaded interpreters. Compatibility with future Python versions depends on nanobind, NumPy, and compiler support. Other SDK versions, PyPy, ARM, macOS, and MinGW are not currently supported.

Installation

Install the vendor C++ SDK first. Build and runtime use these defaults:

Platform SDK root Expected files
Linux /opt/mech-mind/mech-eye-sdk/ include/, lib/libMechEyeApi.so
Windows C:/Mech-Mind/Mech-Eye SDK-2.6.0/API/ include/, lib/MechEyeApi.lib, dll/MechEyeApi.dll

Set MECHEYE_SDK_ROOT to an absolute SDK directory to override the default:

# Linux
export MECHEYE_SDK_ROOT=/absolute/path/to/mech-eye-sdk
# Windows PowerShell
$env:MECHEYE_SDK_ROOT = 'C:\path\to\Mech-Eye SDK\API'

An unset or empty variable selects the platform default. An invalid override fails directly. Headers, link library, and runtime must come from the same SDK installation. The runtime SHA-256 must match the one used to compile the binding.

Add it to your uv project:

uv add pymecheye

Or install directly from Git:

uv add git+https://github.com/slabpanic/pymecheye.git

uv records the dependency and resolves a Git commit in the lockfile. You can select a released tag with --tag or a commit with --rev.

Both commands install a regular package. With source-only PyPI releases, uv resolves the build dependencies and compiles the extension for the consuming project's Python and local C++ SDK; Git installations also build from source. The C++ SDK must already be installed: uv builds this binding, not the vendor SDK. uv can reuse previously compiled wheels from its cache. Keep any SDK environment override set for both building and running your application. The package never copies vendor headers, import libraries, or runtime binaries into its source archives or wheels.

For a local checkout, use a regular installation:

uv add ../pymecheye

Or install from inside the checkout with pip:

python -m pip install .

Source changes take effect after rebuilding and reinstalling. After changing SDK files, clear the previously compiled package, rebuild, and restart Python:

uv cache clean pymecheye
uv sync --reinstall-package pymecheye

Inside this repository, add --no-editable to uv sync and uv run to keep the project installed as a regular package. For Git and registry dependencies, compiled wheels may be cached; clearing the package cache forces a source rebuild when the SDK changes. Each Python minor version, threading ABI, and platform requires its own compatible environment and build.

API compatibility

Module API
mecheye.area_scan_3d_camera Camera, metadata, frames, image/cloud containers, parameter descriptors, mapping, transformations, events, and hand-eye calibration
mecheye.shared ErrorStatus, common data types, parameters, user sets, version and log-export utilities
mecheye.area_scan_3d_camera_utils Print and interactive selection helpers, plus image_to_gray()

SDK operations retain their signatures and return conventions. For example, Camera.connect() returns ErrorStatus; captures fill the supplied frame and return ErrorStatus; scalar parameter reads return [status, value], while structured reads return (status, value). SDK error codes and descriptions pass through unchanged. disconnect() returns None.

The compatibility target is the public Python area-scan API of version 2.6.0, not laser profilers or SWIG implementation internals. Direct this pointer access, foreign SWIG pointer interoperability, and __swig_destroy__ are not provided. Python manages native object lifetimes; thisown is a compatibility attribute and cannot transfer ownership of native memory. Future SDK versions may add interfaces that require further updates.

Camera API example

Existing scripts can keep this calling pattern:

from mecheye.area_scan_3d_camera import Camera, Frame3D

camera = Camera()
try:
    status = camera.connect("192.168.1.100")
    if not status.is_ok():
        raise RuntimeError(status.error_description)

    frame = Frame3D()
    status = camera.capture_3d(frame)
    if not status.is_ok():
        raise RuntimeError(status.error_description)

    points = frame.get_untextured_point_cloud().data()
finally:
    camera.disconnect()

Image and cloud accessors return independent NumPy copies with the documented dtypes and layouts. XYZ values use float32; grayscale and BGR use uint8. Textured clouds expose vertices() and colors(), with normals() available on their normal-bearing variant. For compatibility, empty accessors return np.zeros(1).

Pixel objects returned by at() or indexing support field assignment. A pixel reference becomes invalid after its container is resized or released, raising ReferenceError instead of accessing freed memory. Camera, frame, parameter, and pixel operations are serialized. SDK object construction and destruction run on the dedicated worker.

For multiprocessing, prefer multiprocessing.get_context("spawn") and create SDK objects inside each child process. SDK calls after a fork that inherits mecheye raise RuntimeError before acquiring inherited locks. Such forks are unsupported: the vendor runtime can also abort during child process shutdown. To use forkserver, call multiprocessing.set_forkserver_preload([]) before starting the server, so it does not preload a main module that imports mecheye. Exchange owned NumPy arrays between processes rather than SDK objects.

Integrated conveniences

The Camera object provides NumPy capture and context management:

from mecheye.area_scan_3d_camera import Camera

with Camera("192.168.1.100") as camera:
    camera.configure_depth_source_2d_exposure("flash")
    image, points_mm = camera.capture_points(timeoutMs=10000)
    device_info = camera.device_info
    camera_parameters = camera.camera_parameters

print(image.shape, image.dtype)
print(points_mm.shape, points_mm.dtype)

Camera() follows the explicit-connect SDK workflow. An optional IP connects on context entry or first convenience capture/configuration. Importing the package or constructing a camera does not connect. close() aliases disconnect().

capture_points() composes capture_depth_source_2d_and_3d(), point cloud mapping, grayscale conversion, and owned NumPy output. It returns a grayscale uint8 image (h, w) and an organized float64 cloud (h, w, 3) on the same grid, in millimeters with NaN invalid points. A previously selected point cloud unit is restored on success and failure. This capture requires a camera model with an external 2D camera, as specified by the SDK.

device_info and camera_parameters query the connected camera and return plain Python dictionaries. Exposure configuration accepts "timed" or "flash" and returns the mode read back from the current user set. Synchronize concurrent writes or resizing of returned NumPy arrays in your application.

Errors, events, and utilities

SDK methods return ErrorStatus. Convenience methods raise MechEyeConnectionError, MechEyeConfigurationError, MechEyeCaptureError, or MechEyePointCloudError, derived from MechEyeError and available from mecheye.shared and the package root.

Override EventCallbackBase methods and register with CameraEvent, using the camera API. Event data and payloads are copied before Python dispatch, so callbacks can inspect them while a capture is still running. Callback exceptions are retained and retrieved with camera.pop_callback_errors(); none are printed or discarded. Callback code that calls another SDK operation waits for the ongoing operation to finish.

No automatic Python logging is added. SDK standard-stream output on Linux is silenced on the dedicated worker without redirecting the host process's streams. The SDK may still create its own diagnostic files. show_error() and print_*() helpers print only when explicitly called; interactive helpers ask for input only when called.

from mecheye.area_scan_3d_camera_utils import image_to_gray

gray = image_to_gray(bgr_image)

This utility accepts grayscale, BGR, and BGRA arrays and returns an independent array, ignoring alpha. Color input supports uint8, uint16, and float32.

The capture example prints array shapes, dtypes, and valid point count:

uv run --no-editable python examples/capture.py 192.168.1.100

It connects to a camera and opens no GUI. Pillow, OpenCV, and Open3D are not Python dependencies of this project.

Documentation

Read the English documentation or 简体中文文档 for setup, camera workflows, and API reference pages generated from the installed binding's docstrings. To build the same documentation locally with an installed C++ SDK:

uv sync --locked --no-editable --group docs
uv run --no-sync sphinx-build -n -W --keep-going -b html -D language=en docs docs/_build/en
uv run --no-sync sphinx-build -n -W --keep-going -b html -D language=zh_CN docs docs/_build/zh_CN

Open docs/_build/en/index.html or docs/_build/zh_CN/index.html. The build checks public docstrings and treats warnings and broken cross-references as errors. Generated API pages and HTML are ignored by Git. GitHub Actions also builds the documentation and saves an HTML preview artifact.

The Read the Docs configuration downloads and verifies the external SDK, compiles the binding, and builds with the locked documentation dependencies. To activate publishing, import slabpanic/pymecheye into Read the Docs with the project slug pymecheye, authorize its GitHub App, and use main as the default branch. Subsequent pushes trigger the hosted build automatically; activate release-tag versions to publish stable documentation. Import the same repository as pymecheye-zh-cn with Simplified Chinese selected, and add it under the parent project's Translations settings to publish /zh-cn/ documentation. No Read the Docs token is needed in GitHub Actions. The hosted URL becomes available after the project is imported and its first build succeeds. See docs/development.rst for the complete setup.

Development

uv sync --no-editable
uv lock --check
uv run --no-editable black --check src/mecheye/*.py examples/*.py tests/*.py
uv run --no-editable python -m compileall -q src/mecheye
uv run --no-editable python -c "import mecheye"
uv run --no-editable python -m unittest discover -s tests
uv pip check
uv build --sdist

The format, compile, import, and dependency checks do not connect to a camera. The unit tests use native SDK data operations and simulated device responses; no camera or network discovery is required. GitHub Actions builds the extension on Linux with Python 3.10–3.14, 3.13t, and 3.14t, and on Windows with Python 3.10, 3.14, and 3.14t. Free-threaded tests verify that importing the binding keeps the GIL disabled. The workflow downloads SDK 2.6.0 from the vendor, verifies pinned SHA-256 checksums, and unpacks it outside the repository. Windows uses the runner's WiX tools and an administrative MSI image; hardware captures are not part of CI.

To run Python branch coverage locally:

uv sync --locked --no-editable --group test
uv run --no-sync coverage run -m unittest discover -s tests
uv run --no-sync coverage report --fail-under=95
uv run --no-sync coverage xml

To also measure the C++ binding on Linux with GCC:

uv sync --locked --no-editable --group test --group native-coverage \
  --no-build-isolation-package pymecheye --reinstall-package pymecheye \
  -Ccmake.define.MECHEYE_ENABLE_COVERAGE=ON -Cbuild-dir=build/coverage
uv run --no-sync coverage run -m unittest discover -s tests
uv run --no-sync gcovr --root . --filter 'src/mecheye/' --merge-lines \
  --xml-pretty --output coverage-native.xml --print-summary build/coverage

Native coverage measures this repository's C++ code, excluding vendor binaries and nanobind implementation code. Restore a regular build with uv sync --locked --no-editable --reinstall-package pymecheye after profiling. CI requires at least 95% Python coverage and 80% native line coverage, and retains both XML reports as workflow artifacts. Codecov tracks Python and native reports with separate flags.

Enable this repository in Codecov and install its GitHub App to enable pull request checks and comments. Uploads use OIDC, so no CODECOV_TOKEN secret is needed. Fork pull requests run all tests and generate reports, but do not perform authenticated uploads; reports remain available in the workflow artifacts. Uploads run for main-branch pushes, internal pull requests, and manual workflow runs.

Source releases

Pushing a version tag such as v0.1.0 runs release.yml. The workflow requires the tag to match the normalized project.version in pyproject.toml, validates the source archive and PyPI metadata, and runs the complete CI workflow before uploading the .tar.gz to PyPI. Release tools are installed separately, so building the source archive does not require the SDK.

To enable automatic publishing once:

  1. Create a GitHub environment named pypi under the repository's Settings → Environments.
  2. In your PyPI account's Publishing settings, add a pending GitHub Trusted Publisher with project name pymecheye, owner slabpanic, repository pymecheye, workflow filename release.yml, and environment pypi. For an existing PyPI project, add the same publisher in that project's Publishing settings.

The publisher uses OIDC; no PyPI API token is needed in GitHub secrets. See the PyPI Trusted Publishing guide.

For each release, update project.version, run uv lock, commit the release changes, then push the matching tag:

git tag -a v0.1.0 -m "Release 0.1.0"
git push origin v0.1.0

Use normalized versions for prereleases as well, for example 0.2.0rc1 with v0.2.0rc1. A tag mismatch or failed build, metadata, test, coverage, or documentation check prevents uploading. Existing tags do not trigger publication until pushed; the version is never changed automatically by the workflow.

To build a source archive locally:

uv build --sdist --no-sources

Upload the resulting dist/pymecheye-<version>.tar.gz. Locally built wheels record the builder's exact SDK runtime hash; they are intended for matching environments and should not be uploaded as general-purpose PyPI releases. Publishing only the source archive lets uv add pymecheye build with the recipient's installed SDK when a compatible compiled package is not already cached. If an index also offers wheels, consumers can explicitly request a source installation:

uv add --no-binary-package pymecheye pymecheye

For a persistent source-only preference, add this to the consuming project:

[tool.uv]
no-binary-package = ["pymecheye"]

Dependency-specific uv settings are configured by the consuming project; they are not imposed through pymecheye's published package metadata.

License

Project-owned code and documentation are licensed under the MIT License. Third-party code retains its original terms; the distribution includes MIT and BSD 3-Clause notices.

Third-party notices

The independently installed Mech-Eye SDK remains subject to its vendor agreement. Notices for nanobind, robin-map, and SDK header code compiled into the extension are included in THIRD_PARTY_NOTICES.md and src/mecheye/licenses/.

Metadata

Release files for pymecheye 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pymecheye 0.1.0
File Size Uploaded
pymecheye-0.1.0.tar.gz 273.3 kB Details

Release files / pymecheye-0.1.0.tar.gz

Download URL pymecheye-0.1.0.tar.gz
Size 273.3 kB
Tags Source
SHA-256 checksum
How to use checksums
47caafd63c598a667fa3100dd66fd272662bf5ee1bbc57416b1a479b722c53d9
BLAKE2b-256 checksum
How to use checksums
87086f8e5dbb059ed19d3fe3040853b987ebf7c7032165bdb8a6d3f3e36d8a33
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.0 This release

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page