Files
CaptchBreaker/CLAUDE.md

630 lines
26 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-3.12
- uv (包管理,依赖定义在 pyproject.toml)
- PyTorch 2.x (训练)
- ONNX + ONNXRuntime (推理部署)
- Pillow (图像处理)
- OpenCV (可选,滑块求解与相关测试)
- FastAPI (可选,提供 HTTP 识别服务)
## 当前命令入口
- 优先使用 `uv run captcha ...` 调用 CLI`pyproject.toml` 已将 `captcha` 映射到 `cli:main`
- 额外依赖按需安装:
- `uv sync --extra server`HTTP 服务
- `uv sync --extra cv`OpenCV / 滑块 solver
- `uv sync --extra dev`pytest
- Linux `x86_64` 环境通过 `pyproject.toml``tool.uv.sources` / `tool.uv.index` 固定从官方 `cu121` index 安装 `torch==2.5.1``torchvision==0.20.1`
- 该组合已验证可在 GTX 1050 Ti (`sm_61`) 上执行 CUDA仓库之前解析到的 `torch 2.10.0 + cu128` 不兼容这张卡
- Python 3.10 环境下保持 `onnxruntime < 1.24`,避免 `uv` 解析到无 `cp310` wheel 的版本
- 当前 CLI 子命令包括:`generate``train``export``predict``predict-dir``serve``generate-solver``train-solver``solve``train-funcaptcha``predict-funcaptcha`
## 项目结构
```
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/
│ │ └── funcaptcha/
│ │ └── 4_3d_rollball_animals/
│ ├── 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)
│ └── fun_captcha_siamese.py # FunCaptcha 专项 Siamese
├── 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_funcaptcha_rollball.py # 训练 4_3d_rollball_animals
│ ├── train_utils.py # CTC 训练通用逻辑
│ ├── train_regression_utils.py # 回归训练通用逻辑
│ ├── data_fingerprint.py # 合成数据指纹 / manifest
│ └── dataset.py # 通用 Dataset 类
├── inference/
│ ├── __init__.py
│ ├── pipeline.py # 核心推理流水线 (调度+识别)
│ ├── fun_captcha.py # FunCaptcha 专项推理
│ ├── export_onnx.py # PyTorch → ONNX 导出脚本
│ ├── model_metadata.py # ONNX sidecar metadata
│ └── 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
│ └── funcaptcha_rollball_animals.pth
├── onnx_models/ # 导出的 ONNX 模型
│ ├── classifier.onnx
│ ├── classifier.meta.json
│ ├── normal.onnx
│ ├── math.onnx
│ ├── threed_text.onnx
│ ├── threed_rotate.onnx
│ ├── threed_slider.onnx
│ ├── gap_detector.onnx
│ ├── rotation_regressor.onnx
│ └── funcaptcha_rollball_animals.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"
```
补充规则:
- ONNX 导出时同步生成 `<model>.meta.json`,保存 OCR 字符集、分类器类别顺序、回归标签范围等部署时必需的信息。
- `inference/pipeline.py` 优先读取 sidecar metadata缺失时才回退到 `config.py`,以兼容历史导出产物。
### 调度分类器 (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
### FunCaptcha 专项专家 (fun_captcha_siamese.py)
- 任务: 识别 `task.question=4_3d_rollball_animals`
- 路由方式: 不走调度分类器,而是由 HTTP/CLI 的 `question` 直接路由到专项模型
- 输入: 从整张 challenge 截图中裁出 `reference` 和 4 个 top-row candidates
- 预处理: RGB `3x48x48`
- 架构: 共享编码器 Siamese`candidate/reference` 特征拼接后输出单个匹配 logit
- 训练方式: 每个 challenge 展开成 4 组 pair正确候选为正样本其余为负样本
- 推理输出: 对 4 个候选分别打分,取 argmax返回 `objects=[index]`
- 模型体积目标: < 2MB
## 数据生成器规范
### 基类 (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`
### Slide Solver 目标约定
- `slide_gen.py` / `GapDetectorCNN` / `slide_solver.py` 统一使用缺口中心点 `x` 作为输出语义
- 训练时先按 solver 输入宽度归一化到 `[0, 1]`,运行时再映射回原图宽度
## 训练规范
### 通用训练配置
```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` 避免多进程兼容问题
补充规则:
- 合成数据目录写入 `.dataset_meta.json`,其指纹由生成器源码哈希与配置快照共同构成。
- OCR / 回归训练只在 checkpoint 中的 `synthetic_data_spec_hash` 与当前数据指纹一致时续训;合成数据被刷新后必须从 epoch 1 重新训练。
- legacy `normal` / `math` 数据在 manifest 缺失时可被采纳,但 `math` 仍必须覆盖 `+ - × ÷`,缺失 `÷` 时必须重建数据后再训练。
### 数据增强策略
```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 # 按依赖顺序全部训练
uv run python cli.py train-funcaptcha --question 4_3d_rollball_animals
# 导出 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 export --model 4_3d_rollball_animals
# 推理
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/ # 批量识别
uv run python cli.py predict-funcaptcha challenge.jpg --question 4_3d_rollball_animals
# 启动 HTTP 服务 (需先安装 server 可选依赖)
uv run python cli.py serve --port 8080
```
## HTTP 服务 (server.py可选)
纯推理服务,不依赖 torch / 训练代码,仅需 onnxruntime + FastAPI。
```python
# POST /solve - JSON base64 图片识别 (同步)
# 请求: {"image": "<base64>", "type": "normal"} (type 可选)
# 请求也可用 {"image":"<base64>", "question":"4_3d_rollball_animals"}
# 响应: {"type": "normal", "result": "A3B8", "raw": "A3B8", "time_ms": 12.3}
# FunCaptcha 响应: {"type":"funcaptcha","question":"4_3d_rollball_animals","objects":[2],"result":"2","raw":"2","time_ms":12.3}
#
# POST /solve/upload - multipart 文件上传识别 (同步)
# 请求: multipart/form-data, 字段名 image, 可选 query param type/question
# 响应: 同上
#
# POST /createTask - 创建异步识别任务
# 请求: {"clientKey":"local","callbackUrl":"https://...","softId":1,"languagePool":"en","task":{"type":"ImageToTextTask","body":"<base64>","captchaType":"normal"}}
# 或: {"clientKey":"local","task":{"type":"FunCaptcha","body":"<base64>","question":"4_3d_rollball_animals"}}
# 响应: {"errorId":0,"taskId":"<uuid>","status":"processing","createTime":1710000000,"expiresAt":1710000600}
#
# POST /getTaskResult - 查询异步任务结果
# 处理中: {"errorId":0,"taskId":"<uuid>","status":"processing"}
# 完成: {"errorId":0,"taskId":"<uuid>","status":"ready","cost":"0.00000","ip":"127.0.0.1","solveCount":1,"task":{"type":"ImageToTextTask","captchaType":"normal"},"callback":{"configured":true,"attempts":1,"delivered":true},"solution":{"text":"A3B8","answer":"A3B8","raw":"A3B8","captchaType":"normal","timeMs":12.3}}
# FunCaptcha 完成: {"errorId":0,"taskId":"<uuid>","status":"ready","task":{"type":"FunCaptcha","question":"4_3d_rollball_animals"},"solution":{"objects":[2],"answer":2,"raw":"2","text":"2","question":"4_3d_rollball_animals","timeMs":12.3}}
#
# POST /getBalance - 本地兼容接口
# 响应: {"errorId":0,"balance":999999.0}
#
# GET /health - 健康检查
# GET /api/v1/health - 健康检查兼容别名
# 响应: {"status": "ok", "models_loaded": true, "client_key_required": false, "async_tasks": {...}}
```
- 异步任务接口参考 `ohmycaptcha``taskId` 轮询模式实现
- 兼容根路径与 `/api/v1/*` 双路由;如设置环境变量 `CLIENT_KEY`,则任务接口要求请求体中的 `clientKey` 匹配
- 普通 OCR 任务通过 `task.captchaType` 路由FunCaptcha 专项任务通过 `task.question` 路由,不进入 `CaptchaPipeline` 调度分类器
- `callbackUrl` 会在任务完成后触发一次 `application/x-www-form-urlencoded` POST 回调,字段包含 `id/taskId``code`;默认失败重试 2 次并按退避间隔重发
- 若设置环境变量 `CALLBACK_SIGNING_SECRET`,回调请求会携带 `X-CaptchaBreaker-Timestamp``X-CaptchaBreaker-Signature-Alg``X-CaptchaBreaker-Signature` 头,签名算法为 `hmac-sha256`
- 任务结果额外暴露 `task` / `callback` 元信息,便于接入方排查异步状态
- 任务结果持久化在 `data/server_tasks/`,默认 TTL 为 600 秒,服务重启后可恢复未过期任务
## 关键约束和注意事项
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. **类型扩展**: OCR/回归验证码类型继续遵循 “生成器 + 专家模型 + 分类器类别” 的主线FunCaptcha 这类专项 challenge 优先走 `task.question` 专项路由,不强行塞进 `CAPTCHA_TYPES`
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 |
| FunCaptcha rollball | > 90% challenge acc | < 30ms | < 2MB |
| 全流水线 | - | < 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. 实现 FunCaptcha 专项推理/训练支线 (`fun_captcha.py`, `train_funcaptcha_rollball.py`)
7. 实现 cli.py 统一入口
8. 可选: server.py HTTP 服务
9. 编写 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 |