pyhikrobot
Thin, zero-copy Python bindings for Hikrobot machine-vision cameras over the MVS SDK. NumPy views without the copy, a cross-platform loader, and errors you can catch.
Status: alpha. Enumeration, open/close, the GenICam node map, streaming and GigE transport tuning are implemented and tested against real hardware. Action commands and the CUDA layer are not written yet. The public API may still change.
Requirements
| Python | 3.9+ |
| Runtime dependency | numpy — nothing else, ever, in the core package |
| SDK | Hikrobot MVS 4.x, installed separately by you |
| Platforms | Linux x86_64 / aarch64 / armv7l, Windows x64 |
This package does not ship any Hikrobot code. It opens an SDK you installed yourself; get it from the vendor under their terms.
Install
pip install pyhikrobot
The cuda extra (pip install "pyhikrobot[cuda]") pulls in CuPy for the device-side
layer, which is not written yet.
The SDK is found through MVCAM_SDK_PATH, falling back to /opt/MVS on Linux and the
installer's Common Files\MVS\Runtime on Windows. Nothing is loaded at import time, so
import hikrobot works on a machine with no SDK and no camera — a missing SDK raises
SDKNotFoundError on first real use.
Quick start
import hikrobot
devices = hikrobot.enumerate_devices()
for device in devices:
print(device.transport, device.model_name, device.serial_number, device.ip_address)
with hikrobot.Camera(devices[0]) as camera:
camera.exposure_us = 2500.0
camera.gain_db = 0.0
for frame in camera.frames(timeout_ms=1000):
print(frame.frame_number, frame.data.shape, frame.data.mean())
break
Camera() touches no SDK state; the context manager opens and closes the device.
Buffer lifetime
This is the one rule that matters. frame.data is a read-only NumPy view onto a node
of the driver's buffer pool — not memory you own. The pool is fixed and recycled, so once
the node goes back the driver writes the next frame into the same address.
for frame in camera.frames(timeout_ms=1000):
total = frame.data.sum() # fine — inside the body
keep = frame.copy() # fine — owns its memory, writable
leaked = frame.data # a view; its node goes back at the end of the body
break
frame.data # BufferReleasedError — the usual mistake, caught
leaked.mean() # no error, and no longer this frame's pixels
Note the asymmetry. Reaching for frame.data after the release raises, which catches the
common mistake in development instead of in the field. An array you already took out
cannot be caught — NumPy has no idea the memory changed hands, so it keeps working and
quietly returns whatever the driver wrote there next. That is the failure this API is
shaped to avoid, and .copy() is the only thing that avoids it.
frames() releases the node in a finally, on every route out including break,
return and exceptions. There is deliberately no "hold this one for me" shortcut: a
.copy() of a 2 MB frame is visible in a profile, an accidentally retained view is not.
For consumers that outlive the loop body, frames_raw() hands the release over:
camera.start_grabbing(node_count=8) # own acquisition, or leaving the loop stops it
try:
for frame in camera.frames_raw(timeout_ms=1000):
queue.put(frame) # released later, by whoever drains the queue
finally:
camera.stop_grabbing() # reclaims anything still outstanding
The pool holds exactly node_count nodes and the SDK's default is one, so holding a
second frame without raising it fails with InsufficientBufferError.
Camera settings
Named properties cover the common SFNC features:
camera.width, camera.height, camera.offset_x, camera.offset_y
camera.exposure_us, camera.gain_db, camera.frame_rate
camera.pixel_format, camera.pixel_formats
camera.payload_size # read-only
camera.exposure_range_us.min # FloatRange(value, min, max)
camera.nodes.int_range("Width").inc # IntRange(value, min, max, inc)
Anything else goes through the node map directly:
camera.nodes.set_enum("TriggerMode", "On")
camera.nodes.set_enum("TriggerSource", "Software")
camera.nodes.execute("TriggerSoftware")
camera.nodes.get_int("GevTimestampTickFrequency")
Two behaviours worth knowing, both measured rather than assumed:
- A missing node and a wrong-typed access return the same status, so both raise
GenICamError. When one appears, check the spelling and the type. - Float features are quantised to a hardware step the node map does not expose. Writing
gain_db = 1.0reads back as1.0052. Write, then read, and trust the second value.
GigE transport
The knobs that decide whether streaming works at all:
camera.tune_packet_size() # probe the path, apply what it carries
camera.enable_resend(True) # retransmit packets the host missed
camera.start_grabbing(node_count=4)
for frame in camera.frames(timeout_ms=5000):
...
stats = camera.statistics # only valid while acquisition runs
camera.stop_grabbing()
stats.lost_packets, stats.lost_frames, stats.resent_packets
Incomplete frames almost always mean a packet size the network path cannot carry — start
with tune_packet_size(). Counters live only between start_grabbing() and
stop_grabbing() and reset on every start.
Errors
Everything derives from HikrobotError, so you can catch one thing. Below it, one class
per vendor error range, plus named classes for the codes callers actually branch on:
HikrobotError
├── SDKNotFoundError, SDKLoadError, UnsupportedPlatformError
├── CameraStateError, BufferReleasedError, UnsupportedPixelFormatError
└── StatusError .status .name .operation
├── GeneralError InvalidHandleError, CallOrderError, NoDataError,
│ IncompleteImageError, InsufficientBufferError, …
├── GenICamError CameraTimeoutError, ValueOutOfRangeError, …
├── GigEError AccessDeniedError, DeviceBusyError, NetworkError, …
├── USBError
└── UpgradeError
An untranslated code still arrives as its range's class carrying .status and .name, so
nothing is lost:
try:
camera.open()
except hikrobot.AccessDeniedError:
... # held by another process
except hikrobot.HikrobotError as exc:
print(exc) # MV_CC_OpenDevice failed: MV_E_NETER (0x80000206) - network error
Development
pip install -e ".[dev]"
pytest tests/unit # no SDK, no camera, no CUDA — runs anywhere
pytest tests --hardware # opt-in; needs one reachable camera
ruff check . && ruff format --check . && mypy
Unit tests run against a fake that sits at the CDLL boundary, so struct packing,
argtypes and the status-to-exception mapping stay under test without hardware.
License
Apache-2.0. See LICENSE and NOTICE.
Hikrobot and MVS are trademarks of Hangzhou Hikrobot Co., Ltd. This project is not affiliated with or endorsed by them, and distributes none of their software.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyhikrobot-0.1.0.tar.gz.
File metadata
- Download URL: pyhikrobot-0.1.0.tar.gz
- Upload date:
- Size: 65.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07e977606836a96b8bca618884ddc93be9d6f172ab475771fffe54ad018092b3
|
|
| MD5 |
43048984cb903721e350e354abc64acf
|
|
| BLAKE2b-256 |
9919d088573969de256550558363d089efda25d5aacbeb2f9076adf89e13c8c9
|
Provenance
The following attestation bundles were made for pyhikrobot-0.1.0.tar.gz:
Publisher:
release.yml on Shalimov04/pyhikrobot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyhikrobot-0.1.0.tar.gz -
Subject digest:
07e977606836a96b8bca618884ddc93be9d6f172ab475771fffe54ad018092b3 - Sigstore transparency entry: 2260826286
- Sigstore integration time:
-
Permalink:
Shalimov04/pyhikrobot@6defda660cb415c9986941654091dd545e89d548 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Shalimov04
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6defda660cb415c9986941654091dd545e89d548 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyhikrobot-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pyhikrobot-0.1.0-py3-none-any.whl
- Upload date:
- Size: 44.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e7a23a2b4a5a09440afeea5f5212ea169463440d3648eaf1055fc57dfa8119b
|
|
| MD5 |
a3d2136ef62c6fe40913b54d7d48091f
|
|
| BLAKE2b-256 |
5e2b0e723392dd4086ddcf71d5b4cf4104e8314703921ff7b4e4cc164698de34
|
Provenance
The following attestation bundles were made for pyhikrobot-0.1.0-py3-none-any.whl:
Publisher:
release.yml on Shalimov04/pyhikrobot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyhikrobot-0.1.0-py3-none-any.whl -
Subject digest:
4e7a23a2b4a5a09440afeea5f5212ea169463440d3648eaf1055fc57dfa8119b - Sigstore transparency entry: 2260826723
- Sigstore integration time:
-
Permalink:
Shalimov04/pyhikrobot@6defda660cb415c9986941654091dd545e89d548 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Shalimov04
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6defda660cb415c9986941654091dd545e89d548 -
Trigger Event:
push
-
Statement type: