Files
code-test-drone/utils/stereo_yolo/README.md
T

110 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# stereo_yolo(Python 版)
双目 YOLO 检测前端:在左右目图像上**独立运行** YOLO 检测器,输出符合
`stereo_ranger` 输入契约的检测框(每目一个 `DetectionFrame`、共用 frame_id)。
不做测距、左右目关联与立体校正。
规格:`docs/双目YOLO目标识别需求说明.md`(接口契约以其为准;几何语义权威
`docs/双目测距技术路线.md`)。
⚠ 全部参数为占位值、demo 素材为 AI 合成图:标定与地面真值验证完成前,
仅限台架/回放验证,不得接入实飞控制回路(规格 §7 红线同源)。
## 接口清单
| 名称 | 说明 |
| --- | --- |
| `StereoYoloFrontend(config, detector=None)` | 主入口;`detector` 可注入(接口 `detect(img) -> list[Detection]`,单测用假检测器) |
| `.detect(left_img, right_img, frame_id) -> (DetectionFrame, DetectionFrame)` | 单帧检测;BGR ndarray(cv2 约定),输出框位于输入图原生坐标系 |
| `.close()` | 释放并发线程池 |
| `.config` | 只读配置访问 |
| `StereoYoloConfig` / `load_from_json(path)` | 7 项配置;缺项/未知字段/类型错误/非法取值一律 `ValueError`(fail-fast) |
| `UltralyticsDetector` | ultralytics YOLO `.pt` 后端(双实例池;`ultralytics` 延迟导入) |
| `split_sbs(image) -> (left, right)` | side-by-side 单流裁切(宽须为偶数) |
| `Detection` / `DetectionFrame` | 直接复用 `stereo_ranger.types`(纯值语义,§2.1 推荐) |
| `WarnThrottle` | 告警节流(第 1 次 + 每满 100 次,带累计计数) |
`logger` 名称为 `stereo_yolo`,走标准 `logging`。
## 单帧流程(`detect` 四步)
1. **形状一致性**:左右图形状不一致 → 整帧拒收输出空 + WARN 节流
(§2.2 第 2 条:禁止左右各自缩放/填充);
2. **左右推理**:`parallel`(默认,ThreadPoolExecutor(2),§5 硬性要求)或
`sequential`(基准对照);单目异常被隔离,该目输出空帧列表(§5 失败处理);
3. **出口自检 + 白名单**:丢弃非有限值 / `x2<=x1` / `y2<=y1` 的框
(§2.2 第 6 条);`class_whitelist` 非空时过滤类别;
4. **封装**:每目一个 `DetectionFrame(frame_id, detections)`,`frame_id`
左右共用(§2.3 同帧契约)。
## 实现决策(v1.0 冻结)
1. **ultralytics 直跑 `.pt`**(项目拍板口径):letterbox、输出解码、NMS、
框映射回原生坐标系全部由库内完成;识别端只负责阈值外置、出口自检与
契约封装。
2. **双实例池**:ultralytics 单实例非线程安全,左右并发各持一份模型
(§4 并发策略的"双实例"形态)。
3. **无跨帧状态**:前端不持有任何历史(无追踪、无平滑、无 Coast)——
结构性满足"只送本帧原生实测框"(§2.5 第 1 条)。
4. **形状不一致不 raise**:按"拒收 + 空帧 + 节流 WARN"处理,与 stereo_ranger
非同帧拒收同哲学(不阻塞整体)。
5. **几何参数单一来源**:`stereo_yolo.json` 不存内参/原分辨率(输出框天然在
输入图坐标系),F/B/cx/cy 唯一来源是 `utils/config/stereo_ranger.json`,
避免两处漂移(§4 配置一致性)。
6. **`track_id` 留 `None`**:外部追踪器注入属链路后段(§2.1;v1.1 可选)。
## 移植注记(C++ 侧需自行复现的库内行为)
本次为 ultralytics 直跑口径,以下行为在库内完成、C++ 实现必须逐项复现
(不一致会在视差取点上引入系统性偏差):
| 库内行为 | C++ 需复现要点 |
| --- | --- |
| letterbox | 等比缩放到 `imgsz`、灰边填充(ultralytics 预测默认填充值 114);左右同一套参数(形状一致时天然满足) |
| 输出解码 | YOLO26 检测头输出布局与解码(建议导出一份 ONNX 作为参照物;本仓环境:ultralytics 8.4 / torch 2.11 / onnx 1.18) |
| NMS | `conf` 先过滤、NMS 用配置 IoU 门限;`max_det` 默认 300 |
| 坐标回映 | letterbox 逆变换(scale/pad)到原生分辨率;输出 `x1,y1,x2,y2` 浮点 |
| 并发 | 双实例双线程(单实例并发调用不安全) |
## 配置项(全部外置,占位值)
| 键 | 占位值 | 说明 |
| --- | --- | --- |
| `model_path` | `models/yolo26n-drone.pt` | 相对路径按启动工作目录解析(demo 内按仓库根兜底) |
| `imgsz` | 640 | 须为 32 的整数倍(YOLO 步长约束) |
| `conf_threshold` | 0.25 | 识别端自身过滤口径(测距端不消费检测置信度,§2.4) |
| `iou_threshold` | 0.7 | NMS IoU 门限 |
| `class_whitelist` | `null` | 可选白名单;同一目标左右目类别必须一致(§2.2 第 5 条) |
| `device` | `null` | null = 自动选择(`cpu` / `cuda:0` / …) |
| `concurrency` | `parallel` | `parallel` / `sequential` |
## 测试与演示
```bash
cd utils
"D:/program file/conda/envs/pytorch/python.exe" -m pytest tests/stereo_yolo -q # 36 条
python utils/demo_stereo_yolo.py # 真模型 + 占位素材
```
单测全部用注入的假检测器驱动(无模型、无素材、无硬件):frame_id 左右共用、
出口自检丢弃退化框、白名单过滤、单目失败隔离、形状不一致拒收、双人屏障
断言真并发(串行实现会屏障超时)。
demo(真模型 + AI 合成素材 `utils/assets/stereo_yolo/drone_sky.png`):
① 单图冒烟 → ② 合成双目对(同一 patch 羽化粘贴、已知视差 12 px,实测
|ΔD|≈0.3 px、|Δy|≈0.2 px)→ 真检测框喂 `StereoRanger.update()`,记录值与
解析值 `Z=F·B/D` 同量级 → ③ 空场景(正常空帧不告警)→ ④ 形状不一致拒收
→ ⑤ 帧周期实测(台架开发机 ≈34 ms/帧,占位)。替换素材文件即可换真实图片。
## 验收对照(规格 §6)
| 项 | 状态 |
| --- | --- |
| 坐标回映误差 <0.5 px | 由库内完成,无法构件级单测;以合成对整体校验(|ΔD|≈0.3 px @1080p,含检测边缘噪声);C++ 移植后需实测 |
| 垂直对齐 P95 ≤1 px | 合成对实测 |Δy|≈0.2 px(合成口径,非真实校正质量);待真实素材 |
| 类别一致性 | 模型单类 `Drone`,合成对天然一致;待真实素材 |
| 输出合法性 | ✓ 单测(非有限值/退化框构造用例) |
| 输入纯净性 | ✓ 结构性保证(前端无跨帧状态) |
| 下游对接 | ✓ demo 场景②;δZ ∝ Z² 趋势由测距端单测覆盖 |
| 帧周期 | △ 台架开发机实测 ≈34 ms/帧(30 fps 预算 33.3 ms);待平台实测(P7) |