# 拖动重叠验证码:最终方案 > 历史实验过程与方法淘汰记录见 [experiments.md](experiments.md); > 实验原始日志在 `try/findings.md`。 ## 任务定义 给定「拖动重叠」验证码的背景大图(552×344)与 mark 小图(110×110 RGBA), 离线求出 mark 应拖动重叠的目标区域偏移。 背景中存在 ≥2 个与 mark 同形的区域(缩放/旋转生成的干扰项), 只有**无缩放、无旋转**(尺度≈1.0、旋转≈0°)的实例是目标。 ## 核心发现(决定技术路线) 1. **mark 的 RGB 是"捐赠补丁",不是洞内内容。** 决定性实验:真掩码 NCC 全图任意位置最高仅 0.562(41ac)/ 0.464(444d)。 若"洞 = 该处照片变暗、mark = 洞下原图"成立,真目标处 NCC 应接近 1.0 (NCC 对仿射亮度变换不变)。因此**一切基于 RGB/灰度的模板匹配在原理上不可行**。 2. **洞是叠加在照片上的实心暗色几何覆盖物**,边界 = mark 的 alpha 轮廓。 可靠信号只有三个:**alpha 轮廓形状、尺度≈1.0、旋转≈0°**。 3. **照片纹理会产生大量虚假贴合**:纯倒角距离会被草地等密纹理区域击败。 必须叠加方向约束与洞内部特征。 ## 最终算法(`src/method_l_shape.py`) ```text 粗扫:尺度 0.85–1.15(步长 0.025)× 全图位置 评分 = 方向感知倒角DT + 20×内部边缘密度 + 0.5×max(0, 内-环亮度差) ↓ 全图 NMS 取 top-5 候选 ↓ 每候选局部旋转扫描:36 角(步长 10°)× ±3px 平移 → rot*(近并列 ±0.15 内取离 0 最近者,兼容星形等旋转对称形状) → rot=0 拟合分 refined ↓ 选择:|rot*|≤12° 且 0.96≤s≤1.05 的候选中 refined 最低者 否则取 |rot*| 最小并标 AMBIGUOUS ``` 三个评分项的物理含义: | 项 | 含义 | 区分度(实测) | | --- | --- | --- | | 方向感知倒角DT | 边界点只匹配同法向桶(9 桶×20°)的 Canny 边缘 | 真洞 3–6,天空截断 8+ | | 内部边缘密度 | 洞内部实心平坦(≈0),纹理区内部边缘多 | 真洞 ≤0.1,草地 0.36 | | 内-环亮度差 | 洞比周围暗(半透明暗色覆盖) | 真洞 -40~-104,误检区 +6 | Canny 阈值固定 `lo = max(40, 0.5×中位灰度)`、`hi = 3×lo`。 阈值过低会让 JPEG 噪声在平坦区产生大量假边缘,污染"内部平坦"特征 (曾导致 4db9 星形样本漏检)。 ## 判别力证据 所有样本的候选旋转曲线均能区分目标/干扰: - 目标:`rot*≈0°` 且 rot=0 拟合分明显低于其他角度; - 干扰项:`rot*≠0°`(如 41ac 左月牙 -10°、8727 竞争星 +20°、f4fc 竞品 -20°)。 三个独立真值锚点全部命中: | 样本 | 独立真值来源 | 真值 | 本方案 | 偏差 | | --- | --- | --- | --- | --- | | 4db94793 | SIFT + RANSAC | (295.8, 142.1) | (297, 143) | ~2px | | d7a7f80c | SIFT + RANSAC | (167.8, 146.4) | (171, 146) | ~4px | | 444d0ea7 | 闭合轮廓分析(Hu≈0.003) | 左洞 rot≈0° | (183, 160) rot=0° | 一致 | ## API(`src/solve.py`) ```python from solve import solve result = solve(big_image_b64, mark_image_b64) # 支持 dataURI 前缀 ``` 命令行:`python src/solve.py <大图路径或base64> ` 返回: ```json { "ok": true, "distance_px": 297, "x": 297, "y": 143, "scale": 1.0, "rot": 0.0, "score": 2.429, "confidence": "OK", "candidates": [...] } ``` - `distance_px`:mark 110×110 画布左上角应到达的 x 偏移; 若拖动 UI 的 mark 初始位置不在 x=0,用 `x` 减去初始 x。 - `confidence`:`OK`(满足 rot/尺度门限)或 `AMBIGUOUS`(降级选择,建议人工复核)。 - mark 图必须含 alpha 通道(PNG),否则返回 `ok=false`。 ## 结果验证方法 1. **叠加轮廓检查(最直接)**:`python src/verify_result.py` 生成 `src/out/L/verify/`,把 alpha 轮廓按 offset 以绿色半透明+黄色描边叠回大图。 轮廓与暗色图形边缘完全重合 = 正确。 2. **候选对比**:看 `src/out/L/<前缀>-match.png`,红框目标应同形同大同向, 其余框应为旋转/缩放干扰项或被淘汰纹理。 3. **数值检查**:`summary.txt` 中目标 rot≈0、scale∈[0.96,1.05]、精修分最低。 ## 10 样本最终结果 | 样本 | offset | 尺度 | rot* | 精修分 | 备注 | | --- | --- | --- | --- | --- | --- | | 2a313202f58c | (370,114) | 1.025 | 0° | 4.04 | | | 41ac8781f7e9 | (398,151) | 1.000 | 0° | 4.40 | 右月牙;bbox 88×106 与 alpha 完全一致 | | 444d0ea7c8bb | (178,148) | 1.000 | 0° | 4.50 | 轮廓分析交叉验证 ✓ | | 4db94793ec6d | (297,143) | 1.000 | 0° | 2.43 | SIFT 真值 ✓ | | 632f84299d75 | (113,136) | 1.025 | 0° | 6.82 | 拟合最弱,置信度中 | | 6a53f4529063 | (166,152) | 1.025 | 0° | 4.97 | | | 87276de26640 | (153,134) | 1.025 | 0° | 3.94 | 星形,靠旋转扫描解决 | | d6cd0ebb7eed | (200,55) | 1.000 | 0° | 5.82 | | | d7a7f80c4554 | (171,146) | 1.025 | 0° | 3.11 | SIFT 真值 ✓ | | f4fc5aa426ce | (298,68) | 1.050 | 0° | 4.79 | | ## 已知边界 - `632f` 精修分 6.82 明显高于其余样本(2.4–5.8),建议保留人工复核, 或积累更多样本校准"可信分数"阈值; - 洞叠在深色照片上时"内-环亮度差"约束减弱,可提高方向倒角权重; - 若验证码改用亮色覆盖物,需要把亮度差项改为双向(|内-环|)并重标权重。 ## 复现 ```bash .venv/bin/python src/method_l_shape.py # 批量求解 + 可视化 .venv/bin/python src/verify_result.py # 叠加轮廓验证图 .venv/bin/python src/solve.py <大图> # 单样本 API ```