- src/: 最终交付物(solve.py base64 API、method_l_shape.py 核心算法、verify_result.py 验证工具) - docs/: 方案文档与实验演进记录 - try/: 历史实验脚本(A~K 方法) - 10/10 样本求解成功,3 个独立真值锚点偏差 <=4px
73 lines
2.7 KiB
Python
73 lines
2.7 KiB
Python
"""方法G:暗色图形的连通域 + alpha 轮廓形状匹配实验。"""
|
|
from pathlib import Path
|
|
import cv2
|
|
import numpy as np
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
CAP = ROOT / "captchas"
|
|
OUT = ROOT / "try" / "out" / "G"
|
|
OUT.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
def files_for(prefix):
|
|
bigs = [p for p in CAP.glob(f"{prefix}*.jpeg") if "-mark" not in p.stem]
|
|
if not bigs:
|
|
return None, None
|
|
big = bigs[0]
|
|
return big, CAP / f"{big.stem}-mark.png"
|
|
|
|
|
|
def detect_components(gray):
|
|
"""用局部暗目标检测提取候选组件。"""
|
|
blur = cv2.GaussianBlur(gray, (0, 0), 15)
|
|
dark = np.clip(blur.astype(np.int16) - gray.astype(np.int16), 0, 255).astype(np.uint8)
|
|
mask = (dark > 12).astype(np.uint8) * 255
|
|
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, np.ones((5, 5), np.uint8), iterations=2)
|
|
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, np.ones((3, 3), np.uint8))
|
|
return mask
|
|
|
|
|
|
def process(prefix):
|
|
big_path, mark_path = files_for(prefix)
|
|
if big_path is None:
|
|
return
|
|
big = cv2.imread(str(big_path), cv2.IMREAD_COLOR)
|
|
mark = cv2.imread(str(mark_path), cv2.IMREAD_UNCHANGED)
|
|
if big is None or mark is None:
|
|
return
|
|
alpha = mark[..., 3] if mark.ndim == 3 and mark.shape[2] == 4 else np.full(mark.shape[:2], 255, np.uint8)
|
|
m = (alpha > 128).astype(np.uint8) * 255
|
|
mc, _ = cv2.findContours(m, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
mc = max(mc, key=cv2.contourArea)
|
|
ma = max(cv2.contourArea(mc), 1)
|
|
gray = cv2.cvtColor(big, cv2.COLOR_BGR2GRAY)
|
|
mask = detect_components(gray)
|
|
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
vis = big.copy()
|
|
found = []
|
|
for c in contours:
|
|
area = cv2.contourArea(c)
|
|
x, y, w, h = cv2.boundingRect(c)
|
|
if area < ma * 0.08 or area > ma * 3 or min(w, h) < 20 or max(w, h) > 180:
|
|
continue
|
|
score = cv2.matchShapes(mc, c, cv2.CONTOURS_MATCH_I1, 0)
|
|
found.append((score, area, x, y, w, h))
|
|
found.sort(key=lambda z: z[0])
|
|
for i, (_, _, x, y, w, h) in enumerate(found[:5]):
|
|
color = (0, 0, 255) if i == 0 else (255, 0, 0)
|
|
cv2.rectangle(vis, (x, y), (x + w, y + h), color, 2)
|
|
cv2.putText(vis, f"{i+1}", (x, max(14, y - 3)), cv2.FONT_HERSHEY_SIMPLEX, .5, color, 2)
|
|
cv2.imwrite(str(OUT / f"{prefix}-components.png"), vis)
|
|
line = " ".join(f"#{i+1}:{s:.3f}@({x},{y},{w},{h})" for i,(s,_,x,y,w,h) in enumerate(found[:5]))
|
|
print(prefix, line or "无候选")
|
|
|
|
|
|
def main():
|
|
prefixes = sorted({p.name.split("~", 1)[0] for p in CAP.glob("*.jpeg") if "-mark" not in p.stem})
|
|
for prefix in prefixes:
|
|
process(prefix)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|