Files
douyin-captcha/try/live_solve.py
T
杨豪 cae37465d2 feat: 尺度精修上线,首次机器 PASS(live_160548 s=1.0375)
- method_l_shape: OK 候选尺度 ±0.0375 精修(步长 0.0125 + ±6px 窗口),
  GT live_165204 Δ 179→176(真人 177),离线 9/10 保持
- 插桩 JSON.stringify 捕获 SDK 日志明文;指针 probe 排除 coalesced/pressure
- 拖动不跟手指标作废:post_pos 晚于 SDK 重置滑块,鼠标事件流始终完整
- 剩余 VerifyErr = 候选整体选错(视觉确证),方向:多候选质量评估
- docs: experiments/solution/retrospective 同步收尾
2026-09-15 16:20:05 +08:00

70 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""线上验证码求解(带服务端 tip_y 约束)。
服务端 captcha/get 响应中的 tip_y × 2 = 缺口 y 坐标(mark 画布顶)。
经 10 组线上样本校准:solver 无约束 y 与 2×tip_y 差 ≤4px(定位准的样本),
偏差大的样本(AMBIGUOUS)正是被服务端判 VerifyErr 的。
用 tip_y 约束 y 后在 score map 上重选 x,可显著提高准确率。
用法:
.venv/bin/python try/live_solve.py <big.jpeg> <mark.png> <tip_y>
输出 JSON: x(拖动目标, 原图px)、score、scale
"""
import json
import sys
from pathlib import Path
import cv2
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
import method_l_shape as L # noqa: E402
def live_solve(big_path, mark_path, tip_y, y_tol=8):
"""tip_y 约束版求解。返回 {x, y, scale, score} 或 None。"""
try:
target_y = int(tip_y) * 2
except (TypeError, ValueError):
return None
big = cv2.imread(big_path)
mark = cv2.imread(mark_path, cv2.IMREAD_UNCHANGED)
if big is None or mark is None:
return None
try:
d = L.setup(big, mark)
if d is None:
return None
best = []
for s in L.SCALES:
s = round(float(s), 3)
score, w, h, ptset = L.score_at_scale(d, s)
if score is None:
continue
for v, x, y in L.nms_take(score, 30, int(0.45 * max(w, h))):
if abs(y - target_y) <= y_tol:
best.append((v, s, x, y, w, h))
except (ValueError, IndexError, TypeError) as e:
print(f"匹配失败: {e}", file=sys.stderr)
return None
if not best:
# 放宽容差重试
return live_solve(big_path, mark_path, tip_y, y_tol=y_tol * 2)
best.sort(key=lambda c: c[0])
try:
v, s, x, y, w, h = best[0]
return {"x": x, "y": y, "scale": s, "score": round(float(v), 2), "w": w, "h": h}
except (IndexError, TypeError, ValueError) as e:
print(f"候选解析失败: {e}", file=sys.stderr)
return None
if __name__ == "__main__":
if len(sys.argv) < 4:
print(__doc__)
sys.exit(1)
try:
result = live_solve(sys.argv[1], sys.argv[2], int(sys.argv[3]))
except (OSError, ValueError) as e:
result = None
print(f"求解失败: {e}", file=sys.stderr)
print(json.dumps(result) if result else '{"ok": false}')