6.5 KiB
6.5 KiB
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 四步)
- 形状一致性:左右图形状不一致 → 整帧拒收输出空 + WARN 节流 (§2.2 第 2 条:禁止左右各自缩放/填充);
- 左右推理:
parallel(默认,ThreadPoolExecutor(2),§5 硬性要求)或sequential(基准对照);单目异常被隔离,该目输出空帧列表(§5 失败处理); - 出口自检 + 白名单:丢弃非有限值 /
x2<=x1/y2<=y1的框 (§2.2 第 6 条);class_whitelist非空时过滤类别; - 封装:每目一个
DetectionFrame(frame_id, detections),frame_id左右共用(§2.3 同帧契约)。
实现决策(v1.0 冻结)
- ultralytics 直跑
.pt(项目拍板口径):letterbox、输出解码、NMS、 框映射回原生坐标系全部由库内完成;识别端只负责阈值外置、出口自检与 契约封装。 - 双实例池:ultralytics 单实例非线程安全,左右并发各持一份模型 (§4 并发策略的"双实例"形态)。
- 无跨帧状态:前端不持有任何历史(无追踪、无平滑、无 Coast)—— 结构性满足"只送本帧原生实测框"(§2.5 第 1 条)。
- 形状不一致不 raise:按"拒收 + 空帧 + 节流 WARN"处理,与 stereo_ranger 非同帧拒收同哲学(不阻塞整体)。
- 几何参数单一来源:
stereo_yolo.json不存内参/原分辨率(输出框天然在 输入图坐标系),F/B/cx/cy 唯一来源是utils/config/stereo_ranger.json, 避免两处漂移(§4 配置一致性)。 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 |
测试与演示
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 | 由库内完成,无法构件级单测;以合成对整体校验( |
| 垂直对齐 P95 ≤1 px | 合成对实测 |
| 类别一致性 | 模型单类 Drone,合成对天然一致;待真实素材 |
| 输出合法性 | ✓ 单测(非有限值/退化框构造用例) |
| 输入纯净性 | ✓ 结构性保证(前端无跨帧状态) |
| 下游对接 | ✓ demo 场景②;δZ ∝ Z² 趋势由测距端单测覆盖 |
| 帧周期 | △ 台架开发机实测 ≈34 ms/帧(30 fps 预算 33.3 ms);待平台实测(P7) |