- src/: 最终交付物(solve.py base64 API、method_l_shape.py 核心算法、verify_result.py 验证工具) - docs/: 方案文档与实验演进记录 - try/: 历史实验脚本(A~K 方法) - 10/10 样本求解成功,3 个独立真值锚点偏差 <=4px
4.5 KiB
4.5 KiB
实验演进记录(A → L)
最终方案与使用说明见 solution.md。 本文记录每个方法的思路、失败原因与沉淀的经验教训, 原始实验脚本在
try/,输出在try/out/<方法字母>/。
方法总览
| 方法 | 脚本 | 思路 | 结论 |
|---|---|---|---|
| A | method_a_template.py | 蒙版 CCORR 多尺度模板匹配 | 废弃 |
| A2 | method_a2_ccoeff.py | 蒙版 CCOEFF + 峰显著度 + NMS | 基线可用,但锁纹理 |
| B | method_b_sift.py | SIFT + RANSAC 迭代聚类 | 仅 2 样本可用,留作真值锚点 |
| C | method_c_rotation.py | NCC 粗扫 + 旋转响应二次评分 | 第一阶段候选即错 |
| D | method_d_shape.py | Chamfer 轮廓距离 | 小尺度纹理饱和 |
| E | method_e_shape_coverage.py | 轮廓边缘覆盖率 | 同上 |
| F | method_f_contour.py | Canny 轮廓 + matchShapes | 背景轮廓合并成超大块 |
| G | method_g_component.py | 固定暗度阈值连通域 | 阈值敏感 |
| H | method_h_silhouette.py | alpha 轮廓距离 + 局部对比度 | 分数归一化饱和 |
| I | method_i_adaptive_components.py | 自适应暗目标连通域 | 误检照片内容 |
| J | method_j_objects.py | 低饱和度暗色物体分割 | 同上 |
| K | method_k_objectness.py | 暗色覆盖物概率图 + 掩码 CCORR | 掩码 CCORR 边界产生 inf |
| L | src/method_l_shape.py |
方向感知倒角 + 洞内部特征 + 旋转扫描 | 最终方案,10/10 |
逐方法教训
A / A2:RGB 模板匹配(不减小均值的代价)
TM_CCORR_NORMED不做均值中心化,亮背景上处处高分;- 跨尺度直接比原始分数偏向小模板 → A2 改用峰显著度
(max-median)/std; - A2 在 8/10 样本上候选位置合理,但 41ac 出现多峰分歧—— 后来证明这些 0.7 的高分全是小尺度纹理巧合,方向本身是错的。
B:SIFT(意外价值)
- 平面图形缺少纹理,大部分样本匹配不足;
- 但在纹理丰富的 4db9、d7a7 上给出亚像素级真值 (scale≈1.00、rot≈0°),成为后续所有方法的交叉验证锚点。
C:旋转感知 NCC(两次评分救不了错误的候选)
- 旋转响应曲线本身有效(真目标 rot≈0),但候选来自 NCC 粗扫, 粗扫错了旋转阶段无法挽回。
D/E:倒角与覆盖率(尺度公平性问题)
- 覆盖率在小尺度上饱和到 1.0(背景纹理到处有边);
- 倒角均值在密纹理区反而更低 → 必须限制尺度范围并加方向约束。
F/G/H/I/J/K:分割路线的共同困境
- Canny 轮廓易把地面/山脉合并成超大块(F);
- 固定/自适应暗度阈值对每张照片都不同(G/I/J);
- K 的教训:
TM_CCORR_NORMED带 mask 在边界产生 inf 值, 需要手写 filter2D 掩码 NCC(后来用于决定性实验)。
转折点实验(值得复用的诊断手段)
- 掩码 NCC 全图扫描(手写 filter2D 实现,无 inf 问题): 对 41ac/444d 在尺度 0.9–1.1 全图扫描,最高分仅 0.56/0.46 → 证明 mark RGB 与洞内容无像素关系,RGB 路线全部判死。
- 闭合轮廓 + matchShapes(444d 上): Canny 闭合轮廓直接命中两个洞,Hu≈0.003,左洞 rot=-0.6°、右洞 -15.5° → 证明"轮廓 + 尺度 + 旋转"路线成立,且给出 444d 独立真值。
- 三特征分离度测量: 洞:内部边缘密度 ≤0.1、亮度差 -40~-104; 草地误检:密度 0.36、亮度差 +6 → 直接给出 L 的特征权重依据。
调试中踩过的具体坑
np.flatnonzero对 2D 数组返回一维扁平索引,取坐标应用np.argwhere;filter2D输出是全尺寸,与手写滑窗比对时要裁剪有效相关区[h//2 : h//2+H-h+1, w//2 : w//2+W-w+1];- 方向桶不能越界重叠:角度已在 [0,180),9 桶直接线性划分即可, 最后一桶再做 wrap 会导致点被双计(表现为均值超上限);
np.arange有浮点累积误差,参与区间比较前先round(s, 3);- Canny 阈值过低(如 26/77)会让 JPEG 噪声淹没"内部平坦"特征。
无 ground truth 时的交叉验证方法
- 找至少一个可用独立方法的样本建立锚点(如 SIFT 能用的纹理丰富样本);
- 用结构性证据补充锚点:候选 bbox 尺寸与 mark alpha bbox 完全一致 (41ac:88×106 vs 88×106);
- 全部候选可视化后人工目检:真目标应落在几何图形上而非纹理上;
- 结果叠加轮廓回贴大图(
src/verify_result.py),观察贴合精度。