Add slide and rotate interactive captcha solvers

New solver subsystem with independent models:
- GapDetectorCNN (1x128x256 grayscale → sigmoid) for slide gap detection
- RotationRegressor (3x128x128 RGB → sin/cos via tanh) for rotation angle prediction
- SlideSolver with 3-tier strategy: template match → edge detect → CNN fallback
- RotateSolver with ONNX sin/cos → atan2 inference
- Generators, training scripts, CLI commands, and slide track utility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hua
2026-03-11 18:07:06 +08:00
parent 90d6423551
commit 9b5f29083e
20 changed files with 1440 additions and 10 deletions

View File

@@ -224,3 +224,55 @@ class RegressionDataset(Dataset):
img = self.transform(img)
return img, torch.tensor([label], dtype=torch.float32)
# ============================================================
# 旋转求解器用数据集 (sin/cos 编码)
# ============================================================
class RotateSolverDataset(Dataset):
"""
旋转求解器数据集。
从目录中读取 {angle}_{xxx}.png 文件,
将角度转换为 (sin θ, cos θ) 目标。
RGB 输入,不转灰度。
"""
def __init__(
self,
dirs: list[str | Path],
transform: transforms.Compose | None = None,
):
"""
Args:
dirs: 数据目录列表
transform: 图片预处理/增强 (RGB)
"""
import math
self.transform = transform
self.samples: list[tuple[str, float, float]] = [] # (路径, sin, cos)
for d in dirs:
d = Path(d)
if not d.exists():
continue
for f in sorted(d.glob("*.png")):
raw_label = f.stem.rsplit("_", 1)[0]
try:
angle = float(raw_label)
except ValueError:
continue
rad = math.radians(angle)
self.samples.append((str(f), math.sin(rad), math.cos(rad)))
def __len__(self) -> int:
return len(self.samples)
def __getitem__(self, idx: int):
import torch
path, sin_val, cos_val = self.samples[idx]
img = Image.open(path).convert("RGB")
if self.transform:
img = self.transform(img)
return img, torch.tensor([sin_val, cos_val], dtype=torch.float32)