Files
CaptchBreaker/CLAUDE.md
Hua 9b5f29083e 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>
2026-03-11 18:07:06 +08:00

545 lines
20 KiB
Markdown
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.
# CLAUDE.md - 验证码识别多模型系统 (CaptchaBreaker)
## 项目概述
构建一个本地验证码识别系统,采用 **调度模型 + 多专家模型** 的两级架构。调度模型负责分类验证码类型,专家模型负责具体识别。所有模型轻量化设计,最终导出 ONNX 用于部署。
## 技术栈
- Python 3.10+
- uv (包管理,依赖定义在 pyproject.toml)
- PyTorch 2.x (训练)
- ONNX + ONNXRuntime (推理部署)
- Pillow (图像处理)
- FastAPI (可选,提供 HTTP 识别服务)
## 项目结构
```
captcha-breaker/
├── CLAUDE.md
├── pyproject.toml # 项目配置与依赖 (uv 管理)
├── config.py # 全局配置 (字符集、图片尺寸、路径等)
├── data/
│ ├── synthetic/ # 合成训练数据 (自动生成,不入 git)
│ │ ├── normal/ # 普通字符型
│ │ ├── math/ # 算式型
│ │ ├── 3d_text/ # 3D立体文字型
│ │ ├── 3d_rotate/ # 3D旋转型
│ │ └── 3d_slider/ # 3D滑块型
│ ├── real/ # 真实验证码样本 (手动标注)
│ │ ├── normal/
│ │ ├── math/
│ │ ├── 3d_text/
│ │ ├── 3d_rotate/
│ │ └── 3d_slider/
│ ├── classifier/ # 调度分类器训练数据 (混合各类型)
│ └── solver/ # Solver 训练数据
│ ├── slide/ # 滑块缺口检测训练数据
│ └── rotate/ # 旋转角度回归训练数据
├── generators/
│ ├── __init__.py
│ ├── base.py # 生成器基类
│ ├── normal_gen.py # 普通字符验证码生成器
│ ├── math_gen.py # 算式验证码生成器 (如 3+8=?)
│ ├── threed_gen.py # 3D立体文字验证码生成器
│ ├── threed_rotate_gen.py # 3D旋转验证码生成器
│ ├── threed_slider_gen.py # 3D滑块验证码生成器
│ ├── slide_gen.py # 滑块缺口训练数据生成器
│ └── rotate_solver_gen.py # 旋转求解器训练数据生成器
├── models/
│ ├── __init__.py
│ ├── lite_crnn.py # 轻量 CRNN (用于普通字符和算式)
│ ├── classifier.py # 调度分类模型
│ ├── threed_cnn.py # 3D文字验证码专用模型 (更深的CNN)
│ ├── regression_cnn.py # 回归CNN (3D旋转+滑块, ~1MB)
│ ├── gap_detector.py # 滑块缺口检测CNN (~1MB)
│ └── rotation_regressor.py # 旋转角度回归 sin/cos (~2MB)
├── training/
│ ├── __init__.py
│ ├── train_classifier.py # 训练调度模型
│ ├── train_normal.py # 训练普通字符识别
│ ├── train_math.py # 训练算式识别
│ ├── train_3d_text.py # 训练3D文字识别
│ ├── train_3d_rotate.py # 训练3D旋转回归
│ ├── train_3d_slider.py # 训练3D滑块回归
│ ├── train_slide.py # 训练滑块缺口检测
│ ├── train_rotate_solver.py # 训练旋转角度回归
│ ├── train_utils.py # CTC 训练通用逻辑
│ ├── train_regression_utils.py # 回归训练通用逻辑
│ └── dataset.py # 通用 Dataset 类
├── inference/
│ ├── __init__.py
│ ├── pipeline.py # 核心推理流水线 (调度+识别)
│ ├── export_onnx.py # PyTorch → ONNX 导出脚本
│ └── math_eval.py # 算式计算模块
├── solvers/ # 交互式验证码求解器
│ ├── __init__.py
│ ├── base.py # 求解器基类
│ ├── slide_solver.py # 滑块求解 (OpenCV + CNN)
│ └── rotate_solver.py # 旋转求解 (ONNX sin/cos)
├── utils/
│ ├── __init__.py
│ └── slide_utils.py # 滑块轨迹生成工具
├── checkpoints/ # 训练产出的模型文件
│ ├── classifier.pth
│ ├── normal.pth
│ ├── math.pth
│ ├── threed_text.pth
│ ├── threed_rotate.pth
│ ├── threed_slider.pth
│ ├── gap_detector.pth
│ └── rotation_regressor.pth
├── onnx_models/ # 导出的 ONNX 模型
│ ├── classifier.onnx
│ ├── normal.onnx
│ ├── math.onnx
│ ├── threed_text.onnx
│ ├── threed_rotate.onnx
│ ├── threed_slider.onnx
│ ├── gap_detector.onnx
│ └── rotation_regressor.onnx
├── server.py # FastAPI 推理服务 (可选)
├── cli.py # 命令行入口
└── tests/
├── test_generators.py
├── test_models.py
└── test_pipeline.py
```
## 核心架构设计
### 推理流水线
```
输入图片 → 预处理 → 调度分类器 → 路由到专家模型 → 后处理 → 输出结果
┌────────┬───┼───────┬──────────┐
▼ ▼ ▼ ▼ ▼
normal math 3d_text 3d_rotate 3d_slider
(CRNN) (CRNN) (CNN) (RegCNN) (RegCNN)
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
"A3B8" "3+8=?"→11 "X9K2" "135" "87"
```
### 调度分类器 (classifier.py)
- 任务: 图像分类,判断验证码属于哪个类型
- 架构: 轻量 CNN3-4 层卷积 + 全局平均池化 + 全连接
- 输入: 灰度图 1x64x128
- 输出: softmax 概率分布,类别数 = 验证码类型数
- 要求: 准确率 99%+,推理 < 5ms
- 模型体积目标: < 500KB
```python
class CaptchaClassifier(nn.Module):
"""
轻量分类器,几层卷积即可区分不同类型验证码。
不同类型验证码视觉差异大有无运算符、3D效果等分类很容易。
"""
def __init__(self, num_types=5):
# 4层卷积 + GAP + FC
# Conv2d(1,16) -> Conv2d(16,32) -> Conv2d(32,64) -> Conv2d(64,64)
# AdaptiveAvgPool2d(1) -> Linear(64, num_types)
pass
```
### 普通字符识别专家 (lite_crnn.py - normal 模式)
- 任务: 识别彩色字符验证码 (数字+字母混合)
- 架构: CRNN + CTC
- 字符集: `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ` (36个包含易混淆字符按本地配置训练)
- 输入: 灰度图 1x40x120
- 输出: 字符序列,通过 CTC 贪心解码
- 验证码特征: 浅色背景、彩色字符、轻微干扰线、字符有倾斜
- 模型体积目标: < 2MB
### 算式识别专家 (lite_crnn.py - math 模式)
- 任务: 识别算式验证码并计算结果
- 架构: 复用 CRNN + CTC字符集不同
- 字符集: `0123456789+-×÷=?` (数字+运算符)
- 输入: 灰度图 1x40x160 (算式通常更宽)
- 输出: 识别出算式字符串,然后交给 math_eval.py 计算
- 分两步: (1) OCR 识别 → "3+8=?" (2) 正则解析并计算 → 11
- 模型体积目标: < 2MB
```python
# math_eval.py 核心逻辑
def eval_captcha_math(expr: str) -> str:
"""
解析并计算验证码算式。
支持: 加减乘除,个位到两位数运算。
输入: "3+8=?""12×3=?""15-7=?"
输出: "11""36""8"
用正则提取数字和运算符,不要用 eval()。
"""
pass
```
### 3D立体文字识别专家 (threed_cnn.py)
- 任务: 识别带 3D 透视/阴影效果的文字验证码
- 架构: 更深的 CNN + CRNN或 ResNet-lite backbone
- 输入: 灰度图 1x60x160
- 需要更强的特征提取能力来处理透视变形和阴影
- 模型体积目标: < 5MB
### 3D旋转识别专家 (regression_cnn.py - 3d_rotate 模式)
- 任务: 预测旋转验证码的正确旋转角度
- 架构: 轻量回归 CNN (4层卷积 + GAP + FC + Sigmoid)
- 输入: 灰度图 1x80x80
- 输出: [0,1] sigmoid 值,按 (0, 360) 缩放回角度
- 标签范围: 0-359°
- 模型体积目标: ~1MB
### 3D滑块识别专家 (regression_cnn.py - 3d_slider 模式)
- 任务: 预测滑块拼图缺口的 x 偏移
- 架构: 同上回归 CNN不同输入尺寸
- 输入: 灰度图 1x80x240
- 输出: [0,1] sigmoid 值,按 (10, 200) 缩放回像素偏移
- 标签范围: 10-200px
- 模型体积目标: ~1MB
## 数据生成器规范
### 基类 (base.py)
```python
class BaseCaptchaGenerator:
def generate(self, text=None) -> tuple[Image.Image, str]:
"""生成一张验证码,返回 (图片, 标签文本)"""
raise NotImplementedError
def generate_dataset(self, num_samples: int, output_dir: str):
"""批量生成,文件名格式: {label}_{index:06d}.png"""
pass
```
### 普通字符生成器 (normal_gen.py)
模拟目标风格:
- 浅色随机背景 (RGB 各通道 230-255)
- 每个字符随机颜色 (深色: 蓝/红/绿/紫/棕等)
- 字符数量: 4-5 个
- 字符有 ±15° 随机旋转
- 2-5 条浅色干扰线
- 少量噪点
- 可选轻微高斯模糊
### 算式生成器 (math_gen.py)
- 生成形如 `A op B = ?` 的算式图片
- A, B 范围: 1-30 的整数
- op: +, -, × (除法只生成能整除的)
- 确保结果为非负整数
- 标签格式: `3+8` (存储算式本身,不存结果)
- 视觉风格: 与目标算式验证码一致
### 3D文字生成器 (threed_gen.py)
- 使用 Pillow 的仿射变换模拟 3D 透视
- 添加阴影效果
- 字符有深度感和倾斜
- 字符旋转角度由 `config.py` `GENERATE_CONFIG["3d_text"]["rotation_range"]` 统一配置
- 标签: 纯字符内容
### 3D旋转生成器 (threed_rotate_gen.py)
- 圆盘上绘制字符 + 方向标记,随机旋转 0-359°
- 标签 = 旋转角度(整数字符串)
- 文件名格式: `{angle}_{index:06d}.png`
### 3D滑块生成器 (threed_slider_gen.py)
- 纹理背景 + 拼图缺口 + 拼图块在左侧
- 标签 = 缺口 x 坐标偏移(整数字符串)
- 文件名格式: `{offset}_{index:06d}.png`
## 训练规范
### 通用训练配置
```python
# config.py 中定义
TRAIN_CONFIG = {
'classifier': {
'epochs': 30,
'batch_size': 128,
'lr': 1e-3,
'scheduler': 'cosine',
'synthetic_samples': 50000, # 每类 10000 × 5 类
},
'normal': {
'epochs': 50,
'batch_size': 128,
'lr': 1e-3,
'scheduler': 'cosine',
'synthetic_samples': 60000,
'loss': 'CTCLoss',
},
'math': {
'epochs': 50,
'batch_size': 128,
'lr': 1e-3,
'scheduler': 'cosine',
'synthetic_samples': 60000,
'loss': 'CTCLoss',
},
'3d_text': {
'epochs': 80,
'batch_size': 64,
'lr': 5e-4,
'scheduler': 'cosine',
'synthetic_samples': 80000,
'loss': 'CTCLoss',
},
'3d_rotate': {
'epochs': 60,
'batch_size': 128,
'lr': 1e-3,
'scheduler': 'cosine',
'synthetic_samples': 60000,
'loss': 'SmoothL1',
},
'3d_slider': {
'epochs': 60,
'batch_size': 128,
'lr': 1e-3,
'scheduler': 'cosine',
'synthetic_samples': 60000,
'loss': 'SmoothL1',
},
}
```
### 训练脚本要求
每个训练脚本必须:
1. 训练开始前设置全局随机种子 (random/numpy/torch),使用 `config.RANDOM_SEED`,保证可复现
2. 检查合成数据是否已生成,没有则自动调用生成器
3. 支持混合真实数据 (如果 data/real/{type}/ 有文件)
4. 使用数据增强: RandomAffine, ColorJitter, GaussianBlur, RandomErasing
5. 输出训练日志: epoch, loss, 整体准确率, 字符级准确率 (CTC) 或 MAE, 容差准确率 (回归)
6. 保存最佳模型到 checkpoints/
7. 训练结束自动导出 ONNX 到 onnx_models/
8. DataLoader 统一使用 `num_workers=0` 避免多进程兼容问题
### 数据增强策略
```python
# 训练时增强
train_augment = transforms.Compose([
transforms.Grayscale(),
transforms.Resize((H, W)),
transforms.RandomAffine(degrees=8, translate=(0.05, 0.05), scale=(0.95, 1.05)),
transforms.ColorJitter(brightness=0.3, contrast=0.3),
transforms.GaussianBlur(3, sigma=(0.1, 0.5)),
transforms.ToTensor(),
transforms.Normalize([0.5], [0.5]),
transforms.RandomErasing(p=0.15, scale=(0.01, 0.05)),
])
```
## 推理流水线 (pipeline.py)
```python
class CaptchaPipeline:
"""
核心推理流水线。
加载调度模型和所有专家模型 (ONNX 格式)。
提供统一的 solve(image) 接口。
"""
def __init__(self, models_dir='onnx_models/'):
"""
初始化加载所有 ONNX 模型。
使用 onnxruntime.InferenceSession。
"""
pass
def preprocess(self, image: Image.Image, target_size: tuple) -> np.ndarray:
"""图片预处理: resize, grayscale, normalize, 转 numpy"""
pass
def classify(self, image: Image.Image) -> str:
"""调度分类,返回类型名: 'normal' / 'math' / '3d_text' / '3d_rotate' / '3d_slider'"""
pass
def solve(self, image) -> str:
"""
完整识别流程:
1. 分类验证码类型
2. 路由到对应专家模型 (CTC 或回归)
3. 后处理 (算式型需要计算结果)
4. 返回最终答案字符串
image: PIL.Image 或文件路径或 bytes
"""
pass
```
## ONNX 导出 (export_onnx.py)
```python
def export_model(model, model_name, input_shape, onnx_dir='onnx_models/'):
"""
导出单个模型为 ONNX。
- 使用 opset_version=18
- 开启 dynamic_axes 支持动态 batch
- 导出后用 onnxruntime 验证推理一致性
- 可选: onnx 模型简化 (onnxsim)
"""
pass
def export_all():
"""依次导出 classifier, normal, math, threed_text, threed_rotate, threed_slider 六个模型"""
pass
```
## CLI 入口 (cli.py)
```bash
# 安装依赖
uv sync # 核心依赖
uv sync --extra server # 含 HTTP 服务依赖
# 生成训练数据
uv run python cli.py generate --type normal --num 60000
uv run python cli.py generate --type math --num 60000
uv run python cli.py generate --type 3d_text --num 80000
uv run python cli.py generate --type 3d_rotate --num 60000
uv run python cli.py generate --type 3d_slider --num 60000
uv run python cli.py generate --type classifier --num 50000
# 训练模型
uv run python cli.py train --model classifier
uv run python cli.py train --model normal
uv run python cli.py train --model math
uv run python cli.py train --model 3d_text
uv run python cli.py train --model 3d_rotate
uv run python cli.py train --model 3d_slider
uv run python cli.py train --all # 按依赖顺序全部训练
# 导出 ONNX
uv run python cli.py export --all
uv run python cli.py export --model 3d_text # "3d_text" 自动映射为 "threed_text"
# 推理
uv run python cli.py predict image.png # 自动分类+识别
uv run python cli.py predict image.png --type normal # 跳过分类直接识别
uv run python cli.py predict image.png --type 3d_rotate # 指定为旋转类型
uv run python cli.py predict-dir ./test_images/ # 批量识别
# 启动 HTTP 服务 (需先安装 server 可选依赖)
uv run python cli.py serve --port 8080
```
## HTTP 服务 (server.py可选)
```python
# FastAPI 服务,提供 REST API
# POST /solve - 上传图片,返回识别结果
# 请求: multipart/form-data字段名 image
# 响应: {"type": "normal", "result": "A3B8", "confidence": 0.95, "time_ms": 45}
```
## 关键约束和注意事项
1. **所有模型用 float32 训练,导出 ONNX 时不做量化**,先保证精度
2. **CTC 解码统一用贪心解码**,不需要 beam search验证码场景贪心够用
3. **字符集由 config.py 统一定义**: 当前 normal 保留易混淆字符3d_text 继续使用去混淆字符集
4. **算式识别分两步**: 先 OCR 识别字符串,再用规则计算,不要让模型直接输出数值
5. **随机种子**: 生成数据和训练时均通过 `config.RANDOM_SEED` 设置全局种子 (random/numpy/torch),保证可复现
6. **真实数据文件名格式**: `{label}_{任意}.png`label 部分是标注内容
11. **数据集字符过滤**: `CRNNDataset` 加载标签时,若发现字符不在字符集内会发出 warning便于排查标注/字符集不匹配问题
7. **模型保存格式**: CTC checkpoint 包含 model_state_dict, chars, best_acc, epoch; 回归 checkpoint 包含 model_state_dict, label_range, best_mae, best_tol_acc, epoch
8. **不使用 GPU 特有功能**,确保 CPU 也能训练和推理 (只是慢一些)
9. **类型扩展**: 新增验证码类型时,只需 (1) 加生成器 (2) 加专家模型 (3) 调度器加一个类别重新训练
10. **文档同步**: 对项目结构、配置、架构等做出变更时,必须同步更新 CLAUDE.md 中的对应内容,保持文档与代码一致
## 目标指标
| 模型 | 准确率目标 | 推理延迟 | 模型体积 |
|------|-----------|---------|---------|
| 调度分类器 (5类) | > 99% | < 5ms | < 500KB |
| 普通字符 | > 95% | < 30ms | < 2MB |
| 算式识别 | > 93% | < 30ms | < 2MB |
| 3D立体文字 | > 85% | < 50ms | < 5MB |
| 3D旋转 (±5°) | > 85% | < 30ms | ~1MB |
| 3D滑块 (±3px) | > 90% | < 30ms | ~1MB |
| 全流水线 | - | < 80ms | < 12MB 总计 |
## 开发顺序
1. 先实现 config.py 和 generators/
2. 实现 models/ 中所有模型定义
3. 实现 training/dataset.py 通用数据集类
4. 按顺序训练: normal → math → 3d_text → 3d_rotate → 3d_slider → classifier
5. 实现 inference/pipeline.py 和 export_onnx.py
6. 实现 cli.py 统一入口
7. 可选: server.py HTTP 服务
8. 编写 tests/
## 交互式 Solver 扩展
### 概述
在现有验证码识别架构之上,新增滑块 (slide) 和旋转 (rotate) 两种交互式验证码求解能力。与现有 3d_rotate/3d_slider 的区别:
- **3d_slider** (合成拼图回归) → **slide solver**: 真实滑块验证码OpenCV 优先 + CNN 兜底
- **3d_rotate** (合成圆盘 sigmoid 回归) → **rotate solver**: 真实旋转验证码sin/cos 编码 + 自然图
每个 solver 模型独立训练、独立导出 ONNX、独立替换互不依赖。
### 滑块求解器 (SlideSolver)
- 三种方法按优先级: 模板匹配 → 边缘检测 → CNN 兜底
- 模型: `GapDetectorCNN` (1x128x256 灰度 → sigmoid [0,1])
- OpenCV 延迟导入,未安装时退化到 CNN only
- 输出: `{"gap_x", "gap_x_percent", "confidence", "method"}`
### 旋转求解器 (RotateSolver)
- ONNX 推理 → (sin, cos) → atan2 → 角度
- 模型: `RotationRegressor` (3x128x128 RGB → tanh (sin θ, cos θ))
- 输出: `{"angle", "confidence"}`
### Solver CLI 用法
```bash
# 生成训练数据
uv run python cli.py generate-solver slide --num 30000
uv run python cli.py generate-solver rotate --num 50000
# 训练 (各模型独立)
uv run python cli.py train-solver slide
uv run python cli.py train-solver rotate
# 求解
uv run python cli.py solve slide --bg bg.png [--tpl tpl.png]
uv run python cli.py solve rotate --image img.png
# 导出 (已集成到 export --all)
uv run python cli.py export --model gap_detector
uv run python cli.py export --model rotation_regressor
```
### 滑块轨迹生成
`utils/slide_utils.py` 提供 `generate_slide_track(distance)`:
- 贝塞尔曲线 ease-out 加速减速
- y 轴 ±1~3px 随机抖动
- 时间间隔不均匀
- 末尾微小过冲回退
### Solver 目标指标
| 模型 | 准确率目标 | 推理延迟 | 模型体积 |
|------|-----------|---------|---------|
| 滑块 CNN (±5px) | > 85% | < 30ms | ~1MB |
| 旋转回归 (±5°) | > 85% | < 30ms | ~2MB |