- src/: 最终交付物(solve.py base64 API、method_l_shape.py 核心算法、verify_result.py 验证工具) - docs/: 方案文档与实验演进记录 - try/: 历史实验脚本(A~K 方法) - 10/10 样本求解成功,3 个独立真值锚点偏差 <=4px
117 lines
3.9 KiB
Python
117 lines
3.9 KiB
Python
"""分歧裁决:对争议候选位置做细粒度尺度扫描 + 分通道 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()
|