"""分歧裁决:对争议候选位置做细粒度尺度扫描 + 分通道 NCC。 用法:.venv/bin/python try/adjudicate.py 对 41ac 的 A2/C 两候选、444d 的共识候选、以及基线对 4db9/d7a7 做细化评估, 输出每候选的最优尺度、NCC 与分通道 NCC,判断真目标。 """ from pathlib import Path import sys import cv2 import numpy as np sys.path.insert(0, str(Path(__file__).resolve().parent)) from method_c_rotation import load_mark, template_at, patch_ncc # noqa: E402 ROOT = Path(__file__).resolve().parent.parent CAP = ROOT / "captchas" CASES = { # 12位前缀: [(标签, 中心, 初始尺度), ...] "41ac8781f7e9": [ ("C ", (218.0, 223.0), 0.40), ("A2 ", (110.0, 216.0), 0.50), ("A2#2", (363.0, 214.5), 0.50), ("A2#3", (270.0, 223.5), 0.50), ], "444d0ea7c8bb": [("共识", (232.5, 202.5), 1.10)], "4db94793ec6d": [("基线", (351.0, 197.0), 1.05)], "d7a7f80c4554": [("基线", (222.8, 201.4), 0.95)], } SCALES_FINE = np.arange(-0.12, 0.121, 0.02) def find_files(prefix): """按 12 位前缀 glob 完整文件名。""" bigs = [p for p in CAP.glob(f"{prefix}*.jpeg") if "-mark" not in p.stem] if not bigs: return None, None return bigs[0], CAP / f"{bigs[0].stem}-mark.png" def channel_ncc(big_f, center, tpl, mask): """分通道 NCC(模板与 patch 中心对齐)。失败返回 None。""" try: h, w = tpl.shape[:2] x0 = int(round(center[0] - w / 2.0)) y0 = int(round(center[1] - h / 2.0)) if x0 < 0 or y0 < 0 or x0 + w > big_f.shape[1] or y0 + h > big_f.shape[0]: return None patch = big_f[y0:y0 + h, x0:x0 + w] vals = [] for c in range(3): res = cv2.matchTemplate(patch[..., c:c + 1].copy(), tpl[..., c:c + 1].copy(), cv2.TM_CCOEFF_NORMED, mask=mask[..., None].copy()) v = float(np.nan_to_num(res[0, 0], nan=-1.0)) vals.append(v) return vals except cv2.error as e: print(f" 分通道失败: {e}") return None def refine(big_f, bgr, alpha, center, scale0): """细粒度尺度扫描,返回 (best_ncc, best_scale, 分通道NCC)。""" try: best_ncc = -1.0 best_scale = scale0 best_ch = None for ds in SCALES_FINE: s = float(scale0 + ds) tpl, mask, _, _ = template_at(bgr, alpha, s, 0.0) if tpl is None: continue ncc = patch_ncc(big_f, center[0], center[1], tpl, mask) if ncc > best_ncc: ch = channel_ncc(big_f, center, tpl, mask) best_ncc = ncc best_scale = s best_ch = ch return best_ncc, best_scale, best_ch except Exception as e: print(f" 细化失败: {e}") return (-1.0, scale0, None) def process_case(prefix, cands): try: big_path, mark_path = find_files(prefix) if big_path is None: print(f"{prefix} 样本缺失") return big = cv2.imread(str(big_path), cv2.IMREAD_COLOR) if big is None: print(f"{prefix} 大图读取失败") return big_f = big.astype(np.float32) bgr, alpha = load_mark(mark_path) if bgr is None or alpha is None: print(f"{prefix} mark 读取失败") return print(f"== {prefix}") for label, center, scale0 in cands: ncc, s, ch = refine(big_f, bgr, alpha, center, scale0) chs = f"BGR={ch[0]:.2f}/{ch[1]:.2f}/{ch[2]:.2f}" if ch else "N/A" print(f" {label} center=({center[0]:.0f},{center[1]:.0f}) " f"scale*={s:.2f} ncc={ncc:.3f} {chs}") except Exception as e: print(f"{prefix} 处理失败: {e}") def main(): for prefix, cands in CASES.items(): process_case(prefix, cands) if __name__ == "__main__": main()