viva-genicam 0.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- viva_genicam-0.2.4/PKG-INFO +64 -0
- viva_genicam-0.2.4/README.md +33 -0
- viva_genicam-0.2.4/pyproject.toml +49 -0
- viva_genicam-0.2.4/python/viva_genicam/__init__.py +59 -0
- viva_genicam-0.2.4/python/viva_genicam/_native.pyi +102 -0
- viva_genicam-0.2.4/python/viva_genicam/camera.py +126 -0
- viva_genicam-0.2.4/python/viva_genicam/discovery.py +110 -0
- viva_genicam-0.2.4/python/viva_genicam/errors.py +26 -0
- viva_genicam-0.2.4/python/viva_genicam/frame.py +73 -0
- viva_genicam-0.2.4/python/viva_genicam/node.py +72 -0
- viva_genicam-0.2.4/python/viva_genicam/py.typed +0 -0
- viva_genicam-0.2.4/python/viva_genicam/stream.py +56 -0
- viva_genicam-0.2.4/python/viva_genicam/testing.py +88 -0
- viva_genicam-0.2.4/viva-fake-gige/Cargo.toml +22 -0
- viva_genicam-0.2.4/viva-fake-gige/README.md +36 -0
- viva_genicam-0.2.4/viva-fake-gige/src/gvcp_server.rs +361 -0
- viva_genicam-0.2.4/viva-fake-gige/src/gvsp_sender.rs +298 -0
- viva_genicam-0.2.4/viva-fake-gige/src/lib.rs +210 -0
- viva_genicam-0.2.4/viva-fake-gige/src/main.rs +93 -0
- viva_genicam-0.2.4/viva-fake-gige/src/registers.rs +815 -0
- viva_genicam-0.2.4/viva-genapi/Cargo.toml +22 -0
- viva_genicam-0.2.4/viva-genapi/README.md +48 -0
- viva_genicam-0.2.4/viva-genapi/src/bitops.rs +194 -0
- viva_genicam-0.2.4/viva-genapi/src/conversions.rs +314 -0
- viva_genicam-0.2.4/viva-genapi/src/error.rs +65 -0
- viva_genicam-0.2.4/viva-genapi/src/io.rs +28 -0
- viva_genicam-0.2.4/viva-genapi/src/lib.rs +1019 -0
- viva_genicam-0.2.4/viva-genapi/src/nodemap.rs +1749 -0
- viva_genicam-0.2.4/viva-genapi/src/nodes.rs +426 -0
- viva_genicam-0.2.4/viva-genapi/src/swissknife.rs +1169 -0
- viva_genicam-0.2.4/viva-genapi-xml/Cargo.toml +29 -0
- viva_genicam-0.2.4/viva-genapi-xml/README.md +44 -0
- viva_genicam-0.2.4/viva-genapi-xml/src/builders.rs +406 -0
- viva_genicam-0.2.4/viva-genapi-xml/src/fetch.rs +176 -0
- viva_genicam-0.2.4/viva-genapi-xml/src/lib.rs +1280 -0
- viva_genicam-0.2.4/viva-genapi-xml/src/parsers/control.rs +179 -0
- viva_genicam-0.2.4/viva-genapi-xml/src/parsers/converter.rs +359 -0
- viva_genicam-0.2.4/viva-genapi-xml/src/parsers/mod.rs +356 -0
- viva_genicam-0.2.4/viva-genapi-xml/src/parsers/numeric.rs +489 -0
- viva_genicam-0.2.4/viva-genapi-xml/src/parsers/struct_reg.rs +163 -0
- viva_genicam-0.2.4/viva-genapi-xml/src/parsers/swissknife.rs +126 -0
- viva_genicam-0.2.4/viva-genapi-xml/src/parsers/symbolic.rs +509 -0
- viva_genicam-0.2.4/viva-genapi-xml/src/util.rs +133 -0
- viva_genicam-0.2.4/viva-gencp/Cargo.toml +24 -0
- viva_genicam-0.2.4/viva-gencp/README.md +45 -0
- viva_genicam-0.2.4/viva-gencp/src/lib.rs +378 -0
- viva_genicam-0.2.4/viva-genicam/Cargo.toml +46 -0
- viva_genicam-0.2.4/viva-genicam/README.md +56 -0
- viva_genicam-0.2.4/viva-genicam/examples/action_trigger.rs +148 -0
- viva_genicam-0.2.4/viva-genicam/examples/bitfields_demo.rs +145 -0
- viva_genicam-0.2.4/viva-genicam/examples/demo_fake_camera.rs +170 -0
- viva_genicam-0.2.4/viva-genicam/examples/enum_pvalue_demo.rs +208 -0
- viva_genicam-0.2.4/viva-genicam/examples/events_gige.rs +168 -0
- viva_genicam-0.2.4/viva-genicam/examples/features_demo.rs +203 -0
- viva_genicam-0.2.4/viva-genicam/examples/get_set_feature.rs +66 -0
- viva_genicam-0.2.4/viva-genicam/examples/grab_gige.rs +264 -0
- viva_genicam-0.2.4/viva-genicam/examples/grab_with_chunks.rs +335 -0
- viva_genicam-0.2.4/viva-genicam/examples/list_cameras.rs +60 -0
- viva_genicam-0.2.4/viva-genicam/examples/multicast_grab.rs +395 -0
- viva_genicam-0.2.4/viva-genicam/examples/paddress_demo.rs +216 -0
- viva_genicam-0.2.4/viva-genicam/examples/selectors_demo.rs +201 -0
- viva_genicam-0.2.4/viva-genicam/examples/soak_bench.rs +421 -0
- viva_genicam-0.2.4/viva-genicam/examples/stats.rs +36 -0
- viva_genicam-0.2.4/viva-genicam/examples/swissknife_demo.rs +166 -0
- viva_genicam-0.2.4/viva-genicam/examples/time_sync.rs +166 -0
- viva_genicam-0.2.4/viva-genicam/src/chunks.rs +222 -0
- viva_genicam-0.2.4/viva-genicam/src/events.rs +174 -0
- viva_genicam-0.2.4/viva-genicam/src/frame.rs +308 -0
- viva_genicam-0.2.4/viva-genicam/src/lib.rs +988 -0
- viva_genicam-0.2.4/viva-genicam/src/stream.rs +931 -0
- viva_genicam-0.2.4/viva-genicam/src/time.rs +7 -0
- viva_genicam-0.2.4/viva-genicam/tests/common/mod.rs +59 -0
- viva_genicam-0.2.4/viva-genicam/tests/fake_camera.rs +527 -0
- viva_genicam-0.2.4/viva-genicam/tests/fake_u3v_camera.rs +95 -0
- viva_genicam-0.2.4/viva-genicam/tests/predicates.rs +188 -0
- viva_genicam-0.2.4/viva-gige/Cargo.toml +35 -0
- viva_genicam-0.2.4/viva-gige/README.md +47 -0
- viva_genicam-0.2.4/viva-gige/src/action.rs +211 -0
- viva_genicam-0.2.4/viva-gige/src/gvcp.rs +1115 -0
- viva_genicam-0.2.4/viva-gige/src/gvsp.rs +729 -0
- viva_genicam-0.2.4/viva-gige/src/lib.rs +15 -0
- viva_genicam-0.2.4/viva-gige/src/message.rs +248 -0
- viva_genicam-0.2.4/viva-gige/src/nic.rs +431 -0
- viva_genicam-0.2.4/viva-gige/src/stats.rs +403 -0
- viva_genicam-0.2.4/viva-gige/src/time.rs +432 -0
- viva_genicam-0.2.4/viva-pfnc/Cargo.toml +24 -0
- viva_genicam-0.2.4/viva-pfnc/README.md +43 -0
- viva_genicam-0.2.4/viva-pfnc/src/lib.rs +211 -0
- viva_genicam-0.2.4/viva-pygenicam/Cargo.lock +1062 -0
- viva_genicam-0.2.4/viva-pygenicam/Cargo.toml +34 -0
- viva_genicam-0.2.4/viva-pygenicam/README.md +33 -0
- viva_genicam-0.2.4/viva-pygenicam/examples/README.md +33 -0
- viva_genicam-0.2.4/viva-pygenicam/examples/demo_fake_camera.py +56 -0
- viva_genicam-0.2.4/viva-pygenicam/examples/discover.py +46 -0
- viva_genicam-0.2.4/viva-pygenicam/examples/get_set_feature.py +64 -0
- viva_genicam-0.2.4/viva-pygenicam/examples/grab_frame.py +62 -0
- viva_genicam-0.2.4/viva-pygenicam/examples/node_browser.py +65 -0
- viva_genicam-0.2.4/viva-pygenicam/src/camera.rs +342 -0
- viva_genicam-0.2.4/viva-pygenicam/src/discovery.rs +141 -0
- viva_genicam-0.2.4/viva-pygenicam/src/errors.rs +60 -0
- viva_genicam-0.2.4/viva-pygenicam/src/frame.rs +147 -0
- viva_genicam-0.2.4/viva-pygenicam/src/lib.rs +28 -0
- viva_genicam-0.2.4/viva-pygenicam/src/nodemap.rs +67 -0
- viva_genicam-0.2.4/viva-pygenicam/src/runtime.rs +16 -0
- viva_genicam-0.2.4/viva-pygenicam/src/stream.rs +143 -0
- viva_genicam-0.2.4/viva-pygenicam/src/testing.rs +187 -0
- viva_genicam-0.2.4/viva-pygenicam/tests/conftest.py +38 -0
- viva_genicam-0.2.4/viva-pygenicam/tests/test_api_shape.py +57 -0
- viva_genicam-0.2.4/viva-pygenicam/tests/test_discovery.py +26 -0
- viva_genicam-0.2.4/viva-pygenicam/tests/test_features.py +64 -0
- viva_genicam-0.2.4/viva-pygenicam/tests/test_streaming.py +63 -0
- viva_genicam-0.2.4/viva-pygenicam/tests/test_testing.py +56 -0
- viva_genicam-0.2.4/viva-sfnc/Cargo.toml +20 -0
- viva_genicam-0.2.4/viva-sfnc/README.md +40 -0
- viva_genicam-0.2.4/viva-sfnc/src/lib.rs +86 -0
- viva_genicam-0.2.4/viva-u3v/Cargo.toml +24 -0
- viva_genicam-0.2.4/viva-u3v/README.md +48 -0
- viva_genicam-0.2.4/viva-u3v/src/bootstrap.rs +438 -0
- viva_genicam-0.2.4/viva-u3v/src/control.rs +523 -0
- viva_genicam-0.2.4/viva-u3v/src/descriptor.rs +144 -0
- viva_genicam-0.2.4/viva-u3v/src/device.rs +386 -0
- viva_genicam-0.2.4/viva-u3v/src/discovery.rs +113 -0
- viva_genicam-0.2.4/viva-u3v/src/lib.rs +46 -0
- viva_genicam-0.2.4/viva-u3v/src/stream.rs +458 -0
- viva_genicam-0.2.4/viva-u3v/src/usb.rs +207 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: viva-genicam
|
|
3
|
+
Version: 0.2.4
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Science/Research
|
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Rust
|
|
14
|
+
Classifier: Topic :: Multimedia :: Graphics :: Capture
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
16
|
+
Requires-Dist: numpy>=1.23
|
|
17
|
+
Requires-Dist: pytest>=7 ; extra == 'dev'
|
|
18
|
+
Requires-Dist: maturin>=1.7 ; extra == 'dev'
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Summary: Pure-Rust GenICam stack for Python: discover, control, and stream GigE Vision / USB3 Vision cameras
|
|
21
|
+
Keywords: genicam,gige,usb3-vision,camera,vision
|
|
22
|
+
Home-Page: https://github.com/VitalyVorobyev/viva-genicam
|
|
23
|
+
Author: Vitaly Vorobyev
|
|
24
|
+
License: MIT
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
27
|
+
Project-URL: Documentation, https://vitalyvorobyev.github.io/viva-genicam/
|
|
28
|
+
Project-URL: Homepage, https://github.com/VitalyVorobyev/viva-genicam
|
|
29
|
+
Project-URL: Repository, https://github.com/VitalyVorobyev/viva-genicam
|
|
30
|
+
|
|
31
|
+
# viva-genicam (Python)
|
|
32
|
+
|
|
33
|
+
Pure-Rust GenICam stack with Python bindings. Discover, control, and stream GigE Vision and USB3 Vision cameras from Python — no aravis, no C toolchain, just a wheel.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install viva-genicam
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import viva_genicam as vg
|
|
41
|
+
|
|
42
|
+
cams = vg.discover(timeout_ms=500)
|
|
43
|
+
cam = vg.connect_gige(cams[0])
|
|
44
|
+
|
|
45
|
+
print(cam.get("DeviceModelName"))
|
|
46
|
+
cam.set_exposure_time_us(10_000.0)
|
|
47
|
+
|
|
48
|
+
with cam.stream() as frames:
|
|
49
|
+
for frame in frames:
|
|
50
|
+
arr = frame.to_numpy() # NumPy (H, W) or (H, W, 3) uint8
|
|
51
|
+
print(frame.width, frame.height, frame.pixel_format)
|
|
52
|
+
break
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
See the [documentation](https://vitalyvorobyev.github.io/viva-genicam/python.html) for the full API.
|
|
56
|
+
|
|
57
|
+
## Build from source
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
uv venv .venv
|
|
61
|
+
uv pip install --python .venv/bin/python maturin numpy pytest
|
|
62
|
+
uv run --python .venv/bin/python maturin develop -m crates/viva-pygenicam/Cargo.toml
|
|
63
|
+
```
|
|
64
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# viva-genicam (Python)
|
|
2
|
+
|
|
3
|
+
Pure-Rust GenICam stack with Python bindings. Discover, control, and stream GigE Vision and USB3 Vision cameras from Python — no aravis, no C toolchain, just a wheel.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install viva-genicam
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
import viva_genicam as vg
|
|
11
|
+
|
|
12
|
+
cams = vg.discover(timeout_ms=500)
|
|
13
|
+
cam = vg.connect_gige(cams[0])
|
|
14
|
+
|
|
15
|
+
print(cam.get("DeviceModelName"))
|
|
16
|
+
cam.set_exposure_time_us(10_000.0)
|
|
17
|
+
|
|
18
|
+
with cam.stream() as frames:
|
|
19
|
+
for frame in frames:
|
|
20
|
+
arr = frame.to_numpy() # NumPy (H, W) or (H, W, 3) uint8
|
|
21
|
+
print(frame.width, frame.height, frame.pixel_format)
|
|
22
|
+
break
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
See the [documentation](https://vitalyvorobyev.github.io/viva-genicam/python.html) for the full API.
|
|
26
|
+
|
|
27
|
+
## Build from source
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv venv .venv
|
|
31
|
+
uv pip install --python .venv/bin/python maturin numpy pytest
|
|
32
|
+
uv run --python .venv/bin/python maturin develop -m crates/viva-pygenicam/Cargo.toml
|
|
33
|
+
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["maturin>=1.7,<2.0"]
|
|
3
|
+
build-backend = "maturin"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "viva-genicam"
|
|
7
|
+
version = "0.2.4"
|
|
8
|
+
description = "Pure-Rust GenICam stack for Python: discover, control, and stream GigE Vision / USB3 Vision cameras"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Vitaly Vorobyev" },
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
keywords = ["genicam", "gige", "usb3-vision", "camera", "vision"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Rust",
|
|
27
|
+
"Topic :: Multimedia :: Graphics :: Capture",
|
|
28
|
+
"Topic :: Scientific/Engineering :: Image Processing",
|
|
29
|
+
]
|
|
30
|
+
dependencies = [
|
|
31
|
+
"numpy>=1.23",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=7",
|
|
37
|
+
"maturin>=1.7",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
Homepage = "https://github.com/VitalyVorobyev/viva-genicam"
|
|
42
|
+
Repository = "https://github.com/VitalyVorobyev/viva-genicam"
|
|
43
|
+
Documentation = "https://vitalyvorobyev.github.io/viva-genicam/"
|
|
44
|
+
|
|
45
|
+
[tool.maturin]
|
|
46
|
+
module-name = "viva_genicam._native"
|
|
47
|
+
features = ["pyo3/extension-module"]
|
|
48
|
+
manifest-path = "viva-pygenicam/Cargo.toml"
|
|
49
|
+
python-source = "python"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Python bindings for viva-genicam.
|
|
2
|
+
|
|
3
|
+
Pure-Rust GenICam stack for GigE Vision and USB3 Vision cameras.
|
|
4
|
+
|
|
5
|
+
Quickstart:
|
|
6
|
+
|
|
7
|
+
import viva_genicam as vg
|
|
8
|
+
|
|
9
|
+
cams = vg.discover(timeout_ms=500)
|
|
10
|
+
cam = vg.connect_gige(cams[0])
|
|
11
|
+
print(cam.get("DeviceModelName"))
|
|
12
|
+
|
|
13
|
+
with cam.stream() as frames:
|
|
14
|
+
for frame in frames:
|
|
15
|
+
arr = frame.to_numpy()
|
|
16
|
+
break
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from .camera import Camera
|
|
22
|
+
from .discovery import DeviceInfo, GigeDeviceInfo, U3vDeviceInfo, discover, discover_u3v
|
|
23
|
+
from .errors import (
|
|
24
|
+
GenApiError,
|
|
25
|
+
GenicamError,
|
|
26
|
+
MissingChunkFeatureError,
|
|
27
|
+
ParseError,
|
|
28
|
+
TransportError,
|
|
29
|
+
UnsupportedPixelFormatError,
|
|
30
|
+
)
|
|
31
|
+
from .frame import Frame
|
|
32
|
+
from .node import NodeInfo, NodeKind
|
|
33
|
+
from .stream import FrameStream
|
|
34
|
+
|
|
35
|
+
# Lazy functional wrappers so the public API is a flat module.
|
|
36
|
+
from .camera import connect_gige, connect_u3v
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"Camera",
|
|
40
|
+
"DeviceInfo",
|
|
41
|
+
"GigeDeviceInfo",
|
|
42
|
+
"U3vDeviceInfo",
|
|
43
|
+
"Frame",
|
|
44
|
+
"FrameStream",
|
|
45
|
+
"NodeInfo",
|
|
46
|
+
"NodeKind",
|
|
47
|
+
"GenicamError",
|
|
48
|
+
"GenApiError",
|
|
49
|
+
"TransportError",
|
|
50
|
+
"ParseError",
|
|
51
|
+
"MissingChunkFeatureError",
|
|
52
|
+
"UnsupportedPixelFormatError",
|
|
53
|
+
"discover",
|
|
54
|
+
"discover_u3v",
|
|
55
|
+
"connect_gige",
|
|
56
|
+
"connect_u3v",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
__version__ = "0.2.4"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Type stubs for the private native extension.
|
|
2
|
+
|
|
3
|
+
End users should not import from this module directly — use the public
|
|
4
|
+
``viva_genicam`` package instead.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Optional
|
|
8
|
+
|
|
9
|
+
class GigeDeviceInfo:
|
|
10
|
+
ip: str
|
|
11
|
+
mac: str
|
|
12
|
+
manufacturer: Optional[str]
|
|
13
|
+
model: Optional[str]
|
|
14
|
+
|
|
15
|
+
class U3vDeviceInfo:
|
|
16
|
+
bus: int
|
|
17
|
+
address: int
|
|
18
|
+
vendor_id: int
|
|
19
|
+
product_id: int
|
|
20
|
+
serial: Optional[str]
|
|
21
|
+
manufacturer: Optional[str]
|
|
22
|
+
model: Optional[str]
|
|
23
|
+
|
|
24
|
+
class _Frame:
|
|
25
|
+
width: int
|
|
26
|
+
height: int
|
|
27
|
+
pixel_format: str
|
|
28
|
+
pixel_format_code: int
|
|
29
|
+
ts_dev: Optional[int]
|
|
30
|
+
ts_host: Optional[float]
|
|
31
|
+
def payload(self) -> bytes: ...
|
|
32
|
+
def to_numpy(self) -> Any: ...
|
|
33
|
+
def to_rgb8(self) -> Any: ...
|
|
34
|
+
|
|
35
|
+
class _FrameStream:
|
|
36
|
+
def next_frame(self, timeout_ms: Optional[int] = None) -> Optional[_Frame]: ...
|
|
37
|
+
def close(self) -> None: ...
|
|
38
|
+
def __iter__(self) -> "_FrameStream": ...
|
|
39
|
+
def __next__(self) -> _Frame: ...
|
|
40
|
+
|
|
41
|
+
class _Camera:
|
|
42
|
+
transport: str
|
|
43
|
+
xml: str
|
|
44
|
+
def get(self, name: str) -> str: ...
|
|
45
|
+
def set(self, name: str, value: str) -> None: ...
|
|
46
|
+
def set_exposure_time_us(self, value: float) -> None: ...
|
|
47
|
+
def set_gain_db(self, value: float) -> None: ...
|
|
48
|
+
def enum_entries(self, name: str) -> list[str]: ...
|
|
49
|
+
def nodes(self) -> list[str]: ...
|
|
50
|
+
def node_info(self, name: str) -> Optional[dict]: ...
|
|
51
|
+
def all_node_info(self) -> list[dict]: ...
|
|
52
|
+
def categories(self) -> dict[str, list[str]]: ...
|
|
53
|
+
def acquisition_start(self) -> None: ...
|
|
54
|
+
def acquisition_stop(self) -> None: ...
|
|
55
|
+
def open_stream(
|
|
56
|
+
self,
|
|
57
|
+
iface: Optional[str],
|
|
58
|
+
auto_packet_size: Optional[bool],
|
|
59
|
+
multicast: Optional[str],
|
|
60
|
+
destination_port: Optional[int],
|
|
61
|
+
) -> _FrameStream: ...
|
|
62
|
+
|
|
63
|
+
def discover_gige(
|
|
64
|
+
timeout_ms: int = 500,
|
|
65
|
+
iface: Optional[str] = None,
|
|
66
|
+
all: bool = False,
|
|
67
|
+
) -> list[GigeDeviceInfo]: ...
|
|
68
|
+
def discover_u3v() -> list[U3vDeviceInfo]: ...
|
|
69
|
+
def connect_gige(
|
|
70
|
+
device_info: GigeDeviceInfo, iface: Optional[str] = None
|
|
71
|
+
) -> _Camera: ...
|
|
72
|
+
def connect_u3v(device_info: U3vDeviceInfo) -> _Camera: ...
|
|
73
|
+
|
|
74
|
+
class GenicamError(Exception): ...
|
|
75
|
+
class GenApiError(GenicamError): ...
|
|
76
|
+
class TransportError(GenicamError): ...
|
|
77
|
+
class ParseError(GenicamError): ...
|
|
78
|
+
class MissingChunkFeatureError(GenicamError): ...
|
|
79
|
+
class UnsupportedPixelFormatError(GenicamError): ...
|
|
80
|
+
|
|
81
|
+
class _testing:
|
|
82
|
+
class FakeGigeCamera:
|
|
83
|
+
def __init__(
|
|
84
|
+
self,
|
|
85
|
+
width: int = 640,
|
|
86
|
+
height: int = 480,
|
|
87
|
+
fps: int = 30,
|
|
88
|
+
bind_ip: str = "127.0.0.1",
|
|
89
|
+
port: int = 3956,
|
|
90
|
+
pixel_format: str = "Mono8",
|
|
91
|
+
) -> None: ...
|
|
92
|
+
@property
|
|
93
|
+
def ip(self) -> str: ...
|
|
94
|
+
@property
|
|
95
|
+
def port(self) -> int: ...
|
|
96
|
+
def start(self) -> None: ...
|
|
97
|
+
def stop(self) -> None: ...
|
|
98
|
+
def device_info(self, timeout_ms: int = 1500) -> GigeDeviceInfo: ...
|
|
99
|
+
def __enter__(self) -> "_testing.FakeGigeCamera": ...
|
|
100
|
+
def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> bool: ...
|
|
101
|
+
|
|
102
|
+
testing: _testing
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Camera facade: unified API over GigE and U3V transports."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from . import _native
|
|
8
|
+
from .discovery import DeviceInfo, GigeDeviceInfo, U3vDeviceInfo
|
|
9
|
+
from .node import NodeInfo
|
|
10
|
+
from .stream import FrameStream
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Camera:
|
|
14
|
+
"""Unified camera handle.
|
|
15
|
+
|
|
16
|
+
Construct via ``connect_gige(device_info)`` or ``connect_u3v(device_info)``.
|
|
17
|
+
The class itself presents the same method surface regardless of transport.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
__slots__ = ("_native",)
|
|
21
|
+
|
|
22
|
+
def __init__(self, native: Any) -> None:
|
|
23
|
+
self._native = native
|
|
24
|
+
|
|
25
|
+
# ── class factories ──────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def open(cls, device: DeviceInfo, **kwargs) -> "Camera":
|
|
29
|
+
"""Connect to ``device`` using the appropriate transport."""
|
|
30
|
+
if isinstance(device, GigeDeviceInfo):
|
|
31
|
+
return connect_gige(device, **kwargs)
|
|
32
|
+
if isinstance(device, U3vDeviceInfo):
|
|
33
|
+
return connect_u3v(device)
|
|
34
|
+
raise TypeError(f"unsupported device type: {type(device).__name__}")
|
|
35
|
+
|
|
36
|
+
# ── metadata ─────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def transport(self) -> str:
|
|
40
|
+
"""``"gige"`` or ``"u3v"``."""
|
|
41
|
+
return self._native.transport
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def xml(self) -> str:
|
|
45
|
+
"""Raw GenICam XML fetched from the camera."""
|
|
46
|
+
return self._native.xml
|
|
47
|
+
|
|
48
|
+
# ── feature access ───────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
def get(self, name: str) -> str:
|
|
51
|
+
"""Read a feature value, formatted as a string."""
|
|
52
|
+
return self._native.get(name)
|
|
53
|
+
|
|
54
|
+
def set(self, name: str, value: str) -> None:
|
|
55
|
+
"""Set a feature value; the string is parsed per node type."""
|
|
56
|
+
self._native.set(name, value)
|
|
57
|
+
|
|
58
|
+
def set_exposure_time_us(self, value: float) -> None:
|
|
59
|
+
self._native.set_exposure_time_us(value)
|
|
60
|
+
|
|
61
|
+
def set_gain_db(self, value: float) -> None:
|
|
62
|
+
self._native.set_gain_db(value)
|
|
63
|
+
|
|
64
|
+
def enum_entries(self, name: str) -> list[str]:
|
|
65
|
+
"""List allowed entries for an enumeration feature."""
|
|
66
|
+
return self._native.enum_entries(name)
|
|
67
|
+
|
|
68
|
+
# ── introspection ────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
def nodes(self) -> list[str]:
|
|
71
|
+
"""Names of every feature exposed by the camera."""
|
|
72
|
+
return self._native.nodes()
|
|
73
|
+
|
|
74
|
+
def node_info(self, name: str) -> Optional[NodeInfo]:
|
|
75
|
+
"""Metadata for one feature, or ``None`` if not present."""
|
|
76
|
+
raw = self._native.node_info(name)
|
|
77
|
+
return NodeInfo.from_dict(raw) if raw is not None else None
|
|
78
|
+
|
|
79
|
+
def all_node_info(self) -> list[NodeInfo]:
|
|
80
|
+
"""Metadata for every feature."""
|
|
81
|
+
return [NodeInfo.from_dict(d) for d in self._native.all_node_info()]
|
|
82
|
+
|
|
83
|
+
def categories(self) -> dict[str, list[str]]:
|
|
84
|
+
"""Category → feature-name children."""
|
|
85
|
+
return dict(self._native.categories())
|
|
86
|
+
|
|
87
|
+
# ── control ──────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
def acquisition_start(self) -> None:
|
|
90
|
+
self._native.acquisition_start()
|
|
91
|
+
|
|
92
|
+
def acquisition_stop(self) -> None:
|
|
93
|
+
self._native.acquisition_stop()
|
|
94
|
+
|
|
95
|
+
# ── streaming ────────────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
def stream(
|
|
98
|
+
self,
|
|
99
|
+
iface: Optional[str] = None,
|
|
100
|
+
auto_packet_size: Optional[bool] = None,
|
|
101
|
+
multicast: Optional[str] = None,
|
|
102
|
+
destination_port: Optional[int] = None,
|
|
103
|
+
) -> FrameStream:
|
|
104
|
+
"""Open a frame stream (context-manager yielding ``Frame`` objects).
|
|
105
|
+
|
|
106
|
+
GigE-only parameters are ignored for U3V cameras.
|
|
107
|
+
"""
|
|
108
|
+
native = self._native.open_stream(
|
|
109
|
+
iface, auto_packet_size, multicast, destination_port
|
|
110
|
+
)
|
|
111
|
+
return FrameStream(native, self)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def connect_gige(device_info: GigeDeviceInfo, iface: Optional[str] = None) -> Camera:
|
|
115
|
+
"""Connect to a GigE Vision camera."""
|
|
116
|
+
native = _native.connect_gige(device_info._handle, iface)
|
|
117
|
+
return Camera(native)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def connect_u3v(device_info: U3vDeviceInfo) -> Camera:
|
|
121
|
+
"""Connect to a USB3 Vision camera."""
|
|
122
|
+
native = _native.connect_u3v(device_info._handle)
|
|
123
|
+
return Camera(native)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
__all__ = ["Camera", "connect_gige", "connect_u3v"]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Camera discovery: GigE Vision (network) and USB3 Vision (libusb)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Any, Literal, Optional, Union
|
|
7
|
+
|
|
8
|
+
from . import _native
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class GigeDeviceInfo:
|
|
13
|
+
"""A discovered GigE Vision camera."""
|
|
14
|
+
|
|
15
|
+
ip: str
|
|
16
|
+
mac: str
|
|
17
|
+
manufacturer: Optional[str]
|
|
18
|
+
model: Optional[str]
|
|
19
|
+
transport: Literal["gige"] = "gige"
|
|
20
|
+
_handle: Any = field(default=None, repr=False, compare=False)
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def _from_native(cls, native: Any) -> "GigeDeviceInfo":
|
|
24
|
+
return cls(
|
|
25
|
+
ip=native.ip,
|
|
26
|
+
mac=native.mac,
|
|
27
|
+
manufacturer=native.manufacturer,
|
|
28
|
+
model=native.model,
|
|
29
|
+
_handle=native,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def to_dict(self) -> dict:
|
|
33
|
+
return {
|
|
34
|
+
"ip": self.ip,
|
|
35
|
+
"mac": self.mac,
|
|
36
|
+
"manufacturer": self.manufacturer,
|
|
37
|
+
"model": self.model,
|
|
38
|
+
"transport": self.transport,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(frozen=True)
|
|
43
|
+
class U3vDeviceInfo:
|
|
44
|
+
"""A discovered USB3 Vision camera."""
|
|
45
|
+
|
|
46
|
+
bus: int
|
|
47
|
+
address: int
|
|
48
|
+
vendor_id: int
|
|
49
|
+
product_id: int
|
|
50
|
+
serial: Optional[str]
|
|
51
|
+
manufacturer: Optional[str]
|
|
52
|
+
model: Optional[str]
|
|
53
|
+
transport: Literal["u3v"] = "u3v"
|
|
54
|
+
_handle: Any = field(default=None, repr=False, compare=False)
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def _from_native(cls, native: Any) -> "U3vDeviceInfo":
|
|
58
|
+
return cls(
|
|
59
|
+
bus=native.bus,
|
|
60
|
+
address=native.address,
|
|
61
|
+
vendor_id=native.vendor_id,
|
|
62
|
+
product_id=native.product_id,
|
|
63
|
+
serial=native.serial,
|
|
64
|
+
manufacturer=native.manufacturer,
|
|
65
|
+
model=native.model,
|
|
66
|
+
_handle=native,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def to_dict(self) -> dict:
|
|
70
|
+
return {
|
|
71
|
+
"bus": self.bus,
|
|
72
|
+
"address": self.address,
|
|
73
|
+
"vendor_id": self.vendor_id,
|
|
74
|
+
"product_id": self.product_id,
|
|
75
|
+
"serial": self.serial,
|
|
76
|
+
"manufacturer": self.manufacturer,
|
|
77
|
+
"model": self.model,
|
|
78
|
+
"transport": self.transport,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
DeviceInfo = Union[GigeDeviceInfo, U3vDeviceInfo]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def discover(
|
|
86
|
+
timeout_ms: int = 500,
|
|
87
|
+
iface: Optional[str] = None,
|
|
88
|
+
all: bool = False,
|
|
89
|
+
) -> list[GigeDeviceInfo]:
|
|
90
|
+
"""Discover GigE Vision cameras on the local network.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
timeout_ms: How long to wait for responses.
|
|
94
|
+
iface: If given, restrict discovery to the named NIC (e.g. ``"en0"``).
|
|
95
|
+
all: If True, enumerate all system interfaces and merge results.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
A list of ``GigeDeviceInfo`` — may be empty.
|
|
99
|
+
"""
|
|
100
|
+
natives = _native.discover_gige(timeout_ms, iface, all)
|
|
101
|
+
return [GigeDeviceInfo._from_native(n) for n in natives]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def discover_u3v() -> list[U3vDeviceInfo]:
|
|
105
|
+
"""Enumerate USB3 Vision cameras connected to the system."""
|
|
106
|
+
natives = _native.discover_u3v()
|
|
107
|
+
return [U3vDeviceInfo._from_native(n) for n in natives]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
__all__ = ["GigeDeviceInfo", "U3vDeviceInfo", "DeviceInfo", "discover", "discover_u3v"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Exception hierarchy for viva-genicam.
|
|
2
|
+
|
|
3
|
+
All exceptions raised by the native extension subclass ``GenicamError``.
|
|
4
|
+
They are defined on the Rust side and re-exported here so users can do
|
|
5
|
+
``except vg.TransportError:`` without reaching into ``_native``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from . import _native
|
|
11
|
+
|
|
12
|
+
GenicamError = _native.GenicamError
|
|
13
|
+
GenApiError = _native.GenApiError
|
|
14
|
+
TransportError = _native.TransportError
|
|
15
|
+
ParseError = _native.ParseError
|
|
16
|
+
MissingChunkFeatureError = _native.MissingChunkFeatureError
|
|
17
|
+
UnsupportedPixelFormatError = _native.UnsupportedPixelFormatError
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"GenicamError",
|
|
21
|
+
"GenApiError",
|
|
22
|
+
"TransportError",
|
|
23
|
+
"ParseError",
|
|
24
|
+
"MissingChunkFeatureError",
|
|
25
|
+
"UnsupportedPixelFormatError",
|
|
26
|
+
]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Frame wrapper around the native ``_Frame`` handle."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Frame:
|
|
12
|
+
"""One image frame produced by the camera.
|
|
13
|
+
|
|
14
|
+
Wraps a native handle — the payload is only materialized into Python
|
|
15
|
+
memory when ``payload()``, ``to_numpy()``, or ``to_rgb8()`` is called.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
__slots__ = ("_native",)
|
|
19
|
+
|
|
20
|
+
def __init__(self, native: Any) -> None:
|
|
21
|
+
self._native = native
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def width(self) -> int:
|
|
25
|
+
return self._native.width
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def height(self) -> int:
|
|
29
|
+
return self._native.height
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def pixel_format(self) -> str:
|
|
33
|
+
"""Human-readable PFNC name, e.g. ``"Mono8"`` or ``"BayerRG8"``."""
|
|
34
|
+
return self._native.pixel_format
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def pixel_format_code(self) -> int:
|
|
38
|
+
"""Raw PFNC numeric code."""
|
|
39
|
+
return self._native.pixel_format_code
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def ts_dev(self) -> Optional[int]:
|
|
43
|
+
"""Device tick timestamp (camera-local), if reported."""
|
|
44
|
+
return self._native.ts_dev
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def ts_host(self) -> Optional[float]:
|
|
48
|
+
"""Host-mapped timestamp as POSIX seconds, if available."""
|
|
49
|
+
return self._native.ts_host
|
|
50
|
+
|
|
51
|
+
def payload(self) -> bytes:
|
|
52
|
+
"""Raw payload as ``bytes`` (one copy)."""
|
|
53
|
+
return self._native.payload()
|
|
54
|
+
|
|
55
|
+
def to_numpy(self) -> "np.ndarray":
|
|
56
|
+
"""Return a NumPy array shaped for the pixel format.
|
|
57
|
+
|
|
58
|
+
- Mono8 → ``(H, W) uint8``
|
|
59
|
+
- Mono16 → ``(H, W) uint16``
|
|
60
|
+
- RGB8 / BGR8 / Bayer8 → ``(H, W, 3) uint8``
|
|
61
|
+
- unknown → ``(N,) uint8``
|
|
62
|
+
"""
|
|
63
|
+
return self._native.to_numpy()
|
|
64
|
+
|
|
65
|
+
def to_rgb8(self) -> "np.ndarray":
|
|
66
|
+
"""Always return an ``(H, W, 3) uint8`` RGB array."""
|
|
67
|
+
return self._native.to_rgb8()
|
|
68
|
+
|
|
69
|
+
def __repr__(self) -> str:
|
|
70
|
+
return self._native.__repr__()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
__all__ = ["Frame"]
|