- src/: 最终交付物(solve.py base64 API、method_l_shape.py 核心算法、verify_result.py 验证工具) - docs/: 方案文档与实验演进记录 - try/: 历史实验脚本(A~K 方法) - 10/10 样本求解成功,3 个独立真值锚点偏差 <=4px
86 lines
4.5 KiB
Markdown
86 lines
4.5 KiB
Markdown
# 实验演进记录(A → L)
|
||
|
||
> 最终方案与使用说明见 [solution.md](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(后来用于决定性实验)。
|
||
|
||
## 转折点实验(值得复用的诊断手段)
|
||
|
||
1. **掩码 NCC 全图扫描**(手写 filter2D 实现,无 inf 问题):
|
||
对 41ac/444d 在尺度 0.9–1.1 全图扫描,最高分仅 0.56/0.46
|
||
→ 证明 mark RGB 与洞内容无像素关系,RGB 路线全部判死。
|
||
2. **闭合轮廓 + matchShapes**(444d 上):
|
||
Canny 闭合轮廓直接命中两个洞,Hu≈0.003,左洞 rot=-0.6°、右洞 -15.5°
|
||
→ 证明"轮廓 + 尺度 + 旋转"路线成立,且给出 444d 独立真值。
|
||
3. **三特征分离度测量**:
|
||
洞:内部边缘密度 ≤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 时的交叉验证方法
|
||
|
||
1. 找至少一个可用独立方法的样本建立锚点(如 SIFT 能用的纹理丰富样本);
|
||
2. 用结构性证据补充锚点:候选 bbox 尺寸与 mark alpha bbox 完全一致
|
||
(41ac:88×106 vs 88×106);
|
||
3. 全部候选可视化后人工目检:真目标应落在几何图形上而非纹理上;
|
||
4. 结果叠加轮廓回贴大图(`src/verify_result.py`),观察贴合精度。
|