185 lines
7.9 KiB
Markdown
185 lines
7.9 KiB
Markdown
1. 系统目标
|
||
# 检测靶纸四角的等腰直角三角形标记(每个角一个)
|
||
# 计算激光落点在靶面上的二维偏移(厘米)
|
||
# 通过PnP算法估算靶面到相机的距离(米)
|
||
|
||
2. 核心算法流程
|
||
2.1 三角形检测 (detect_triangle_markers)
|
||
采用多策略级联保证鲁棒性:
|
||
图像输入 → 多阈值策略 → 候选三角形过滤 → 四点匹配
|
||
检测策略(按优先级):
|
||
|
||
1.全局Otsu二值化(最快,~10ms)
|
||
2.自适应阈值(多种block size,光照不均时)
|
||
3.ROI局部阈值(候选不足3个时,分象限独立处理)
|
||
4.Black-Hat形态学增强(仍不足时,突出暗色标记)
|
||
|
||
三角形几何验证:
|
||
# 必须是直角三角形(检查勾股定理,容差20%)
|
||
# 两直角边长度差<20%
|
||
# 内部像素足够暗(灰度≤130,暗像素比例≥30%)
|
||
# 与周围背景对比度≥15灰度级
|
||
|
||
四点匹配算法:
|
||
# 从候选三角形中枚举所有4点组合
|
||
# 计算四边形评分:(对角比-1)*3 + (水平比-1) + (垂直比-1) + (边长偏差)*2
|
||
# 选择评分最低的组合作为四角标记
|
||
|
||
2.2 单应性落点计算 (homography_calibration)
|
||
建立图像坐标系 → 靶面坐标系(二维平面)的透视变换
|
||
|
||
将激光点像素坐标映射到靶面坐标(厘米)
|
||
|
||
使用RANSAC提高鲁棒性(阈值1像素)
|
||
|
||
2.3 PnP距离估计 (pnp_distance_meters)
|
||
已知四个标记点的三维坐标(x,y,z,单位cm)
|
||
|
||
通过solvePnP求解相机外参(旋转+平移)
|
||
|
||
距离 = ‖平移向量‖ / 100(转换为米)
|
||
|
||
3. 关键优化策略
|
||
3.1 多路径投票
|
||
同一图像区域被不同二值化方法检测到时,path_votes++
|
||
|
||
选择投票数高的候选,提高检测可信度
|
||
|
||
3.2 早退机制
|
||
候选≥3个 且 覆盖3个以上象限 → 停止更多阈值尝试
|
||
|
||
大幅降低嵌入式设备计算开销
|
||
|
||
3.3 3点补全机制
|
||
当只检测到3个角时,通过仿射变换估算第4个角位置
|
||
|
||
公式:P_missing = M_inv @ [x_target, y_target, 1]
|
||
|
||
3.4 图像缩放
|
||
默认缩放到0.5倍进行检测(由config控制)
|
||
|
||
坐标还原时乘以inv_scale,保持与标定矩阵一致
|
||
|
||
4. 数据流示例
|
||
python
|
||
输入:
|
||
- img_rgb: H×W×3 图像
|
||
- laser_xy: (x_px, y_px) 激光点像素坐标
|
||
- marker_positions: {0:[0,0,0], 1:[0,30,0], 2:[30,30,0], 3:[30,0,0]} # 4角3D坐标(cm)
|
||
|
||
输出:
|
||
{
|
||
"ok": True,
|
||
"dx_cm": 2.5, # 靶面X偏移(cm,向右为正)
|
||
"dy_cm": -3.2, # 靶面Y偏移(cm,向上为正)
|
||
"distance_m": 5.43, # 相机到靶面距离(米)
|
||
"offset_method": "triangle_homography",
|
||
"distance_method": "pnp_triangle"
|
||
}
|
||
5. 鲁棒性设计
|
||
5.1 参数自适应
|
||
从config.py动态读取所有阈值(可在线调整)
|
||
|
||
三角形边长范围、灰度阈值、对比度要求等均可配置
|
||
|
||
5.2 异常处理
|
||
角点退化检测(距离<3像素判定为重复)
|
||
|
||
NaN/Inf校验(单应性矩阵、偏移量、距离)
|
||
|
||
距离合理性检查(0.3~20米)
|
||
|
||
5.3 降级策略
|
||
PnP失败 → 只输出偏移,距离置None
|
||
|
||
4角检测失败 → 尝试3角补全
|
||
|
||
快速路径失败 → CLAHE增强兜底(可选)
|
||
|
||
6. 性能特点
|
||
CPU友好:默认Otsu单次处理,多数场景10-30ms完成检测
|
||
|
||
内存可控:最大候选数截断(默认10个),避免组合爆炸
|
||
|
||
嵌入式适配:支持图像缩放、早退机制降低计算量
|
||
|
||
7. 局限性
|
||
依赖四个等腰直角三角形(需靶纸特殊设计)
|
||
|
||
要求三角形内部足够暗、与背景有对比度
|
||
|
||
单应性假设靶面为平面(实际靶纸可能有轻微起伏)
|
||
|
||
这套算法在射击训练系统中作为主要定位手段。
|
||
|
||
8. 为了加速单应性的计算,引入了yolo模型,一共做了两个模型,一个为靶纸和黑色三角形一体的识别模型,用于做原照片上快速找到靶纸区域。另一个模型是黑色三角形的模型,用于做靶纸区域再找黑色三角形。但是经过对比发现,引入黑色三角形模型反而更慢。入下面的流程A和流程B:
|
||
yolo靶纸+传统(流程B) yolo靶纸+yolo黑色三角形(流程A)
|
||
平均值 646.08 916.4457143
|
||
标准差 94.61300968 57.40401849
|
||
|
||
公共前置(两条路都一样)
|
||
是否用靶环模型裁 Stage1
|
||
|
||
TRIANGLE_YOLO_ROI_ENABLE=True 时:跑 靶环 YOLO,得到全图上的 roi_xyxy,后面的三角形都在 img_work = 全图[roi] 上做(必要时再缩成 img_det 给整图传统分支用)。
|
||
False 时:roi_xyxy=None,三角形在 整幅相机图 上当 img_work。
|
||
之后都进入 try_triangle_scoring(img_cv, …, roi_xyxy=…, black_yolo_boxes_work=…)
|
||
|
||
在里面先做灰度、v_suppress、锐化、det_scale 缩略图等 prep(与是否黑三角模型无关)。
|
||
差别从 black_yolo_boxes_work 有没有有效子框列表 开始。
|
||
|
||
流程 A:用黑色三角形模型(Stage2 黑三角 YOLO)
|
||
配置要点:TRIANGLE_BLACK_YOLO_ENABLE=True,且 TRIANGLE_BLACK_TRIANGLE_LOCATE_MODE="yolo",并且 已有 Stage1 裁切(roi_xyxy 不能为 None,否则根本不会跑黑三角 YOLO)。
|
||
|
||
步骤概要:
|
||
|
||
try_black_triangle_boxes_work
|
||
|
||
输入:全图 RGB + Stage1 的 ring_roi_xyxy。
|
||
在 Stage1 裁切图(与训练一致的 slab)上跑 黑三角 YOLO,得到若干个 子框(black_boxes_work,坐标在 裁切图/work 系)。
|
||
try_triangle_scoring 内
|
||
|
||
若 black_yolo_boxes_work 非空:
|
||
按配置在 Stage1 全分辨率灰度(或缩略灰度,视 det_scale / TRIANGLE_BLACK_YOLO_PATCH_GRAY_SOURCE)上,对每个子框裁 patch,跑 _extract_triangle_from_yolo_patch(子框内:Otsu → 失败再单次 Adaptive + 轮廓 + 形状/颜色)。
|
||
median_leg 过滤,再 四点分配 ID。
|
||
若 ≥3 个(通常 4 个)有效:认为 Stage2 成功,跳过 整幅 Stage1 上的 detect_triangle_markers。
|
||
若 不足 3 个 且未关 fallback:在 缩略后的整幅 work 灰度上再走 detect_triangle_markers(整图 Otsu + 整图 Adaptive×block_sizes + 各类 fallback),与「不用黑三角模型时的传统主路径」同类。
|
||
后续
|
||
|
||
角点从 det 坐标 ×inv_scale 回到 work,再 +roi 原点 回到全图;单应性、补第 4 点、PnP 等与另一条路相同。
|
||
耗时上多出来的部分:黑三角 YOLO 推理 + 每个子框一遍传统小流水线(成功时通常 不再付整图 detect_triangle_markers)。
|
||
|
||
流程 B:不用黑色三角形模型(纯传统定位三角)
|
||
典型配置(任一即可达到「不用黑三角模型」的效果):
|
||
|
||
TRIANGLE_BLACK_YOLO_ENABLE=False,或
|
||
TRIANGLE_BLACK_TRIANGLE_LOCATE_MODE="traditional"(即使模型开关开着也不跑黑三角 YOLO),或
|
||
没有 Stage1 ROI(roi_xyxy is None)时,当前逻辑下 也不会跑 Stage2 黑三角 YOLO。
|
||
此时 black_yolo_boxes_work=None(或不等价于「有子框」)。
|
||
|
||
步骤概要:
|
||
|
||
try_triangle_scoring 内
|
||
不跑 子框 _extract_triangle_from_yolo_patch。
|
||
直接在 img_det(缩略后的 work) 上调用 detect_triangle_markers:
|
||
全局 Otsu(若 TRIANGLE_SKIP_GLOBAL_OTSU_EXTRACT_ON_YOLO_ROI 在有 ROI 时可能 不算 Otsu 轮廓,但仍会生成 Otsu 图供后续用);
|
||
可选 象限 ROI(TRIANGLE_ROI_ENABLED);
|
||
整图 Adaptive(TRIANGLE_ADAPTIVE_BLOCK_SIZES,例如 (11,));
|
||
不足再走 放宽 approxPolyDP、BlackHat 等。
|
||
后面同样是过滤、四点组合/象限分配、单应性、PnP 等。
|
||
特点:没有黑三角 NPU 时间,也 没有「按框重复 4 次子框传统」;但要在 一整张(缩略)ROI 图 上跑一套更重的 整图 pipeline。
|
||
|
||
对照一句话
|
||
用黑三角 YOLO(流程 A) 不用黑三角 YOLO(流程 B)
|
||
Stage2
|
||
黑三角模型给子框 → 子框内 Otsu + 至多一次 Adaptive
|
||
无 Stage2 模型
|
||
三角角点从哪来
|
||
优先 子框传统;不够再 整图 detect_triangle_markers
|
||
只有 整图 detect_triangle_markers
|
||
和「全图是否只做 Adaptive」
|
||
子框 不是只做 Adaptive;整图回退时也与全图路径一致(先 Otsu 等)
|
||
整图路径 也不是只做 Adaptive
|
||
靶环 YOLO(Stage1 裁切)在 A/B 里都可以开或关,与「黑三角模型」是独立开关。
|
||
|
||
|