organize exampels according to products
This commit is contained in:
@@ -8,7 +8,7 @@ and several runnable examples for camera and video-based workflows.
|
||||
|
||||
- `src/orisys/`: installable SDK package
|
||||
- `config/`: default and lab/runtime JSON configurations
|
||||
- `examples/`: user-facing example scripts
|
||||
- `examples/`: product demos (`products/ory_box`, `ory_mini`, `ory_finger`) plus shared `gui/`, `pipeline/`, `viz/`
|
||||
- `scripts/`: maintenance, calibration, packaging, and demo scripts
|
||||
- `tools/`: non-package tooling such as patch-generation utilities
|
||||
- `tests/`: automated pytest suite
|
||||
@@ -49,18 +49,36 @@ pytest
|
||||
|
||||
### 4. Run an example
|
||||
|
||||
ORY-BOX:
|
||||
|
||||
```bash
|
||||
python examples/basic_pipeline.py -v 0 -c ./config/ddjx01.json
|
||||
python examples/products/ory_box/basic_pipeline.py -v 0 -c ddjx01
|
||||
```
|
||||
|
||||
ORY-MINI:
|
||||
|
||||
```bash
|
||||
python examples/products/ory_mini/basic_pipeline.py -v 0 -c mini
|
||||
```
|
||||
|
||||
ORY-FINGER:
|
||||
|
||||
```bash
|
||||
python examples/products/ory_finger/finger_pipeline.py --camera_id 0 --config dd02
|
||||
```
|
||||
|
||||
Compatibility wrappers remain at `examples/basic_pipeline.py` and
|
||||
`examples/finger_pipeline*.py`.
|
||||
|
||||
## Configuration
|
||||
|
||||
The canonical configuration files now live in the repo-level `config/` folder.
|
||||
The package loader `orisys.configs` resolves configs from:
|
||||
|
||||
1. an explicit JSON file path
|
||||
2. a config file stem under `config/`
|
||||
3. a backward-compatible JSON `"name"` match, such as `"prototype"`
|
||||
2. a config file stem under `config/` (for example `ddjx01`, `mini`, `dd02`)
|
||||
3. a product alias such as `dd02` -> `dd02-ov`
|
||||
4. a backward-compatible JSON `"name"` match, such as `"prototype"`
|
||||
|
||||
Examples:
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
# ---------- Configuration ----------
|
||||
ENTRY_SCRIPT = Path("examples/finger_pipeline_3dview.py").resolve()
|
||||
ENTRY_SCRIPT = Path("examples/products/ory_finger/finger_pipeline_3dview.py").resolve()
|
||||
EXE_NAME = "OrisysDashboard.exe"
|
||||
DIST_NAME = "OrisysDashboard" # final folder name
|
||||
repo_root = Path(__file__).parent.parent
|
||||
|
||||
@@ -2,24 +2,24 @@
|
||||
"name": "prototype",
|
||||
"version": "1.0",
|
||||
"camera": {
|
||||
"resolution_raw": [1280, 720],
|
||||
"fps": 15,
|
||||
"resolution_raw": [1920, 1080],
|
||||
"fps": 30,
|
||||
"units": 15,
|
||||
"units_used": 9,
|
||||
"resolution_stitch": [263, 263],
|
||||
"resolution_stitch": [365, 365],
|
||||
"stitch_res_list": [365,263,123,63]
|
||||
},
|
||||
"stitcher": {
|
||||
"min_area_frac": 0.01,
|
||||
"max_area_frac": 0.04,
|
||||
"max_aspect_ratio": 25,
|
||||
"max_area_frac": 0.05,
|
||||
"max_aspect_ratio": 50,
|
||||
"index": [1,2,3,6,7,8,11,12,13],
|
||||
"margin": 10,
|
||||
"nominal_ov_pos": 67,
|
||||
"nominal_ov_pos": 100,
|
||||
"n": [3,3],
|
||||
"row_tol": 0.6,
|
||||
"box_size": [180, 180],
|
||||
"blend":"numpy",
|
||||
"box_size": [250, 250],
|
||||
"blend":"numba",
|
||||
"blend_list": ["numpy", "numba","none"]
|
||||
},
|
||||
"tracker": {
|
||||
@@ -31,16 +31,15 @@
|
||||
"dis_preset": "medium",
|
||||
"dis_mode": 3,
|
||||
"algorithm_list": ["raft", "dis"],
|
||||
"hhd_downsample": 3,
|
||||
"backend": "cpu"
|
||||
"hhd_downsample": 3
|
||||
},
|
||||
"contact": {
|
||||
"min_contour_area": 3000,
|
||||
"flow_threshold":1,
|
||||
"div_threshold":0.02,
|
||||
"flow_threshold":5,
|
||||
"div_threshold":0.05,
|
||||
"expansion_threshold": 0.01,
|
||||
"kernel_size": [5,5],
|
||||
"denoise_sigma": 10
|
||||
"denoise_sigma": 12
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "dd02-ov",
|
||||
"type": "single_camera",
|
||||
"version": "1.0",
|
||||
"camera": {
|
||||
"resolution_raw": [1280, 720],
|
||||
"fps": 30,
|
||||
"resolution_stitch": [420, 450],
|
||||
"source_scale": 0.4,
|
||||
"old":[672, 728, 1,0.5],
|
||||
"work": [504, 546,1],
|
||||
"work2": [420, 450,0.8],
|
||||
"work3": [350, 375,4,0.7],
|
||||
"work4": [280, 300,4,0.4,0.2]
|
||||
},
|
||||
"tracker": {
|
||||
"adaptive": true,
|
||||
"lower_threshold": 12.0,
|
||||
"upper_threshold": 18.0,
|
||||
"loop_closure_interval": 1,
|
||||
"algorithm": "dis",
|
||||
"dis_preset": "medium",
|
||||
"dis_mode": 3,
|
||||
"algorithm_list": ["raft", "dis"],
|
||||
"hhd_downsample": 5
|
||||
},
|
||||
"contact": {
|
||||
"min_contour_area": 3000,
|
||||
"flow_threshold": 0.2,
|
||||
"div_threshold": 0.022,
|
||||
"expansion_threshold": 0.01,
|
||||
"kernel_size": [5, 5],
|
||||
"denoise_sigma": 12
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -238,7 +238,7 @@ flow = sensor.read_info(sensor.info.VRAW)
|
||||
|
||||
## `orisys.finger` 手指 UV pipeline
|
||||
|
||||
`examples/finger_pipeline.py` 展示了一个更适合手指/单相机场景的高层 API:
|
||||
`examples/products/ory_finger/finger_pipeline.py` 展示了一个更适合手指/单相机场景的高层 API:
|
||||
|
||||
1. 使用 `FingerRuntime.open()` 打开视频源、加载 Blender 导出的资产,并构建 UV 展开映射。
|
||||
2. 用 `grab_and_process()` 或 `read_source() + process_frame()` 获取一帧展开后的触觉图像。
|
||||
@@ -262,7 +262,7 @@ flow = sensor.read_info(sensor.info.VRAW)
|
||||
#### 输入参数
|
||||
|
||||
* **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`。
|
||||
* **config** (`str`,可选): Orisys 配置文件名称或路径,默认 `dd02`(兼容解析到 `dd02-ov`)。
|
||||
* **map_cache** (`str`,可选): UV 展开缓存文件路径,默认值来自 `orisys.finger.DEFAULT_FINGER_MAP_CACHE`。
|
||||
* **source_scale** (`float`,可选): 输入相机分辨率缩放比例;未提供时根据配置自动推断。
|
||||
* **calib** (`str | None`,可选): 外部相机标定文件路径;未提供时优先使用导出资产中的内参。
|
||||
@@ -282,7 +282,7 @@ flow = sensor.read_info(sensor.info.VRAW)
|
||||
from orisys.finger import FingerRuntime
|
||||
|
||||
runtime = FingerRuntime.open(
|
||||
config="./config/dd02-ov.json",
|
||||
config="dd02",
|
||||
video=0,
|
||||
verbose=True,
|
||||
)
|
||||
@@ -312,7 +312,7 @@ runtime = FingerRuntime.open(
|
||||
|
||||
#### 描述
|
||||
|
||||
对一帧原始输入执行源图预处理、UV 展开,并把展开后的结果送入内部 `TactileProcessor`。这是 `examples/finger_pipeline.py` 中 profile 模式使用的方法。
|
||||
对一帧原始输入执行源图预处理、UV 展开,并把展开后的结果送入内部 `TactileProcessor`。这是 `examples/products/ory_finger/finger_pipeline.py` 中 profile 模式使用的方法。
|
||||
|
||||
#### 输入参数
|
||||
|
||||
@@ -327,7 +327,7 @@ runtime = FingerRuntime.open(
|
||||
|
||||
#### 描述
|
||||
|
||||
先读取一帧视频源,再调用 `process_frame()` 完成 UV 展开并推入内部触觉处理器。非 profile 模式下,`examples/finger_pipeline.py` 使用该方法作为主循环入口。
|
||||
先读取一帧视频源,再调用 `process_frame()` 完成 UV 展开并推入内部触觉处理器。非 profile 模式下,`examples/products/ory_finger/finger_pipeline.py` 使用该方法作为主循环入口。
|
||||
|
||||
#### 输入参数
|
||||
|
||||
@@ -357,7 +357,7 @@ runtime = FingerRuntime.open(
|
||||
|
||||
#### 描述
|
||||
|
||||
先执行一次采集与 UV 展开,然后调用 `runtime.tactile.warmup()` 预热内部光流/HHD 相关缓存。`examples/finger_pipeline.py` 在进入主循环前会先执行一次 warmup。
|
||||
先执行一次采集与 UV 展开,然后调用 `runtime.tactile.warmup()` 预热内部光流/HHD 相关缓存。`examples/products/ory_finger/finger_pipeline.py` 在进入主循环前会先执行一次 warmup。
|
||||
|
||||
#### 输入参数
|
||||
|
||||
@@ -386,7 +386,7 @@ runtime = FingerRuntime.open(
|
||||
* **raw_sensor**: 底层 `Sensor` 对象。
|
||||
* **info**: 与 `Sensor.info` 相同的枚举入口。
|
||||
* **img / flow / vnormal / vshear / depth_map**: 当前帧处理结果。
|
||||
* **mask_stats**: 有效 UV 区域统计信息;`examples/finger_pipeline.py` 会读取 `mask_stats["coverage"]` 输出当前掩码覆盖率。
|
||||
* **mask_stats**: 有效 UV 区域统计信息;`examples/products/ory_finger/finger_pipeline.py` 会读取 `mask_stats["coverage"]` 输出当前掩码覆盖率。
|
||||
* **valid_mask**: 当前有效区域掩码。
|
||||
|
||||
#### 说明
|
||||
@@ -395,7 +395,7 @@ runtime = FingerRuntime.open(
|
||||
|
||||
### 10. 最小手指 pipeline 示例
|
||||
|
||||
下面的流程与 `examples/finger_pipeline.py` 一致:
|
||||
下面的流程与 `examples/products/ory_finger/finger_pipeline.py` 一致:
|
||||
|
||||
```python
|
||||
import time
|
||||
@@ -404,7 +404,7 @@ import orisys
|
||||
from orisys.finger import FingerRuntime
|
||||
|
||||
runtime = FingerRuntime.open(
|
||||
config="./config/dd02-ov.json",
|
||||
config="dd02",
|
||||
video=0,
|
||||
verbose=True,
|
||||
)
|
||||
@@ -462,7 +462,7 @@ finally:
|
||||
|
||||
### 11. 关于 profile 模式
|
||||
|
||||
`examples/finger_pipeline.py` 中的 `--profile` / `--profile-every` 是示例脚本参数,不属于 `orisys` SDK 核心 API。本质上它只是把主循环拆成:
|
||||
`examples/products/ory_finger/finger_pipeline.py` 中的 `--profile` / `--profile-every` 是示例脚本参数,不属于 `orisys` SDK 核心 API。本质上它只是把主循环拆成:
|
||||
|
||||
1. `runtime.read_source()`
|
||||
2. `runtime.process_frame(...)`
|
||||
|
||||
+17
-14
@@ -112,13 +112,16 @@ print(orisys.__version__)
|
||||
|
||||
# 使用方法
|
||||
## 运行示例程序
|
||||
套件里面包含了多个例子,建议都在仓库根目录运行(即 `config/`、`examples/`、`assets/` 同级目录)。常见入口分为两类:
|
||||
套件里面包含了多个例子,建议都在仓库根目录运行(即 `config/`、`examples/`、`assets/` 同级目录)。按产品入口如下:
|
||||
|
||||
1. `examples/basic_pipeline.py`:复眼/拼接传感器基础 OpenCV 示例
|
||||
2. `examples/finger_pipeline.py`:手指传感器2维显示最小流程示例
|
||||
3. `examples/finger_pipeline_3dview.py`:手指传感器 Qt 3D 可视化界面
|
||||
1. `examples/products/ory_box/basic_pipeline.py`:ORY-BOX 复眼/拼接传感器基础 OpenCV 示例(默认配置 `ddjx01`)
|
||||
2. `examples/products/ory_mini/basic_pipeline.py`:ORY-MINI 基础 OpenCV 示例(默认配置 `mini`)
|
||||
3. `examples/products/ory_finger/finger_pipeline.py`:ORY-FINGER 2D UV 最小流程(默认配置 `dd02`)
|
||||
4. `examples/products/ory_finger/finger_pipeline_3dview.py`:ORY-FINGER Qt 3D 可视化界面(默认配置 `dd02`)
|
||||
|
||||
下面先介绍最基础的 `basic_pipeline.py` 运行方式。
|
||||
`dd02` 是当前 ORY-FINGER 的稳定别名,实际会解析到 `dd02-ov`。旧路径 `examples/basic_pipeline.py` / `examples/finger_pipeline*.py` 仍可作为兼容入口。
|
||||
|
||||
下面先介绍最基础的 ORY-BOX 运行方式。
|
||||
|
||||
### Windows:
|
||||
1. 检查是否有其他正在连接的USB摄像头设备:打开powershell, 运行命令`Get-PnpDevice -PresentOnly | Where-Object { $_.Class -match '^Camera' }`,如果没有的话不会有输出。
|
||||
@@ -126,7 +129,7 @@ print(orisys.__version__)
|
||||
3. 如果第一步没有输出,传感器的ID就是“0”,如果第一步有一个设备,传感器的ID就是“1”,如此类推。
|
||||
4. 在确定了传感器的ID(下方例子假设为“0”)后,打开Anaconda Prompt, 切换到工作路径,然后在命令行中输入:
|
||||
``` bash
|
||||
python examples/basic_pipeline.py -v 0
|
||||
python examples/products/ory_box/basic_pipeline.py -v 0 -c ddjx01
|
||||
```
|
||||
5. 程序会运行,并自动打开可视化窗口和打印资料,在窗口上按键盘”q”键可以停止运行,按”r”键可以重设数据
|
||||
|
||||
@@ -136,22 +139,22 @@ print(orisys.__version__)
|
||||
3. 根据新增的输出,例如”/dev/video0”,确定传感器的ID为“0”, 如果是”/dev/video3”的话ID为“3” ,如此类推。
|
||||
4. 在确定了传感器的ID(下方例子假设为“0”)后,打开终端, 激活相应的python环境,切换到工作路径,然后在命令行中输入:
|
||||
``` bash
|
||||
python examples/basic_pipeline.py -v 0
|
||||
python examples/products/ory_box/basic_pipeline.py -v 0 -c ddjx01
|
||||
```
|
||||
5. 程序会运行,并自动打开可视化窗口和打印资料,在窗口上按键盘”q”键可以停止运行,按”r”键可以重设数据
|
||||
|
||||
### 手指传感器 UV 示例
|
||||
### ORY-FINGER UV 示例
|
||||
|
||||
如果您使用的是手指/单相机 UV 展开流程,可运行:
|
||||
如果您使用的是 ORY-FINGER(当前配置别名 `dd02`),可运行:
|
||||
|
||||
``` bash
|
||||
python examples/finger_pipeline.py --camera_id 0 --config ./config/dd02-ov.json
|
||||
python examples/products/ory_finger/finger_pipeline.py --camera_id 0 --config dd02
|
||||
```
|
||||
|
||||
如果输入来自视频文件,也可以改用:
|
||||
|
||||
``` bash
|
||||
python examples/finger_pipeline.py --video ./data/sample.mp4 --config ./config/dd02-ov.json
|
||||
python examples/products/ory_finger/finger_pipeline.py --video ./data/sample.mp4 --config dd02
|
||||
```
|
||||
|
||||
该示例会:
|
||||
@@ -168,10 +171,10 @@ python examples/finger_pipeline.py --video ./data/sample.mp4 --config ./config/d
|
||||
例如:
|
||||
|
||||
``` bash
|
||||
python examples/finger_pipeline.py --camera_id 0 --config ./config/dd02-ov.json
|
||||
python examples/products/ory_finger/finger_pipeline.py --camera_id 0 --config dd02
|
||||
```
|
||||
|
||||
### 手指 3D 可视化界面
|
||||
### ORY-FINGER 3D 可视化界面
|
||||
|
||||
如果要使用 GUI ,安装GUI依赖:
|
||||
``` bash
|
||||
@@ -180,7 +183,7 @@ pip install pyqt5 pyqtgraph
|
||||
```
|
||||
然后运行:
|
||||
``` bash
|
||||
python examples/finger_pipeline_3dview.py --video 0 --config ./config/dd02-ov.json
|
||||
python examples/products/ory_finger/finger_pipeline_3dview.py --video 0 --config dd02
|
||||
```
|
||||
|
||||
该界面会提供:
|
||||
|
||||
+52
-38
@@ -1,54 +1,66 @@
|
||||
# Examples
|
||||
|
||||
Top-level demo scripts under `examples/`. Run them from the repository root.
|
||||
Product-facing demos live under `examples/products/`. Shared helpers stay in
|
||||
`pipeline/`, `viz/`, and `gui/`. Run commands from the repository root.
|
||||
|
||||
## Top-Level Files
|
||||
## Product Entry Points
|
||||
|
||||
- `README.md` — this overview of the root-level example entry points
|
||||
- `basic_pipeline.py` — basic single-sensor OpenCV demo for the compound-eye pipeline
|
||||
- `plot_data.py` — single-sensor demo with OpenCV views plus PyQtGraph force charts
|
||||
- `two_sensor_pipeline.py` — two-sensor multiprocessing demo for parallel acquisition / display
|
||||
- `finger_pipeline.py` — minimal finger UV-unroll pipeline with optional profiling
|
||||
- `finger_pipeline_3dview.py` — main finger dashboard entry point with the unified Qt UI
|
||||
| Product | Entry | Default config |
|
||||
|---------|-------|----------------|
|
||||
| ORY-BOX | `products/ory_box/basic_pipeline.py` | `ddjx01` |
|
||||
| ORY-MINI | `products/ory_mini/basic_pipeline.py` | `mini` |
|
||||
| ORY-FINGER (2D) | `products/ory_finger/finger_pipeline.py` | `dd02` |
|
||||
| ORY-FINGER (3D Qt) | `products/ory_finger/finger_pipeline_3dview.py` | `dd02` |
|
||||
|
||||
`dd02` is the stable ORY-FINGER alias and currently resolves to `dd02-ov`.
|
||||
|
||||
Compatibility wrappers still exist at the `examples/` root
|
||||
(`basic_pipeline.py`, `finger_pipeline.py`, `finger_pipeline_3dview.py`) and
|
||||
forward to the product entries above.
|
||||
|
||||
## Typical Usage
|
||||
|
||||
For the Qt dashboard:
|
||||
ORY-BOX:
|
||||
|
||||
```bash
|
||||
python examples/products/ory_box/basic_pipeline.py -v 0 -c ddjx01
|
||||
```
|
||||
|
||||
ORY-MINI:
|
||||
|
||||
```bash
|
||||
python examples/products/ory_mini/basic_pipeline.py -v 0 -c mini
|
||||
```
|
||||
|
||||
ORY-FINGER 2D:
|
||||
|
||||
```bash
|
||||
python examples/products/ory_finger/finger_pipeline.py --camera_id 0 --config dd02
|
||||
```
|
||||
|
||||
ORY-FINGER video file:
|
||||
|
||||
```bash
|
||||
python examples/products/ory_finger/finger_pipeline.py --video ./data/sample.mp4 --config dd02
|
||||
```
|
||||
|
||||
ORY-FINGER 3D Qt dashboard:
|
||||
|
||||
```bash
|
||||
pip install -e ".[gui]"
|
||||
python examples/finger_pipeline_3dview.py --video 0 --config ./config/dd02-ov.json
|
||||
python examples/products/ory_finger/finger_pipeline_3dview.py --video 0 --config dd02
|
||||
```
|
||||
|
||||
For the minimal finger pipeline:
|
||||
## Other Root Examples
|
||||
|
||||
```bash
|
||||
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
|
||||
python examples/basic_pipeline.py -v 0 -c ./config/ddjx01.json
|
||||
```
|
||||
- `plot_data.py` — single-sensor OpenCV views plus PyQtGraph force charts
|
||||
- `two_sensor_pipeline.py` — two-sensor multiprocessing demo
|
||||
|
||||
## Requirements
|
||||
|
||||
- SDK installed from the repository root, for example `pip install -e .`
|
||||
- camera index or test video path
|
||||
- matching config files under `./config/`
|
||||
- matching config names under `./config/` (or packaged `orisys/configs/`)
|
||||
|
||||
Qt / PyQtGraph examples additionally require:
|
||||
|
||||
@@ -58,8 +70,10 @@ pip install -e ".[gui]"
|
||||
|
||||
## Notes
|
||||
|
||||
- `finger_pipeline_3dview.py` is the main interactive demo. It uses `gui/app_paths.py` so the same entry point works in editable and packaged runs.
|
||||
- Dashboard controls: click **启动** to start acquisition; use **Record CSV / Stop CSV** and the CSV save-directory browser to log contact/force data; `Q` quits, `R` resets, `S` starts video recording, `E` stops video 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.
|
||||
- `products/ory_finger/finger_pipeline_3dview.py` is the main interactive finger demo.
|
||||
It uses `gui/app_paths.py` so the same entry works in editable and packaged runs.
|
||||
- Dashboard controls: click **启动** to start acquisition; use **Record CSV / Stop CSV**
|
||||
and the CSV save-directory browser to log contact/force data; `Q` quits, `R` resets,
|
||||
`S` starts video recording, `E` stops video recording, and `Esc` toggles fullscreen.
|
||||
- `products/ory_finger/finger_pipeline.py` is the leanest finger example if you want to
|
||||
validate UV unroll + force/contact processing without the full dashboard.
|
||||
|
||||
+13
-107
@@ -1,115 +1,21 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compatibility wrapper for ORY-BOX.
|
||||
|
||||
Prefer:
|
||||
python examples/products/ory_box/basic_pipeline.py -v 0 -c ddjx01
|
||||
"""
|
||||
示例 1:单传感器基础流程
|
||||
|
||||
本示例演示 Orisys SDK 的标准处理流程:
|
||||
1. 初始化传感器(摄像头或视频文件)
|
||||
2. 获取图像并计算形变
|
||||
3. 按需计算接触区域
|
||||
4. 读取并可视化结果
|
||||
from __future__ import annotations
|
||||
|
||||
SDK version: 0.4.2
|
||||
"""
|
||||
import os
|
||||
os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
|
||||
import cv2
|
||||
import orisys
|
||||
import numpy as np
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--video","-v", type=str, default="0", help="视频源:摄像头编号或视频文件路径")
|
||||
parser.add_argument("--config","-c", type=str, default="./config/ddjx01.json", help="配置文件名称或路径")
|
||||
parser.add_argument("--verbose","-verbose", type=bool, default=True, help="是否输出详细日志")
|
||||
args = parser.parse_args()
|
||||
_EXAMPLES_DIR = Path(__file__).resolve().parent
|
||||
if str(_EXAMPLES_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(_EXAMPLES_DIR))
|
||||
|
||||
# 步骤 1:创建传感器对象(必需)
|
||||
# config_name 可以是内置配置名称,也可以是自定义配置文件路径
|
||||
# cal_path 为标定文件路径;每台设备建议使用独立的标定文件
|
||||
|
||||
# 输入既可以是摄像头编号,也可以是视频文件路径
|
||||
try:
|
||||
input = int(args.video)
|
||||
is_camera = True
|
||||
sensor = orisys.Sensor(input, config_name=args.config, verbose=args.verbose)
|
||||
except:
|
||||
is_camera = False
|
||||
sensor = orisys.Sensor(args.video, config_name=args.config, verbose=args.verbose)
|
||||
|
||||
print("\n按键说明:'q' 退出程序,'r' 重置追踪器。")
|
||||
|
||||
while True:
|
||||
# 步骤 2:数据处理
|
||||
# 步骤 2.1:获取并拼接图像(必需)
|
||||
sensor.get_img()
|
||||
# 步骤 2.2:计算形变场(必需);返回值表示当前帧是否检测到接触/运动
|
||||
is_contact_now = sensor.compute_deformation(check_motion=True, threshold=0)
|
||||
# 步骤 2.3:仅在当前帧检测到接触/运动时计算接触区域
|
||||
# if is_contact_now:
|
||||
sensor.compute_contact()
|
||||
|
||||
|
||||
# 步骤 2.4:读取结果
|
||||
fps, flow, vnormal, img, contour, centroid, depth_map, fnormal, fshearx, fsheary = sensor.read_info(
|
||||
sensor.info.FPS,
|
||||
sensor.info.VRAW,
|
||||
sensor.info.VNORMAL,
|
||||
sensor.info.IMG,
|
||||
sensor.info.CONTOUR,
|
||||
sensor.info.CENTROID,
|
||||
sensor.info.DEPTH,
|
||||
sensor.info.FNORMAL,
|
||||
sensor.info.FSHEARX,
|
||||
sensor.info.FSHEARY
|
||||
)
|
||||
|
||||
# 步骤 3:可视化显示
|
||||
# 步骤 3.1:显示形变矢量场
|
||||
arrows = orisys.util.draw_arrows(
|
||||
img,
|
||||
flow,
|
||||
threshold=2,
|
||||
grid_spacing=20,
|
||||
arrow_scale=1.0
|
||||
)
|
||||
cv2.imshow("形变矢量场", arrows)
|
||||
|
||||
# 步骤 3.2:显示接触区域和质心
|
||||
image_with_foe = orisys.util.draw_contact(img, contour, centroid)
|
||||
cv2.imshow("接触区域与质心", image_with_foe)
|
||||
|
||||
# 步骤 3.3:显示深度图
|
||||
# 将深度值归一化到 0-255 范围
|
||||
div_abs = depth_map
|
||||
if div_abs.max() > div_abs.min():
|
||||
div_normalized = ((div_abs - div_abs.min()) / (div_abs.max() - div_abs.min()) * 255).astype(np.uint8)
|
||||
else:
|
||||
div_normalized = np.zeros_like(div_abs, dtype=np.uint8)
|
||||
|
||||
# 应用伪彩色映射
|
||||
cv2.imshow("深度图", cv2.applyColorMap(div_normalized, cv2.COLORMAP_JET))
|
||||
|
||||
# 步骤 3.4:打印关键结果
|
||||
print(
|
||||
f"FPS={fps:.2f}, 法向力={fnormal:.4f}, 切向力X={fshearx:.4f}, "
|
||||
f"切向力Y={fsheary:.4f}, 深度图尺寸={depth_map.shape}, 原始分辨率={sensor.img_size_raw}"
|
||||
)
|
||||
|
||||
# 步骤 3.5:键盘控制
|
||||
key = cv2.waitKey(1) & 0xFF
|
||||
if key == ord("q"):
|
||||
print("\n正在退出示例程序...")
|
||||
break
|
||||
if key == ord("r"):
|
||||
# 当光流追踪出现漂移时,可手动重置追踪器
|
||||
sensor.reset()
|
||||
print("追踪器已重置。")
|
||||
|
||||
# =========================================================================
|
||||
# 4. 释放资源
|
||||
# =========================================================================
|
||||
sensor.disconnect() # 释放视频源及相关资源
|
||||
from products.ory_box.basic_pipeline import main
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
+10
-98
@@ -1,108 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Minimum UV-unroll finger tactile pipeline.
|
||||
"""Compatibility wrapper for ORY-FINGER 2D pipeline.
|
||||
|
||||
Offline: load Blender export -> build_or_load_domain -> unroll_map_uv.npz
|
||||
Runtime: camera frame -> FrameUnwarper -> TactileProcessor -> forces/contact
|
||||
|
||||
For the full demo (UV debug panels, dashboard, benchmarks) see:
|
||||
tools/3d_patch_generation_blender/unroll_realtime.py
|
||||
Prefer:
|
||||
python examples/products/ory_finger/finger_pipeline.py --camera_id 0 --config dd02
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
os.environ.setdefault("OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS", "0")
|
||||
_EXAMPLES_DIR = Path(__file__).resolve().parent
|
||||
if str(_EXAMPLES_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(_EXAMPLES_DIR))
|
||||
|
||||
import cv2
|
||||
import orisys
|
||||
from orisys.finger import FingerRuntime
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Minimum UV-unroll finger tactile pipeline")
|
||||
parser.add_argument("--camera_id", type=int, default=0)
|
||||
parser.add_argument("--video", help="Video file path (overrides camera)")
|
||||
parser.add_argument("--config", "-c", default="./config/dd02-ov.json", help="Orisys SDK config")
|
||||
parser.add_argument("--show", action="store_true", default=True, help="Show unrolled image window")
|
||||
parser.add_argument("--verbose", action="store_true", help="SDK verbose logs")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
video = args.camera_id
|
||||
if args.video:
|
||||
video = args.video
|
||||
runtime = FingerRuntime.open(
|
||||
config=args.config,
|
||||
video=video,
|
||||
verbose=args.verbose,
|
||||
)
|
||||
except RuntimeError as exc:
|
||||
print(str(exc), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if args.verbose:
|
||||
geometry = runtime.geometry
|
||||
print(
|
||||
f"UV geometry: native={geometry.native_width}x{geometry.native_height}, "
|
||||
f"source={geometry.source_width}x{geometry.source_height} "
|
||||
f"output={geometry.out_width}x{geometry.out_height} "
|
||||
)
|
||||
|
||||
runtime.warmup(color=False)
|
||||
tactile = runtime.tactile
|
||||
|
||||
print("Press 'q' to quit, 'r' to reset tracker.")
|
||||
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,
|
||||
)
|
||||
|
||||
status = (
|
||||
f"FPS={fps:.2f} normal={fnormal:.2f} shear=({fshearx:.2f}, {fsheary:.2f}) "
|
||||
f"mask={tactile.mask_stats['coverage']:.2f}"
|
||||
)
|
||||
|
||||
if args.show:
|
||||
arrows = orisys.util.draw_arrows(
|
||||
unrolled,
|
||||
flow,
|
||||
threshold=0.4,
|
||||
grid_spacing=10,
|
||||
arrow_scale=1.0,
|
||||
)
|
||||
cv2.imshow("UV Unrolled", arrows)
|
||||
|
||||
print(status, end="\r")
|
||||
|
||||
key = cv2.waitKey(1) & 0xFF
|
||||
if key == ord("q"):
|
||||
print("\nExiting.")
|
||||
break
|
||||
if key == ord("r"):
|
||||
tactile.reset()
|
||||
print("\nTracker reset.")
|
||||
finally:
|
||||
runtime.close()
|
||||
cv2.destroyAllWindows()
|
||||
from products.ory_finger.finger_pipeline import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,75 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
UV-unroll finger tactile pipeline with unified Qt dashboard.
|
||||
"""Compatibility wrapper for ORY-FINGER 3D Qt dashboard.
|
||||
|
||||
Where to customize the GUI:
|
||||
Layout (sizes, proportions) -> examples/gui/layout.py <-- edit here
|
||||
Colors / QSS -> examples/gui/styles.py
|
||||
Widget rendering logic -> examples/gui/widgets/
|
||||
Window behavior -> examples/gui/dashboard.py
|
||||
Prefer:
|
||||
python examples/products/ory_finger/finger_pipeline_3dview.py --video 0 --config dd02
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
os.environ.setdefault("OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS", "0")
|
||||
_EXAMPLES_DIR = Path(__file__).resolve().parent
|
||||
if str(_EXAMPLES_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(_EXAMPLES_DIR))
|
||||
|
||||
from gui.app_paths import configure_frozen_runtime, default_config_path, ensure_examples_import_path
|
||||
|
||||
configure_frozen_runtime()
|
||||
ensure_examples_import_path()
|
||||
|
||||
from gui import run_dashboard
|
||||
from pipeline import PipelineConfig
|
||||
from viz import DEFAULT_FINGER_VIZ_POSE
|
||||
|
||||
|
||||
def _parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="UV-unroll finger tactile pipeline with unified Qt dashboard"
|
||||
)
|
||||
parser.add_argument("--video", "-v", default="0", help="Camera index or video file path")
|
||||
parser.add_argument("--config", "-c", default=default_config_path(), help="Orisys SDK config")
|
||||
parser.add_argument("--cal", default="", help="Optional calibration file path")
|
||||
parser.add_argument("--verbose", action="store_true", help="SDK verbose logs")
|
||||
parser.add_argument(
|
||||
"--mesh-map",
|
||||
choices=["flow", "depth"],
|
||||
default="depth",
|
||||
help="Color the 3D mesh from flow magnitude or contact depth.",
|
||||
)
|
||||
parser.add_argument("--threshold", type=float, default=None, help="Mesh/arrow threshold")
|
||||
parser.add_argument("--no-3d", action="store_true", help="Disable 3D mesh view")
|
||||
parser.add_argument(
|
||||
"--viz-pose",
|
||||
default=DEFAULT_FINGER_VIZ_POSE,
|
||||
help="world_to_cam.npy for the default view; pass 'none' to skip",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--cutoff",
|
||||
type=float,
|
||||
default=5.0,
|
||||
help="Low-pass cutoff for force curves (Hz)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--profile",
|
||||
action="store_true",
|
||||
help="Print per-stage timing to the log panel / console",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--profile-every",
|
||||
type=int,
|
||||
default=30,
|
||||
help="When --profile is set, print rolling averages every N frames",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = _parse_args()
|
||||
config = PipelineConfig.from_args(args)
|
||||
sys.exit(run_dashboard(config))
|
||||
from products.ory_finger.finger_pipeline_3dview import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -27,7 +27,8 @@ def resource_path(*parts: str) -> Path:
|
||||
|
||||
|
||||
def default_config_path() -> str:
|
||||
return str(resource_path("config", "dd02-ov.json"))
|
||||
"""Default ORY-FINGER config name (resolves to dd02-ov via alias)."""
|
||||
return "dd02"
|
||||
|
||||
|
||||
def configure_frozen_runtime() -> None:
|
||||
|
||||
@@ -137,7 +137,7 @@ class SettingsPanelWidget(QWidget):
|
||||
root.addWidget(self._caption("Config"))
|
||||
config_row = QHBoxLayout()
|
||||
config_row.setSpacing(6)
|
||||
self.config_edit = QLineEdit("./config/dd02-ov.json")
|
||||
self.config_edit = QLineEdit("dd02")
|
||||
config_row.addWidget(self.config_edit, 1)
|
||||
self.btn_browse_config = QPushButton("…")
|
||||
self.btn_browse_config.setFixedWidth(32)
|
||||
|
||||
@@ -17,7 +17,7 @@ def mesh_threshold(mesh_map: str, threshold: float | None) -> float:
|
||||
@dataclass
|
||||
class PipelineConfig:
|
||||
video: str = "0"
|
||||
config: str = "./config/dd02-ov.json"
|
||||
config: str = "dd02"
|
||||
calib: str | None = None
|
||||
export_dir: str = DEFAULT_FINGER_EXPORT_DIR
|
||||
csv_save_dir: str = "./outputs/csv"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""Product-facing example entry points for ORY-BOX / ORY-MINI / ORY-FINGER."""
|
||||
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
ORY-BOX:单传感器基础流程
|
||||
|
||||
本示例演示 Orisys SDK 的标准处理流程:
|
||||
1. 初始化传感器(摄像头或视频文件)
|
||||
2. 获取图像并计算形变
|
||||
3. 按需计算接触区域
|
||||
4. 读取并可视化结果
|
||||
|
||||
SDK version: 0.4.2
|
||||
"""
|
||||
import os
|
||||
os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
|
||||
import cv2
|
||||
import orisys
|
||||
import numpy as np
|
||||
import argparse
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--video","-v", type=str, default="0", help="视频源:摄像头编号或视频文件路径")
|
||||
parser.add_argument("--config","-c", type=str, default="ddjx01", help="配置文件名称或路径")
|
||||
parser.add_argument("--verbose","-verbose", type=bool, default=True, help="是否输出详细日志")
|
||||
args = parser.parse_args()
|
||||
|
||||
# 步骤 1:创建传感器对象(必需)
|
||||
# config_name 可以是内置配置名称,也可以是自定义配置文件路径
|
||||
# cal_path 为标定文件路径;每台设备建议使用独立的标定文件
|
||||
|
||||
# 输入既可以是摄像头编号,也可以是视频文件路径
|
||||
try:
|
||||
input = int(args.video)
|
||||
is_camera = True
|
||||
sensor = orisys.Sensor(input, config_name=args.config, verbose=args.verbose)
|
||||
except:
|
||||
is_camera = False
|
||||
sensor = orisys.Sensor(args.video, config_name=args.config, verbose=args.verbose)
|
||||
|
||||
print("\n按键说明:'q' 退出程序,'r' 重置追踪器。")
|
||||
|
||||
while True:
|
||||
# 步骤 2:数据处理
|
||||
# 步骤 2.1:获取并拼接图像(必需)
|
||||
sensor.get_img()
|
||||
# 步骤 2.2:计算形变场(必需);返回值表示当前帧是否检测到接触/运动
|
||||
is_contact_now = sensor.compute_deformation(check_motion=True, threshold=0)
|
||||
# 步骤 2.3:仅在当前帧检测到接触/运动时计算接触区域
|
||||
# if is_contact_now:
|
||||
sensor.compute_contact()
|
||||
|
||||
|
||||
# 步骤 2.4:读取结果
|
||||
fps, flow, vnormal, img, contour, centroid, depth_map, fnormal, fshearx, fsheary = sensor.read_info(
|
||||
sensor.info.FPS,
|
||||
sensor.info.VRAW,
|
||||
sensor.info.VNORMAL,
|
||||
sensor.info.IMG,
|
||||
sensor.info.CONTOUR,
|
||||
sensor.info.CENTROID,
|
||||
sensor.info.DEPTH,
|
||||
sensor.info.FNORMAL,
|
||||
sensor.info.FSHEARX,
|
||||
sensor.info.FSHEARY
|
||||
)
|
||||
|
||||
# 步骤 3:可视化显示
|
||||
# 步骤 3.1:显示形变矢量场
|
||||
arrows = orisys.util.draw_arrows(
|
||||
img,
|
||||
flow,
|
||||
threshold=2,
|
||||
grid_spacing=20,
|
||||
arrow_scale=1.0
|
||||
)
|
||||
cv2.imshow("形变矢量场", arrows)
|
||||
|
||||
# 步骤 3.2:显示接触区域和质心
|
||||
image_with_foe = orisys.util.draw_contact(img, contour, centroid)
|
||||
cv2.imshow("接触区域与质心", image_with_foe)
|
||||
|
||||
# 步骤 3.3:显示深度图
|
||||
# 将深度值归一化到 0-255 范围
|
||||
div_abs = depth_map
|
||||
if div_abs.max() > div_abs.min():
|
||||
div_normalized = ((div_abs - div_abs.min()) / (div_abs.max() - div_abs.min()) * 255).astype(np.uint8)
|
||||
else:
|
||||
div_normalized = np.zeros_like(div_abs, dtype=np.uint8)
|
||||
|
||||
# 应用伪彩色映射
|
||||
cv2.imshow("深度图", cv2.applyColorMap(div_normalized, cv2.COLORMAP_JET))
|
||||
|
||||
# 步骤 3.4:打印关键结果
|
||||
print(
|
||||
f"FPS={fps:.2f}, 法向力={fnormal:.4f}, 切向力X={fshearx:.4f}, "
|
||||
f"切向力Y={fsheary:.4f}, 深度图尺寸={depth_map.shape}, 原始分辨率={sensor.img_size_raw}"
|
||||
)
|
||||
|
||||
# 步骤 3.5:键盘控制
|
||||
key = cv2.waitKey(1) & 0xFF
|
||||
if key == ord("q"):
|
||||
print("\n正在退出示例程序...")
|
||||
break
|
||||
if key == ord("r"):
|
||||
# 当光流追踪出现漂移时,可手动重置追踪器
|
||||
sensor.reset()
|
||||
print("追踪器已重置。")
|
||||
|
||||
# =========================================================================
|
||||
# 4. 释放资源
|
||||
# =========================================================================
|
||||
sensor.disconnect() # 释放视频源及相关资源
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ORY-FINGER:Minimum UV-unroll finger tactile pipeline.
|
||||
|
||||
Offline: load Blender export -> build_or_load_domain -> unroll_map_uv.npz
|
||||
Runtime: camera frame -> FrameUnwarper -> TactileProcessor -> forces/contact
|
||||
|
||||
Recommended entry:
|
||||
python examples/products/ory_finger/finger_pipeline.py --camera_id 0 --config dd02
|
||||
|
||||
Current config alias: ``dd02`` resolves to ``dd02-ov`` for compatibility.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.environ.setdefault("OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS", "0")
|
||||
|
||||
import cv2
|
||||
import orisys
|
||||
from orisys.finger import FingerRuntime
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Minimum UV-unroll finger tactile pipeline")
|
||||
parser.add_argument("--camera_id", type=int, default=0)
|
||||
parser.add_argument("--video", help="Video file path (overrides camera)")
|
||||
parser.add_argument("--config", "-c", default="dd02", help="Orisys SDK config name or path")
|
||||
parser.add_argument("--show", action="store_true", default=True, help="Show unrolled image window")
|
||||
parser.add_argument("--verbose", action="store_true", help="SDK verbose logs")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
video = args.camera_id
|
||||
if args.video:
|
||||
video = args.video
|
||||
runtime = FingerRuntime.open(
|
||||
config=args.config,
|
||||
video=video,
|
||||
verbose=args.verbose,
|
||||
)
|
||||
except RuntimeError as exc:
|
||||
print(str(exc), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if args.verbose:
|
||||
geometry = runtime.geometry
|
||||
print(
|
||||
f"UV geometry: native={geometry.native_width}x{geometry.native_height}, "
|
||||
f"source={geometry.source_width}x{geometry.source_height} "
|
||||
f"output={geometry.out_width}x{geometry.out_height} "
|
||||
)
|
||||
|
||||
runtime.warmup(color=False)
|
||||
tactile = runtime.tactile
|
||||
|
||||
print("Press 'q' to quit, 'r' to reset tracker.")
|
||||
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,
|
||||
)
|
||||
|
||||
status = (
|
||||
f"FPS={fps:.2f} normal={fnormal:.2f} shear=({fshearx:.2f}, {fsheary:.2f}) "
|
||||
f"mask={tactile.mask_stats['coverage']:.2f}"
|
||||
)
|
||||
|
||||
if args.show:
|
||||
arrows = orisys.util.draw_arrows(
|
||||
unrolled,
|
||||
flow,
|
||||
threshold=0.4,
|
||||
grid_spacing=10,
|
||||
arrow_scale=1.0,
|
||||
)
|
||||
cv2.imshow("UV Unrolled", arrows)
|
||||
|
||||
print(status, end="\r")
|
||||
|
||||
key = cv2.waitKey(1) & 0xFF
|
||||
if key == ord("q"):
|
||||
print("\nExiting.")
|
||||
break
|
||||
if key == ord("r"):
|
||||
tactile.reset()
|
||||
print("\nTracker reset.")
|
||||
finally:
|
||||
runtime.close()
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ORY-FINGER:UV-unroll finger tactile pipeline with unified Qt dashboard.
|
||||
|
||||
Where to customize the GUI:
|
||||
Layout (sizes, proportions) -> examples/gui/viewer3d/layout.py
|
||||
Colors / QSS -> examples/gui/viewer3d/styles.py
|
||||
Widget rendering logic -> examples/gui/viewer3d/widgets/
|
||||
Window behavior -> examples/gui/viewer3d/dashboard.py
|
||||
|
||||
Recommended entry:
|
||||
python examples/products/ory_finger/finger_pipeline_3dview.py --video 0 --config dd02
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
os.environ.setdefault("OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS", "0")
|
||||
|
||||
# Make shared packages importable when launched as a file path.
|
||||
_EXAMPLES_DIR = Path(__file__).resolve().parents[2]
|
||||
if str(_EXAMPLES_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(_EXAMPLES_DIR))
|
||||
|
||||
from gui.app_paths import configure_frozen_runtime, default_config_path, ensure_examples_import_path
|
||||
|
||||
configure_frozen_runtime()
|
||||
ensure_examples_import_path()
|
||||
|
||||
from gui import run_dashboard
|
||||
from pipeline import PipelineConfig
|
||||
from viz import DEFAULT_FINGER_VIZ_POSE
|
||||
|
||||
|
||||
def _parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="UV-unroll finger tactile pipeline with unified Qt dashboard"
|
||||
)
|
||||
parser.add_argument("--video", "-v", default="0", help="Camera index or video file path")
|
||||
parser.add_argument("--config", "-c", default=default_config_path(), help="Orisys SDK config")
|
||||
parser.add_argument("--cal", default="", help="Optional calibration file path")
|
||||
parser.add_argument("--verbose", action="store_true", help="SDK verbose logs")
|
||||
parser.add_argument(
|
||||
"--mesh-map",
|
||||
choices=["flow", "depth"],
|
||||
default="depth",
|
||||
help="Color the 3D mesh from flow magnitude or contact depth.",
|
||||
)
|
||||
parser.add_argument("--threshold", type=float, default=None, help="Mesh/arrow threshold")
|
||||
parser.add_argument("--no-3d", action="store_true", help="Disable 3D mesh view")
|
||||
parser.add_argument(
|
||||
"--viz-pose",
|
||||
default=DEFAULT_FINGER_VIZ_POSE,
|
||||
help="world_to_cam.npy for the default view; pass 'none' to skip",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--cutoff",
|
||||
type=float,
|
||||
default=5.0,
|
||||
help="Low-pass cutoff for force curves (Hz)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--profile",
|
||||
action="store_true",
|
||||
help="Print per-stage timing to the log panel / console",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--profile-every",
|
||||
type=int,
|
||||
default=30,
|
||||
help="When --profile is set, print rolling averages every N frames",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = _parse_args()
|
||||
config = PipelineConfig.from_args(args)
|
||||
sys.exit(run_dashboard(config))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
ORY-MINI:单传感器基础流程
|
||||
|
||||
本示例演示 Orisys SDK 的标准处理流程:
|
||||
1. 初始化传感器(摄像头或视频文件)
|
||||
2. 获取图像并计算形变
|
||||
3. 按需计算接触区域
|
||||
4. 读取并可视化结果
|
||||
|
||||
SDK version: 0.4.2
|
||||
"""
|
||||
import os
|
||||
os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
|
||||
import cv2
|
||||
import orisys
|
||||
import numpy as np
|
||||
import argparse
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--video","-v", type=str, default="0", help="视频源:摄像头编号或视频文件路径")
|
||||
parser.add_argument("--config","-c", type=str, default="mini", help="配置文件名称或路径")
|
||||
parser.add_argument("--verbose","-verbose", type=bool, default=True, help="是否输出详细日志")
|
||||
args = parser.parse_args()
|
||||
|
||||
# 步骤 1:创建传感器对象(必需)
|
||||
# config_name 可以是内置配置名称,也可以是自定义配置文件路径
|
||||
# cal_path 为标定文件路径;每台设备建议使用独立的标定文件
|
||||
|
||||
# 输入既可以是摄像头编号,也可以是视频文件路径
|
||||
try:
|
||||
input = int(args.video)
|
||||
is_camera = True
|
||||
sensor = orisys.Sensor(input, config_name=args.config, verbose=args.verbose)
|
||||
except:
|
||||
is_camera = False
|
||||
sensor = orisys.Sensor(args.video, config_name=args.config, verbose=args.verbose)
|
||||
|
||||
print("\n按键说明:'q' 退出程序,'r' 重置追踪器。")
|
||||
|
||||
while True:
|
||||
# 步骤 2:数据处理
|
||||
# 步骤 2.1:获取并拼接图像(必需)
|
||||
sensor.get_img()
|
||||
# 步骤 2.2:计算形变场(必需);返回值表示当前帧是否检测到接触/运动
|
||||
is_contact_now = sensor.compute_deformation(check_motion=True, threshold=0)
|
||||
# 步骤 2.3:仅在当前帧检测到接触/运动时计算接触区域
|
||||
# if is_contact_now:
|
||||
sensor.compute_contact()
|
||||
|
||||
|
||||
# 步骤 2.4:读取结果
|
||||
fps, flow, vnormal, img, contour, centroid, depth_map, fnormal, fshearx, fsheary = sensor.read_info(
|
||||
sensor.info.FPS,
|
||||
sensor.info.VRAW,
|
||||
sensor.info.VNORMAL,
|
||||
sensor.info.IMG,
|
||||
sensor.info.CONTOUR,
|
||||
sensor.info.CENTROID,
|
||||
sensor.info.DEPTH,
|
||||
sensor.info.FNORMAL,
|
||||
sensor.info.FSHEARX,
|
||||
sensor.info.FSHEARY
|
||||
)
|
||||
|
||||
# 步骤 3:可视化显示
|
||||
# 步骤 3.1:显示形变矢量场
|
||||
arrows = orisys.util.draw_arrows(
|
||||
img,
|
||||
flow,
|
||||
threshold=2,
|
||||
grid_spacing=20,
|
||||
arrow_scale=1.0
|
||||
)
|
||||
cv2.imshow("形变矢量场", arrows)
|
||||
|
||||
# 步骤 3.2:显示接触区域和质心
|
||||
image_with_foe = orisys.util.draw_contact(img, contour, centroid)
|
||||
cv2.imshow("接触区域与质心", image_with_foe)
|
||||
|
||||
# 步骤 3.3:显示深度图
|
||||
# 将深度值归一化到 0-255 范围
|
||||
div_abs = depth_map
|
||||
if div_abs.max() > div_abs.min():
|
||||
div_normalized = ((div_abs - div_abs.min()) / (div_abs.max() - div_abs.min()) * 255).astype(np.uint8)
|
||||
else:
|
||||
div_normalized = np.zeros_like(div_abs, dtype=np.uint8)
|
||||
|
||||
# 应用伪彩色映射
|
||||
cv2.imshow("深度图", cv2.applyColorMap(div_normalized, cv2.COLORMAP_JET))
|
||||
|
||||
# 步骤 3.4:打印关键结果
|
||||
print(
|
||||
f"FPS={fps:.2f}, 法向力={fnormal:.4f}, 切向力X={fshearx:.4f}, "
|
||||
f"切向力Y={fsheary:.4f}, 深度图尺寸={depth_map.shape}, 原始分辨率={sensor.img_size_raw}"
|
||||
)
|
||||
|
||||
# 步骤 3.5:键盘控制
|
||||
key = cv2.waitKey(1) & 0xFF
|
||||
if key == ord("q"):
|
||||
print("\n正在退出示例程序...")
|
||||
break
|
||||
if key == ord("r"):
|
||||
# 当光流追踪出现漂移时,可手动重置追踪器
|
||||
sensor.reset()
|
||||
print("追踪器已重置。")
|
||||
|
||||
# =========================================================================
|
||||
# 4. 释放资源
|
||||
# =========================================================================
|
||||
sensor.disconnect() # 释放视频源及相关资源
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+25
-6
@@ -4,25 +4,44 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "orisys"
|
||||
description = "Orisys - Vision Based Tactile Sensor SDK"
|
||||
description = "SDK for Orisys Technology Visuotactile Sensors"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
authors = [{ name = "Orisys" }]
|
||||
authors = [{ name = "Orisys-Tech" }]
|
||||
maintainers = [
|
||||
{name = "Yijian Zhang", email = "zhangyijian@orisys-tech.cn"}
|
||||
]
|
||||
dynamic = ["version"]
|
||||
dependencies = [
|
||||
"numpy>=1.24.4",
|
||||
"opencv-python>=4.10.0.84",
|
||||
"scipy>=1.10.1",
|
||||
"open3d>=0.17.0",
|
||||
"numba>=0.59.0",
|
||||
]
|
||||
classifiers = [
|
||||
# How mature is this project? Common values are
|
||||
# 3 - Alpha
|
||||
# 4 - Beta
|
||||
# 5 - Production/Stable
|
||||
"Development Status :: 4 - Beta",
|
||||
|
||||
# Indicate who your project is intended for
|
||||
"Intended Audience :: Developers",
|
||||
"Topic :: Software Development :: SDL",
|
||||
|
||||
# Specify the Python versions you support here.
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Private :: Do Not Upload",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
cuda12 = ["cupy-cuda12x>=12.3.0"]
|
||||
cuda13 = ["cupy-cuda13x>=13.0.0"]
|
||||
gui = ["open3d>=0.17.0","pyqt5>=5.15", "pyqtgraph>=0.13"]
|
||||
# Backward compatibility: 'cuda' defaults to CUDA 12.x
|
||||
cuda = ["cupy-cuda12x>=12.3.0"]
|
||||
gui = ["pyqt5>=5.15", "pyqtgraph>=0.13"]
|
||||
numba = ["numba>=0.59.0"]
|
||||
|
||||
[tool.setuptools]
|
||||
package-dir = { "" = "src" }
|
||||
@@ -52,7 +71,7 @@ dev_install = true
|
||||
name = "examples"
|
||||
source = "examples"
|
||||
target = "examples"
|
||||
patterns = ["*.py", "README.md", "viz/**/*.py", "pipeline/**/*.py", "gui/**/*.py"]
|
||||
patterns = ["*.py", "README.md", "products/**/*.py", "viz/**/*.py", "pipeline/**/*.py", "gui/**/*.py"]
|
||||
preserve_tree = true
|
||||
|
||||
[[tool.orisys.bundle.items]]
|
||||
|
||||
@@ -22,7 +22,7 @@ def main():
|
||||
|
||||
parser = argparse.ArgumentParser(description="Orisys finger Qt dashboard")
|
||||
parser.add_argument("--video", "-v", type=str, default="0")
|
||||
parser.add_argument("--config", "-c", type=str, default="./config/dd02-ov.json")
|
||||
parser.add_argument("--config", "-c", type=str, default="dd02")
|
||||
parser.add_argument("--cal", type=str, default="")
|
||||
parser.add_argument("--verbose", action="store_true")
|
||||
parser.add_argument("--cutoff", type=float, default=10.0)
|
||||
|
||||
+18
-5
@@ -1,6 +1,12 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
# Product-facing names that map to on-disk config stems.
|
||||
# ORY-FINGER currently ships as dd02-ov.json; keep "dd02" as the stable alias.
|
||||
CONFIG_ALIASES = {
|
||||
"dd02": "dd02-ov",
|
||||
}
|
||||
|
||||
|
||||
def _candidate_config_dirs() -> list[Path]:
|
||||
"""
|
||||
@@ -62,9 +68,16 @@ def load_config(config_name_or_path):
|
||||
if config_path.is_absolute():
|
||||
raise FileNotFoundError(f"Config file not found at absolute path: {config_path}")
|
||||
|
||||
direct = CONFIG_DIR / f"{config_name_or_path}.json"
|
||||
if direct.exists():
|
||||
return _load_json(direct)
|
||||
requested = str(config_name_or_path)
|
||||
candidates = [requested]
|
||||
alias = CONFIG_ALIASES.get(requested)
|
||||
if alias and alias not in candidates:
|
||||
candidates.append(alias)
|
||||
|
||||
for name in candidates:
|
||||
direct = CONFIG_DIR / f"{name}.json"
|
||||
if direct.exists():
|
||||
return _load_json(direct)
|
||||
|
||||
# Backward-compatible alias resolution: support old names via JSON "name" field.
|
||||
for candidate in _config_files():
|
||||
@@ -72,7 +85,7 @@ def load_config(config_name_or_path):
|
||||
data = _load_json(candidate)
|
||||
except Exception:
|
||||
continue
|
||||
if data.get("name") == config_name_or_path:
|
||||
if data.get("name") in candidates:
|
||||
return data
|
||||
|
||||
available = [f.stem for f in _config_files()]
|
||||
@@ -80,7 +93,7 @@ def load_config(config_name_or_path):
|
||||
raise FileNotFoundError(
|
||||
f"Input config file not found with name or path: {config_name_or_path}\n"
|
||||
f" Tried as file path: {config_path} (not found)\n"
|
||||
f" Tried as config file: {direct} (not found)\n"
|
||||
f" Tried as config files: {', '.join(candidates)} (not found)\n"
|
||||
f" Available config files: {available_str}"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user