Files
douyin-captcha/docs/solution.md
T
杨豪 d15b3c5b43 feat: 拖动重叠验证码离线求解(方向感知倒角+alpha轮廓+旋转扫描)
- src/: 最终交付物(solve.py base64 API、method_l_shape.py 核心算法、verify_result.py 验证工具)
- docs/: 方案文档与实验演进记录
- try/: 历史实验脚本(A~K 方法)
- 10/10 样本求解成功,3 个独立真值锚点偏差 <=4px
2026-09-07 20:13:22 +08:00

5.6 KiB
Raw Blame History

拖动重叠验证码:最终方案

历史实验过程与方法淘汰记录见 experiments.md 实验原始日志在 try/findings.md

任务定义

给定「拖动重叠」验证码的背景大图(552×344)与 mark 小图(110×110 RGBA), 离线求出 mark 应拖动重叠的目标区域偏移。

背景中存在 ≥2 个与 mark 同形的区域(缩放/旋转生成的干扰项), 只有无缩放、无旋转(尺度≈1.0、旋转≈0°)的实例是目标。

核心发现(决定技术路线)

  1. mark 的 RGB 是"捐赠补丁",不是洞内内容。 决定性实验:真掩码 NCC 全图任意位置最高仅 0.56241ac/ 0.464444d)。 若"洞 = 该处照片变暗、mark = 洞下原图"成立,真目标处 NCC 应接近 1.0 (NCC 对仿射亮度变换不变)。因此一切基于 RGB/灰度的模板匹配在原理上不可行
  2. 洞是叠加在照片上的实心暗色几何覆盖物,边界 = mark 的 alpha 轮廓。 可靠信号只有三个:alpha 轮廓形状、尺度≈1.0、旋转≈0°
  3. 照片纹理会产生大量虚假贴合:纯倒角距离会被草地等密纹理区域击败。 必须叠加方向约束与洞内部特征。

最终算法(src/method_l_shape.py

粗扫:尺度 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 边缘 真洞 36,天空截断 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° 一致

APIsrc/solve.py

from solve import solve
result = solve(big_image_b64, mark_image_b64)   # 支持 dataURI 前缀

命令行:python src/solve.py <大图路径或base64> <mark路径或base64>

返回:

{
 "ok": true,
 "distance_px": 297,
 "x": 297, "y": 143,
 "scale": 1.0, "rot": 0.0,
 "score": 2.429,
 "confidence": "OK",
 "candidates": [...]
}
  • distance_pxmark 110×110 画布左上角应到达的 x 偏移; 若拖动 UI 的 mark 初始位置不在 x=0,用 x 减去初始 x。
  • confidenceOK(满足 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 4.04
41ac8781f7e9 (398,151) 1.000 4.40 右月牙;bbox 88×106 与 alpha 完全一致
444d0ea7c8bb (178,148) 1.000 4.50 轮廓分析交叉验证 ✓
4db94793ec6d (297,143) 1.000 2.43 SIFT 真值 ✓
632f84299d75 (113,136) 1.025 6.82 拟合最弱,置信度中
6a53f4529063 (166,152) 1.025 4.97
87276de26640 (153,134) 1.025 3.94 星形,靠旋转扫描解决
d6cd0ebb7eed (200,55) 1.000 5.82
d7a7f80c4554 (171,146) 1.025 3.11 SIFT 真值 ✓
f4fc5aa426ce (298,68) 1.050 4.79

已知边界

  • 632f 精修分 6.82 明显高于其余样本(2.4–5.8),建议保留人工复核, 或积累更多样本校准"可信分数"阈值;
  • 洞叠在深色照片上时"内-环亮度差"约束减弱,可提高方向倒角权重;
  • 若验证码改用亮色覆盖物,需要把亮度差项改为双向(|内-环|)并重标权重。

复现

.venv/bin/python src/method_l_shape.py    # 批量求解 + 可视化
.venv/bin/python src/verify_result.py     # 叠加轮廓验证图
.venv/bin/python src/solve.py <大图> <mark>  # 单样本 API