Expand 3D captcha into three subtypes: 3d_text, 3d_rotate, 3d_slider

Split the single "3d" captcha type into three independent expert models:
- 3d_text: 3D perspective text OCR (renamed from old "3d", CTC-based ThreeDCNN)
- 3d_rotate: rotation angle regression (new RegressionCNN, circular loss)
- 3d_slider: slider offset regression (new RegressionCNN, SmoothL1 loss)

CAPTCHA_TYPES expanded from 3 to 5 classes. Classifier samples updated
to 50000 (10000 per class). New generators, model, dataset, training
utilities, and full pipeline/export/CLI support for all subtypes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hua
2026-03-11 13:55:53 +08:00
parent 760b80ee5e
commit f5be7671bc
20 changed files with 1109 additions and 142 deletions

157
CLAUDE.md
View File

@@ -24,29 +24,40 @@ captcha-breaker/
│ ├── synthetic/ # 合成训练数据 (自动生成,不入 git)
│ │ ├── normal/ # 普通字符型
│ │ ├── math/ # 算式型
│ │ ── 3d/ # 3D立体型
│ │ ── 3d_text/ # 3D立体文字
│ │ ├── 3d_rotate/ # 3D旋转型
│ │ └── 3d_slider/ # 3D滑块型
│ ├── real/ # 真实验证码样本 (手动标注)
│ │ ├── normal/
│ │ ├── math/
│ │ ── 3d/
│ │ ── 3d_text/
│ │ ├── 3d_rotate/
│ │ └── 3d_slider/
│ └── classifier/ # 调度分类器训练数据 (混合各类型)
├── generators/
│ ├── __init__.py
│ ├── base.py # 生成器基类
│ ├── normal_gen.py # 普通字符验证码生成器
│ ├── math_gen.py # 算式验证码生成器 (如 3+8=?)
── threed_gen.py # 3D立体验证码生成器
── threed_gen.py # 3D立体文字验证码生成器
│ ├── threed_rotate_gen.py # 3D旋转验证码生成器
│ └── threed_slider_gen.py # 3D滑块验证码生成器
├── models/
│ ├── __init__.py
│ ├── lite_crnn.py # 轻量 CRNN (用于普通字符和算式)
│ ├── classifier.py # 调度分类模型
── threed_cnn.py # 3D验证码专用模型 (更深的CNN)
── threed_cnn.py # 3D文字验证码专用模型 (更深的CNN)
│ └── regression_cnn.py # 回归CNN (3D旋转+滑块, ~1MB)
├── training/
│ ├── __init__.py
│ ├── train_classifier.py # 训练调度模型
│ ├── train_normal.py # 训练普通字符识别
│ ├── train_math.py # 训练算式识别
│ ├── train_3d.py # 训练3D识别
│ ├── train_3d_text.py # 训练3D文字识别
│ ├── train_3d_rotate.py # 训练3D旋转回归
│ ├── train_3d_slider.py # 训练3D滑块回归
│ ├── train_utils.py # CTC 训练通用逻辑
│ ├── train_regression_utils.py # 回归训练通用逻辑
│ └── dataset.py # 通用 Dataset 类
├── inference/
│ ├── __init__.py
@@ -57,12 +68,16 @@ captcha-breaker/
│ ├── classifier.pth
│ ├── normal.pth
│ ├── math.pth
── threed.pth
── threed_text.pth
│ ├── threed_rotate.pth
│ └── threed_slider.pth
├── onnx_models/ # 导出的 ONNX 模型
│ ├── classifier.onnx
│ ├── normal.onnx
│ ├── math.onnx
── threed.onnx
── threed_text.onnx
│ ├── threed_rotate.onnx
│ └── threed_slider.onnx
├── server.py # FastAPI 推理服务 (可选)
├── cli.py # 命令行入口
└── tests/
@@ -78,13 +93,13 @@ captcha-breaker/
```
输入图片 → 预处理 → 调度分类器 → 路由到专家模型 → 后处理 → 输出结果
┌────────────────┐
▼ ▼ ▼
normal math 3d
(CRNN) (CRNN) (CNN)
│ │ │
▼ ▼ ▼
"A3B8" "3+8=?"→11 "X9K2"
┌────────┬───┼───────┬──────────┐
normal math 3d_text 3d_rotate 3d_slider
(CRNN) (CRNN) (CNN) (RegCNN) (RegCNN)
"A3B8" "3+8=?"→11 "X9K2" "135" "87"
```
### 调度分类器 (classifier.py)
@@ -102,7 +117,7 @@ class CaptchaClassifier(nn.Module):
轻量分类器,几层卷积即可区分不同类型验证码。
不同类型验证码视觉差异大有无运算符、3D效果等分类很容易。
"""
def __init__(self, num_types=3):
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)
@@ -142,14 +157,32 @@ def eval_captcha_math(expr: str) -> str:
pass
```
### 3D立体识别专家 (threed_cnn.py)
### 3D立体文字识别专家 (threed_cnn.py)
- 任务: 识别带 3D 透视/阴影效果的验证码
- 任务: 识别带 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)
@@ -185,13 +218,26 @@ class BaseCaptchaGenerator:
- 标签格式: `3+8` (存储算式本身,不存结果)
- 视觉风格: 与目标算式验证码一致
### 3D生成器 (threed_gen.py)
### 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`
## 训练规范
### 通用训练配置
@@ -204,7 +250,7 @@ TRAIN_CONFIG = {
'batch_size': 128,
'lr': 1e-3,
'scheduler': 'cosine',
'synthetic_samples': 30000, # 每类 10000
'synthetic_samples': 50000, # 每类 10000 × 5 类
},
'normal': {
'epochs': 50,
@@ -222,7 +268,7 @@ TRAIN_CONFIG = {
'synthetic_samples': 60000,
'loss': 'CTCLoss',
},
'threed': {
'3d_text': {
'epochs': 80,
'batch_size': 64,
'lr': 5e-4,
@@ -230,18 +276,36 @@ TRAIN_CONFIG = {
'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. 检查合成数据是否已生成,没有则自动调用生成器
2. 支持混合真实数据 (如果 data/real/{type}/ 有文件)
3. 使用数据增强: RandomAffine, ColorJitter, GaussianBlur, RandomErasing
4. 输出训练日志: epoch, loss, 整体准确率, 字符级准确率
5. 保存最佳模型到 checkpoints/
6. 训练结束自动导出 ONNX 到 onnx_models/
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` 避免多进程兼容问题
### 数据增强策略
@@ -281,14 +345,14 @@ class CaptchaPipeline:
pass
def classify(self, image: Image.Image) -> str:
"""调度分类,返回类型名: 'normal' / 'math' / '3d'"""
"""调度分类,返回类型名: 'normal' / 'math' / '3d_text' / '3d_rotate' / '3d_slider'"""
pass
def solve(self, image) -> str:
"""
完整识别流程:
1. 分类验证码类型
2. 路由到对应专家模型
2. 路由到对应专家模型 (CTC 或回归)
3. 后处理 (算式型需要计算结果)
4. 返回最终答案字符串
@@ -311,7 +375,7 @@ def export_model(model, model_name, input_shape, onnx_dir='onnx_models/'):
pass
def export_all():
"""依次导出 classifier, normal, math, threed个模型"""
"""依次导出 classifier, normal, math, threed_text, threed_rotate, threed_slider 六个模型"""
pass
```
@@ -325,23 +389,29 @@ 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 --num 80000
uv run python cli.py generate --type classifier --num 30000
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
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-dir ./test_images/ # 批量识别
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
@@ -360,11 +430,12 @@ uv run python cli.py serve --port 8080
1. **所有模型用 float32 训练,导出 ONNX 时不做量化**,先保证精度
2. **CTC 解码统一用贪心解码**,不需要 beam search验证码场景贪心够用
3. **字符集由 config.py 统一定义**: 当前 normal 保留易混淆字符3d 继续使用去混淆字符集
3. **字符集由 config.py 统一定义**: 当前 normal 保留易混淆字符3d_text 继续使用去混淆字符集
4. **算式识别分两步**: 先 OCR 识别字符串,再用规则计算,不要让模型直接输出数值
5. **生成器的随机种子**: 生成数据时设置 seed 保证可复现
5. **随机种子**: 生成数据和训练时均通过 `config.RANDOM_SEED` 设置全局种子 (random/numpy/torch)保证可复现
6. **真实数据文件名格式**: `{label}_{任意}.png`label 部分是标注内容
7. **模型保存格式**: PyTorch checkpoint 包含 model_state_dict, chars, best_acc, epoch
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 中的对应内容,保持文档与代码一致
@@ -373,18 +444,20 @@ uv run python cli.py serve --port 8080
| 模型 | 准确率目标 | 推理延迟 | 模型体积 |
|------|-----------|---------|---------|
| 调度分类器 | > 99% | < 5ms | < 500KB |
| 调度分类器 (5类) | > 99% | < 5ms | < 500KB |
| 普通字符 | > 95% | < 30ms | < 2MB |
| 算式识别 | > 93% | < 30ms | < 2MB |
| 3D立体 | > 85% | < 50ms | < 5MB |
| 全流水线 | - | < 80ms | < 10MB 总计 |
| 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 → classifier
4. 按顺序训练: normal → math → 3d_text → 3d_rotate → 3d_slider → classifier
5. 实现 inference/pipeline.py 和 export_onnx.py
6. 实现 cli.py 统一入口
7. 可选: server.py HTTP 服务