diff --git a/docs/api.md b/docs/api.md index 5eaaeb4..7ae81b8 100644 --- a/docs/api.md +++ b/docs/api.md @@ -244,6 +244,243 @@ flow = sensor.read_info(sensor.info.VRAW) *** *** + +## `orisys.finger` 指纹 UV pipeline + +`examples/finger_pipeline.py` 展示了一个更适合指纹/单相机场景的高层 API: + +1. 使用 `FingerRuntime.open()` 打开视频源、加载 Blender 导出的资产,并构建 UV 展开映射。 +2. 用 `grab_and_process()` 或 `read_source() + process_frame()` 获取一帧展开后的触觉图像。 +3. 通过 `runtime.tactile`(`TactileProcessor`)调用 `compute_deformation()`、`compute_contact()`、`read_info()`。 +4. 退出前调用 `runtime.close()` 释放视频源和内部 `Sensor` 资源。 + +相比直接使用 `Sensor`,`FingerRuntime` 已封装了视频读取、相机尺寸设置、UV 展开和有效区域掩码传递,更适合作为指纹 2D/3D 示例与上层应用的入口。 + +### 1. `FingerRuntime.open` 方法 + +#### 描述 + +创建一个可复用的指纹 UV runtime。该方法会完成以下初始化: + +- 解析 `export_dir` 下的 `mesh.npz`、`camera_intrinsics.npz`、`world_to_cam.npy` +- 根据配置和标定信息构建/加载 `unroll_map_uv.npz` +- 打开相机或视频文件 +- 创建内部 `Sensor("external", ...)` +- 创建 `UVUnrollFrameProcessor` 与 `TactileProcessor` + +#### 输入参数 + +* **export_dir** (`str`,可选): 指纹导出资产目录,默认值来自 `orisys.finger.DEFAULT_FINGER_EXPORT_DIR`。目录下通常需要包含 `mesh.npz`、`camera_intrinsics.npz`、`world_to_cam.npy`。 +* **config** (`str`,可选): Orisys 配置文件路径,默认 `./config/dd02-ov.json`。 +* **map_cache** (`str`,可选): UV 展开缓存文件路径,默认值来自 `orisys.finger.DEFAULT_FINGER_MAP_CACHE`。 +* **source_scale** (`float`,可选): 输入相机分辨率缩放比例;未提供时根据配置自动推断。 +* **calib** (`str | None`,可选): 外部相机标定文件路径;未提供时优先使用导出资产中的内参。 +* **video** (`int | str`,可选): 相机编号、数字字符串或视频路径,默认 `0`。 +* **camera_id** (`int | None`,可选): `video` 的别名;若提供则优先使用。 +* **flip_source_vertical** (`bool`,可选): 是否在 UV 展开前垂直翻转输入源,默认 `True`。 +* **verbose** (`bool`,可选): 是否输出初始化日志。 +* **mirror** (`bool`,可选): 是否对内部触觉图再做镜像,默认 `False`。 + +#### 返回 + +* `FingerRuntime` 实例。 + +#### 示例 + +```python +from orisys.finger import FingerRuntime + +runtime = FingerRuntime.open( + config="./config/dd02-ov.json", + video=0, + verbose=True, +) +``` + +### 2. `FingerRuntime` 常用属性 + +* **runtime.geometry**: `FingerGeometry`,包含输入/输出分辨率、导出资产路径、相机内参、缓存路径等一次性初始化结果。 +* **runtime.domain**: UV 展开域对象,内部包含 remap、valid mask 以及可选 surface 数据。 +* **runtime.tactile**: `TactileProcessor`,用于执行形变、接触和数据读取。 +* **runtime.capture**: OpenCV `VideoCapture` 对象。 +* **runtime.valid_mask**: 当前 UV 域有效像素掩码。 + +### 3. `FingerRuntime.read_source` 方法 + +#### 描述 + +仅读取一帧原始视频源,等价于对内部 `cv2.VideoCapture` 调用 `read()`。适合需要把采集和 UV 展开分别计时的场景。 + +#### 返回 + +* `(ok, frame)`: + * **ok** (`bool`): 是否成功读取。 + * **frame** (`np.ndarray | None`): 原始 BGR 帧。 + +### 4. `FingerRuntime.process_frame` 方法 + +#### 描述 + +对一帧原始输入执行源图预处理、UV 展开,并把展开后的结果送入内部 `TactileProcessor`。这是 `examples/finger_pipeline.py` 中 profile 模式使用的方法。 + +#### 输入参数 + +* **frame** (`np.ndarray`,必填): 原始输入帧。 +* **color** (`bool`,可选): 为 `False` 时走灰度展开流程;为 `True` 时保留 BGR 颜色展开。默认 `False`。 + +#### 返回 + +* `np.ndarray`: 展开后的触觉图像。`color=False` 时通常为单通道灰度图;`color=True` 时为 BGR 图。 + +### 5. `FingerRuntime.grab_and_process` 方法 + +#### 描述 + +先读取一帧视频源,再调用 `process_frame()` 完成 UV 展开并推入内部触觉处理器。非 profile 模式下,`examples/finger_pipeline.py` 使用该方法作为主循环入口。 + +#### 输入参数 + +* **color** (`bool`,可选): 是否保留颜色,默认 `False`。 + +#### 返回 + +* `(ok, unrolled)`: + * **ok** (`bool`): 是否成功读取并处理。 + * **unrolled** (`np.ndarray | None`): 展开后的触觉图像。 + +### 6. `FingerRuntime.get_img` 方法 + +#### 描述 + +`Sensor.get_img()` 风格的别名:内部执行一次采集和 UV 展开,并在成功时返回当前展开图,否则返回 `None`。 + +#### 输入参数 + +* **color** (`bool`,可选): 是否保留颜色,默认 `False`。 + +#### 返回 + +* `np.ndarray | None` + +### 7. `FingerRuntime.warmup` 方法 + +#### 描述 + +先执行一次采集与 UV 展开,然后调用 `runtime.tactile.warmup()` 预热内部光流/HHD 相关缓存。`examples/finger_pipeline.py` 在进入主循环前会先执行一次 warmup。 + +#### 输入参数 + +* **color** (`bool`,可选): 是否保留颜色,默认 `False`。 + +#### 返回 + +* `(ok, unrolled)`: + * **ok** (`bool`): 是否成功完成 warmup 前的采集。 + * **unrolled** (`np.ndarray | None`): warmup 时得到的展开图。 + +### 8. `FingerRuntime.close` 方法 + +#### 描述 + +释放内部 `VideoCapture` 并调用 `runtime.tactile.disconnect()` 清理 `Sensor` 资源。程序退出前应调用。 + +### 9. `TactileProcessor` 类别 + +#### 描述 + +`TactileProcessor` 是对内部 `Sensor("external", ...)` 的窄封装。它保留了 `compute_deformation()`、`compute_contact()`、`read_info()`、`reset()` 等常用接口,同时屏蔽了视频采集与 UV 展开细节。 + +#### 常用属性 + +* **raw_sensor**: 底层 `Sensor` 对象。 +* **info**: 与 `Sensor.info` 相同的枚举入口。 +* **img / flow / vnormal / vshear / depth_map**: 当前帧处理结果。 +* **mask_stats**: 有效 UV 区域统计信息;`examples/finger_pipeline.py` 会读取 `mask_stats["coverage"]` 输出当前掩码覆盖率。 +* **valid_mask**: 当前有效区域掩码。 + +#### 说明 + +除 `push_frame()` / `warmup()` 之外,`TactileProcessor` 的 `compute_deformation()`、`compute_contact()`、`read_info()`、`reset()` 调用方式与 `Sensor` 基本一致,因此可直接复用前文 `Sensor` 章节中的参数说明。 + +### 10. 最小指纹 pipeline 示例 + +下面的流程与 `examples/finger_pipeline.py` 一致: + +```python +import time +import cv2 +import orisys +from orisys.finger import FingerRuntime + +runtime = FingerRuntime.open( + config="./config/dd02-ov.json", + video=0, + verbose=True, +) + +runtime.warmup(color=False) +tactile = runtime.tactile + +try: + while True: + ok, unrolled = runtime.grab_and_process(color=False) + if not ok: + break + + tactile.compute_deformation(check_motion=True, threshold=0) + tactile.compute_contact() + + fps, flow, vnormal, img, contour, centroid, depth_map, fnormal, fshearx, fsheary = tactile.read_info( + tactile.info.FPS, + tactile.info.VRAW, + tactile.info.VNORMAL, + tactile.info.IMG, + tactile.info.CONTOUR, + tactile.info.CENTROID, + tactile.info.DEPTH, + tactile.info.FNORMAL, + tactile.info.FSHEARX, + tactile.info.FSHEARY, + ) + + arrows = orisys.util.draw_arrows( + unrolled, + flow, + threshold=0.4, + grid_spacing=10, + arrow_scale=1.0, + ) + cv2.imshow("UV Unrolled", arrows) + + print( + f"FPS={fps:.2f} normal={fnormal:.2f} " + f"shear=({fshearx:.2f}, {fsheary:.2f}) " + f"mask={tactile.mask_stats['coverage']:.2f}", + end="\r", + ) + + key = cv2.waitKey(1) & 0xFF + if key == ord("q"): + break + if key == ord("r"): + tactile.reset() +finally: + runtime.close() + cv2.destroyAllWindows() +``` + +### 11. 关于 profile 模式 + +`examples/finger_pipeline.py` 中的 `--profile` / `--profile-every` 是示例脚本参数,不属于 `orisys` SDK 核心 API。本质上它只是把主循环拆成: + +1. `runtime.read_source()` +2. `runtime.process_frame(...)` +3. `tactile.compute_deformation(...)` +4. `tactile.compute_contact()` +5. `tactile.read_info(...)` + +并对各阶段做耗时统计。因此在业务代码中,也可以按同样方式自行插入计时逻辑。 + # 可视化相关 ## 1. `util.draw_arrows` 方法 diff --git a/docs/get_started.md b/docs/get_started.md index eb9722e..42e73e6 100644 --- a/docs/get_started.md +++ b/docs/get_started.md @@ -112,7 +112,13 @@ print(orisys.__version__) # 使用方法 ## 运行示例程序 -套件里面包含了例子展示用法,运行步骤如下: +套件里面包含了多个例子,建议都在仓库根目录运行(即 `config/`、`examples/`、`assets/` 同级目录)。常见入口分为两类: + +1. `examples/basic_pipeline.py`:复眼/拼接传感器基础 OpenCV 示例 +2. `examples/finger_pipeline.py`:指纹传感器 UV 展开最小流程示例 +3. `examples/finger_pipeline_3dview.py`:指纹传感器 Qt 3D 可视化界面 + +下面先介绍最基础的 `basic_pipeline.py` 运行方式。 ### Windows: 1. 检查是否有其他正在连接的USB摄像头设备:打开powershell, 运行命令`Get-PnpDevice -PresentOnly | Where-Object { $_.Class -match '^Camera' }`,如果没有的话不会有输出。 @@ -134,5 +140,54 @@ print(orisys.__version__) ``` 5. 程序会运行,并自动打开可视化窗口和打印资料,在窗口上按键盘”q”键可以停止运行,按”r”键可以重设数据 +### 指纹传感器 UV 示例 + +如果您使用的是指纹/单相机 UV 展开流程,可运行: + +``` bash +python examples/finger_pipeline.py --camera_id 0 --config ./config/dd02-ov.json +``` + +如果输入来自视频文件,也可以改用: + +``` bash +python examples/finger_pipeline.py --video ./data/sample.mp4 --config ./config/dd02-ov.json +``` + +该示例会: + +- 读取相机或视频源 +- 将图像展开到 UV 平面 +- 计算光流、法向/切向力和接触区域 +- 在 OpenCV 窗口中显示展开后的光流箭头图 + +常用参数: + +- `--verbose`:输出更详细的初始化日志 +- `--profile`:打印每个阶段的耗时统计 +- `--profile-every N`:每 N 帧输出一次平均耗时 + +例如: + +``` bash +python examples/finger_pipeline.py --camera_id 0 --config ./config/dd02-ov.json --profile --profile-every 30 +``` + +### 指纹 3D 可视化界面 + +如果已经安装 GUI 依赖,还可以运行: + +``` bash +pip install -e ".[gui]" +python examples/finger_pipeline_3dview.py --video 0 --config ./config/dd02-ov.json +``` + +该界面会提供: + +- 指纹 UV 展开图 +- 力曲线 +- 3D mesh 视图 +- 录制与重置等交互控件 + ## 开发套件使用方法 参考开发套件附带的开发文档。 \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index 4ee9a3d..8d5a197 100644 --- a/examples/README.md +++ b/examples/README.md @@ -18,7 +18,7 @@ For the Qt dashboard: ```bash pip install -e ".[gui]" -python examples/finger_pipeline_3dview.py -v 0 -c ./config/dd02-ov.json +python examples/finger_pipeline_3dview.py --video 0 --config ./config/dd02-ov.json ``` For the minimal finger pipeline: @@ -27,6 +27,18 @@ For the minimal finger pipeline: python examples/finger_pipeline.py --camera_id 0 --config ./config/dd02-ov.json ``` +For the minimal finger pipeline with profiling: + +```bash +python examples/finger_pipeline.py --camera_id 0 --config ./config/dd02-ov.json --profile --profile-every 30 +``` + +For a video-file finger run: + +```bash +python examples/finger_pipeline.py --video ./data/sample.mp4 --config ./config/dd02-ov.json +``` + For the basic compound-eye pipeline: ```bash @@ -50,3 +62,5 @@ pip install -e ".[gui]" - `finger_pipeline_3dview.py` is the main interactive demo. It uses `app_paths.py` so the same entry point works in editable and packaged runs. - Dashboard controls: click **启动** to start acquisition; `Q` quits, `R` resets, `S` starts recording, `E` stops recording, and `Esc` toggles fullscreen. - `finger_pipeline.py` is the leanest finger example if you want to validate UV unroll + force/contact processing without the full dashboard. +- `finger_pipeline.py` uses `orisys.finger.FingerRuntime` as the high-level entry point, then runs tactile analysis through `runtime.tactile`. +- In `--profile` mode, the example splits the loop into capture, UV unroll, deformation, contact, and display stages so you can measure each step separately.