04 Hybrid Vision Toolkit Python API
本文档对应 v2.0 的 Python 绑定 —— 单一 hv_toolkit 模块(pybind11 生成,源码 src/python/hv_toolkit_module.cpp),同一套 API 同时支持 USB(x86_64)与 MIPI HVS(S100 / X5)。
事实来源:本文以仓库
src/python/hv_toolkit_module.cpp为准,字段/方法与 C++ 公有 API(API.md→ Python 绑定附录)一一对应。绑定变动时以此文件为锚点同步。v2.0 已移除旧版四个独立模块(
hv_evt2_codec_python/hv_event_reader_python/hv_event_writer_python/hv_camera_python),统一为一个hv_toolkit模块。预编译分发:发布仓
lib/x86_64/python/hv_toolkit.cpython-310-x86_64-linux-gnu.so(Python 3.10)随仓库分发,板卡上需自行交叉编译;RK3588 待适配。
模块概览
import hv_toolkit as hv
hv.__version__ # "2.0.0"导出表面一览:
| 符号 | 类别 | 作用 |
|---|---|---|
EventCD | 类 | 单个事件(x/y/t/polarity 可读写)。 |
Frame | 类 | 帧:width/height/frame_id/format + 只读 evs/aps(numpy 视图) |
Backend | 枚举 | 采集后端:Auto/Usb/Mipi/MipiHvs/Ethernet。 |
EventFormat | 枚举 | 事件格式:Evt2/Evt3。 |
PixelFormat | 枚举 | APS 像素格式:BayerRG8/RGB888/Gray8/RAW8/RAW10/NV12。 |
QueuePolicy | 枚举 | 队列策略:DropOldest/Block。 |
RawFormat | 枚举 | RAW 文件格式:Evt2/Evt3。 |
DeviceConfig | 类 | 后端配置(USB + MIPI + Ethernet 全字段)。 |
Camera | 类 | 统一采集相机。 |
Evt2Decoder / Evt2Encoder | 类 | EVT2 解码器 / 编码器。 |
Evt3Decoder / Evt3Encoder | 类 | EVT3 解码器 / 编码器。 |
MipiRaw8Decoder | 类 | MIPI HVS apx003 RAW8 子帧流解码器。 |
MipiRaw8Layout | 类 | RAW8 子帧布局常量(kSubframeBytes 等)。 |
extract_evs_timestamp(data) | 函数 | 从 RAW8 子帧头提取传感器时间戳。 |
仍未导出(按需再用 C++ API,见 C++ API):异步回调(
set_frame_callback/set_event_callback/set_image_callback)、EventReader/EventWriter、HybridReader/HybridWriter。
EventCD
单个 CD 事件,字段均可读写。
e = hv.EventCD()
e.x = 100
e.y = 50
e.t = 12345 # 微秒
e.polarity = True # True = CD_ON, False = CD_OFF| 属性 | 类型 | 含义 |
|---|---|---|
x / y | int | 像素坐标。 |
t | int | 时间戳(微秒)。 |
polarity | bool | True = CD_ON,False = CD_OFF。 |
Frame
帧对象。evs/aps 是零拷贝 numpy uint8 视图:底层数据是池 slab,视图持 owner 引用计数,Frame 释放后视图仍有效(直到视图自身被回收)。
| 属性 | 类型 | 含义 |
|---|---|---|
width / height | int | 帧宽 / 高(可读写)。 |
frame_id | int | 帧序号(可读写)。 |
format | PixelFormat | APS 像素格式(MIPI HVS 通常为 NV12)。 |
evs | numpy.ndarray(uint8) | 只读,HAL 未解码的原始事件字节(格式依后端与 event_fmt)。 |
aps | numpy.ndarray(uint8) | 只读,APS 原始字节(MIPI HVS 为 NV12)。 |
f = hv.Frame()
if cam.get_frame(f, 1000):
raw = bytes(f.evs) # 转 bytes 喂给 decoder
print(f.aps.nbytes, "bytes APS", f.format)枚举
hv.Backend.Usb # USB 后端(x86_64)
hv.Backend.MipiHvs # MIPI HVS 双 VC 后端(S100 / X5;RK3588 待适配)
hv.EventFormat.Evt2 # / Evt3
hv.PixelFormat.NV12 # MIPI HVS APS 输出格式
hv.QueuePolicy.DropOldest
hv.RawFormat.Evt3 # RAW 文件格式DeviceConfig
采集配置,传给 Camera.init()。USB / MIPI / Ethernet 字段都在同一个类上,按所选 backend 填对应字段即可。
| 分组 | 属性 | 类型 | 含义 |
|---|---|---|---|
| 通用 | backend | Backend | 采集后端。 |
| 通用 | event_fmt | EventFormat | 事件格式(USB/Ethernet 用)。 |
| 通用 | buffer_count | int | 帧缓冲数(默认 8)。 |
| 通用 | queue_policy | QueuePolicy | 帧队列策略(默认 DropOldest)。 |
| 通用 | evs_fps | int | MIPI 帧率档(0 = 默认 240;可选 120/240/300/500/750/1000;非档位值报错;init 时下发)。 |
| USB | vendor_id | int | USB 厂商 ID。 |
| USB | product_id | int | USB 产品 ID。 |
| USB | event_urbs | int | USB 事件端点在途 URB 数(默认 4)。 |
| MIPI | device_node | str | MIPI 设备节点(如 /dev/video0)。 |
| MIPI | sensor_index | int | MIPI sensor 索引(VC0 EVS 配置索引,默认 0)。 |
| MIPI | i2c_bus | int | 安全芯片认证 I2C 总线(默认 1)。 |
| Ethernet | ip | str | 对端 IP。 |
| Ethernet | data_port / ctrl_port | int | 数据 / 控制端口(默认 8000/8001)。 |
| Ethernet | listen_port | int | TCP 监听端口(默认 8888)。 |
| Ethernet | bind_ip | str | 本地绑定 IP(空 = INADDR_ANY)。 |
# USB(x86_64)
cfg = hv.DeviceConfig()
cfg.backend = hv.Backend.Usb
cfg.vendor_id = 0x1d6b
cfg.product_id = 0x0105
# MIPI HVS(S100 / X5)
cfg = hv.DeviceConfig()
cfg.backend = hv.Backend.MipiHvs
cfg.device_node = "/dev/video0"
cfg.sensor_index = 0 # S100 默认 9;X5 默认 49(由 SDK 配置决定)
cfg.i2c_bus = 1
cfg.evs_fps = 0 # 可选:120/240/300/500/750/1000;0=默认 240Camera
统一采集相机,与 C++ Shimeta::hv::Camera 对应。方法名为 Python 风格(snake_case),全后端共用。
cam = hv.Camera()
cam.init(cfg) # cfg: DeviceConfig;返回 bool
cam.start_stream() # 返回 bool(是否连上设备)
f = hv.Frame()
ok = cam.get_frame(f, 1000) # timeout_ms 默认 1000;返回是否取到
cam.stop_stream()
cam.destroy()| 方法 | 作用 | 参数 / 返回 |
|---|---|---|
init(cfg) | 按 DeviceConfig 初始化后端 | cfg(in) DeviceConfig;返回 bool |
start_stream() | 启动采集线程。 | 返回 bool(是否连上设备)。 |
stop_stream() | 停止采集并 join 线程。 | — |
destroy() | 释放后端资源。 | — |
get_frame(frame, timeout_ms=1000) | 同步拉取一帧(事件 + APS) | frame (in/out) Frame¹;返回 bool |
¹ timeout_ms 默认 1000。 | set_exposure(value) | 设置 APS 曝光。 | 返回 bool。 | | set_frame_rate(fps) | 设置 EVS 事件帧率(USB/Ethernet 支持)。 | 返回 bool。 | | get_frame_rate() | 读当前 EVS 帧率。 | 返回 (ok: bool, fps: int)。 | | sync_clock() | 时钟同步(如 Ethernet PTP)。 | 返回 bool。 |
编解码器
Decoder.decode() 收 bytes,返回 numpy 结构化数组(dtype 字段 x/y/t/polarity);Encoder.encode() 收 list[EventCD] 返回 bytes。EVT2/EVT3 编解码器有状态(跨包维护时间基),连续流复用同一实例、新流前调 reset();MipiRaw8Decoder 无状态。
ev = dec.decode(b"\x00\x01...") # → ndarray,字段 x/y/t/polarity
len(ev) # 事件数
ev['x'] # 所有事件的 x 坐标(ndarray)
ev[:100] # 切片EVT2
32-bit word 流,USB 后端默认事件格式。
Evt2Decoder:解码 EVT2 32-bit word 字节流为 CD 事件数组。Evt2Encoder:把事件编码为 EVT2 32-bit word 字节流(含必要的 TimeHigh 冗余字)。
dec = hv.Evt2Decoder()
events = dec.decode(bytes(f.evs)) # f.evs → ndarray
dec.reset()
enc = hv.Evt2Encoder()
raw = enc.encode([e1, e2]) # list[EventCD] → bytes| 方法 | 功能 | 参数 / 返回 |
|---|---|---|
Evt2Decoder.decode(data) | 解码 EVT2 字节流为事件数组 | data: bytes → numpy.ndarray¹ |
Evt2Encoder.encode(events) | 编码事件为 EVT2 字节流 | events: list[EventCD] → bytes |
*.reset() | 清除时间状态,新流前调用 | — |
¹ 字段:x / y / t / polarity。
EVT3
16-bit word 流,event_fmt=Evt3 时 Frame.evs 用此格式(decode 入参字节数须为 2 的倍数)。
Evt3Decoder:解码 EVT3 16-bit word 字节流为 CD 事件数组。Evt3Encoder:把事件编码为 EVT3 16-bit word 字节流。
dec = hv.Evt3Decoder(); events = dec.decode(raw_bytes)
enc = hv.Evt3Encoder(); raw = enc.encode([e1, e2])方法签名与语义同 EVT2:decode(data: bytes)→ndarray、encode(list[EventCD])→bytes、reset() 清状态。
MIPI RAW8
apx003 子帧流,仅 MIPI HVS 后端产生;Frame.evs 必须用 MipiRaw8Decoder 解码,不能用 EVT2/EVT3。
MipiRaw8Decoder:解码 apx003 RAW8 子帧流为 CD 事件数组,无状态。
dec = hv.MipiRaw8Decoder()
events = dec.decode(bytes(f.evs)) # 默认自动档:按数据长度解全部子帧
events = dec.decode(bytes(f.evs), subframe_count=4) # 仅解前 4 个子帧| 方法 | 功能 | 参数 / 返回 |
|---|---|---|
MipiRaw8Decoder.decode(data, subframe_count=0) | 解码 RAW8 子帧流 | data: bytes¹ → numpy.ndarray |
¹ subframe_count: int,≤0 = 自动档按长度解全部;>0 = 仅前 N 个。
MipiRaw8Layout:RAW8 子帧布局常量(类属性),供按子帧分片解码时引用。
| 常量 | 值 | 含义 |
|---|---|---|
kSubWidth / kSubHeight | 384 / 304 | 单子帧分辨率 |
kEvsWidth / kEvsHeight | 768 / 608 | 整帧 EVS 分辨率 |
kSubframeBytes | 32768 | 单子帧字节数 |
kTotalSubframes | 32 | 整包子帧数(4 空间 × 8 合并) |
extract_evs_timestamp(data):从 apx003 RAW8 子帧头提取传感器时间戳(45-bit / 200 → 微秒)。与MipiRaw8Decoder配合——先取时间戳、再解码事件。
raw_ts, processed_us, valid = hv.extract_evs_timestamp(bytes(f.evs))
# raw_ts: 45-bit 原始时间戳;processed_us: raw_ts/200(微秒);valid: 是否有效USB 与 MIPI HVS 对照
| USB(x86_64) | MIPI HVS(S100 / X5) | |
|---|---|---|
backend | Backend.Usb | Backend.MipiHvs |
DeviceConfig 关键字段 | vendor_id/product_id | device_node/sensor_index/i2c_bus/evs_fps |
Frame.evs 解码器 | Evt2Decoder / Evt3Decoder | MipiRaw8Decoder |
Frame.format | NV12 | NV12(S100)/ Gray8(X5) |
Camera / Frame / get_frame / stop_stream 等全部一致——后端只是 DeviceConfig.backend 的运行时取值,不需要学第二套 API。
构建与部署
发布仓 lib/x86_64/python/ 已预编译 Python 模块(cpython-310-x86_64),免构建:
sudo ./run.sh install x86_64 # 装库到系统路径
python3 -c "import hv_toolkit; print(hv_toolkit.__version__)" # 冒烟
python3 samples/python/get_started.py源码仓或需 S100 / X5 板上运行时:
./run.sh --python build x86_64 # USB;产物 build/hv_toolkit.<abi>.so
./run.sh --python build s100 # MIPI HVS(aarch64);产物 out/s100/build/hv_toolkit.cpython-310-aarch64-linux-gnu.so
./run.sh --python build x5 # X5(aarch64);产物 out/x5/build/hv_toolkit.cpython-310-aarch64-linux-gnu.so板卡部署(设库与模块路径后运行):
export LD_LIBRARY_PATH=/app/build:$LD_LIBRARY_PATH
export PYTHONPATH=/app/build:$PYTHONPATH
python3 /app/build/samples/python/get_started_mipi.py详见 HV Toolkit 快速上手 → Python 示例 和 第一个 C++ 程序 → S100 板卡部署。
许可证
Apache License 2.0。
