HTTP 服务:输入验证码图片 + 中文意图("请输入验证码文字 / 红色文字 / 蓝色文字"),返回识别字符。两步法——HSV 单色抠图 + PP-OCRv5 识别,可选 LLM 兜底。被 Chrome 采集插件调用,用于攻克税务发票查验平台的彩色验证码。
本服务的主要调用方是 Chrome 采集插件。插件在采集全国增值税发票查验平台(inv-veri.chinatax.gov.cn)时,该平台要求输入一段彩色验证码(提示"请输入图片中红色/蓝色文字"),插件把验证码交给本服务识别。
Chrome 插件 (directDetail/chinataxV2.ts)
│ ① 抓验证码图片 → 转 base64
│ ② 读页面提示文字当 prompt(如"请输入红色文字")
▼
POST http://<验证码服务>/recognize_base64 ← 本服务
│ ③ 返回识别文字 text
▼
插件填入 #yzm → 提交查验 (queryFpcyxx)
│ ④ 以税务局接口响应判定验证码对错
▼
POST /label/feedback 上报对错 → 进入准确率统计与再训练闭环
全部 / 红色 / 蓝色 / 黄色 四种颜色意图,与本服务 config.yaml 的 intent_rules 精确对应。验证码对错以税务局 queryFpcyxx 查验接口的响应为准,而非本服务自身置信度,判定结果回流形成准确率统计。http://192.168.113.70:8123(端口按部署环境而定,非固定 8000)。旧版采集器 chinatax.ts 调 /recognize,新版 chinataxV2.ts 调 /recognize_base64 并带 /label/feedback 反馈。验证码图片 + 中文意图 prompt
│
▼
① 意图路由 (RED / BLUE / YELLOW / ALL)
│
▼
② 单色抠图 (HSV 四色阈值 + 连通域去噪) —— RED/BLUE/YELLOW 时按颜色抠字
│
▼
③ PP-OCRv5 识别 (在 1749 张人工样本上微调的 mobile rec head)
│
├─ 高置信 → 直接返回 (source=ocr)
└─ 低置信/含中文 → 可选 LLM 兜底 (source=llm)
data/finetune_output_v2/best_accuracy/inference/)。抠图保留原始墨色白底(不二值化),因为 PaddleOCR 对自然色文字识别更好。服务监听 8000,响应统一 RecognizeResponse 结构。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /health | 健康检查,返回 {"status":"ok"}。启动即 200,但首次 OCR 需等模型加载 15–30 秒。 |
| POST | /recognize | multipart 上传:image(file) + prompt(string) + rec_only(可选) |
| POST | /recognize_base64 | JSON:img_base64 + prompt + rec_only。允许 data:image/png;base64, 前缀 |
# multipart
curl -F image=@captcha.png \
-F 'prompt=识别全部' \
-F 'rec_only=false' \
http://127.0.0.1:8000/recognize
# base64 JSON
curl -X POST http://127.0.0.1:8000/recognize_base64 \
-H 'Content-Type: application/json' \
-d '{"img_base64":"iVBORw0K...","prompt":"识别全部","rec_only":false}'
rec_only 为 1/true/yes/on 时跳过检测,整图直接过 rec head。约束:图片 ≤ 2MB,仅 PNG/JPEG。config/config.yaml > intent_rules 命中规则(不区分大小写,子串匹配),未命中回退 default_intent(默认 ALL):
| 意图 | 关键词 |
|---|---|
RED | 红色、红字、red |
BLUE | 蓝色、蓝字、blue |
YELLOW | 黄色、黄字、yellow |
ALL | 全部、所有、验证码文字、all |
颜色阈值在 config.yaml > color_filter 配置(HSV 范围、形态学核、单色抠图参数)。改阈值后需重跑 python -m tests.eval 校准准确率。
{
"code": 0, // 0成功 / 1命中中文需复核 / 2识别失败
"text": "TET7",
"mode": "ALL", // 实际走的意图分支 ALL/RED/BLUE
"source": "ocr", // ocr / ocr_lowconf / llm
"confidence": 0.8949,
"ocr_confidence": 0.8949,
"elapsed_ms": 262,
"message": null
}
code 而非 HTTP 状态码:识别失败/含中文都是 200 OK,只在参数错误时返 4xx。| code | http | 含义 |
|---|---|---|
| 4001 | 400 | image / prompt 缺失 |
| 4002 | 400 | 格式不支持 / 超 2MB / base64 解析失败 |
| 4003 | 400 | prompt 太长或无法路由 |
| 5001 | 500 | 所有识别路径都失败 |
| 5003 | 500 | 服务端未知错误 |
/label/*)除识别接口外,服务还带一套在线标注 + 反馈 + 再训练闭环(app/api/label_routes.py,配合 app/static/label.html 页面)。插件采集时的对错反馈、以及人工打标,都汇入这里持续改进模型。
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /label/feedback | 上报一次识别的对错(插件以税务局查验结果为准调用),计入准确率统计 |
| GET | /label/next | 后端代理上游验证码接口(绕过 CORS),随机取发票号拉图,用本地模型识别一次返回 |
| POST | /label/save | 存人工确认/纠正后的样本到 data/raw_dump/打标-已标记/,每满 100 张提示可开训 |
| GET | /label/history | 标注历史 |
| POST | /label/train | 触发模型微调训练(?mode=full) |
| GET | /label/train/status · /label/train/config | 训练状态与配置 |
<毫秒>,<颜色>,<答案>.png 可被 scripts/prepare_finetune_dataset_v2.py 直接消费,形成"采集识别 → 反馈纠错 → 增量微调"的自我改进链路。python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
python -m app.main # 首次启动下载 PP-OCRv5 权重到 ~/.paddleocr
docker compose up -d --build
docker compose logs -f captcha # 等到 "Application startup complete."
curl http://<host>:8000/health
paddlepaddle 3.3.1 / paddleocr 3.4.1 / paddlex 3.4.3。首次启动顺序拉取模型到容器内 /root/.paddlex/official_models/,缓存在 paddlex_cache named volume,重启不再下载:
PP-OCRv5_server_det(~84MB,文本检测)PP-OCRv5_server_rec(~81MB,识别 fallback)PP-OCRv5_mobile_rec(~8MB,主识别头)PP-LCNet_x1_0_textline_ori(~7MB,行方向分类)en_PP-OCRv5_mobile_rec(~8MB,副 engine)# 内网离线:先在联网机预热 ~/.paddlex,再喂给容器 volume
docker compose up -d && docker compose stop captcha
docker run --rm -v $(pwd):/src -v invoice_verification_paddlex_cache:/dst \
busybox sh -c "cp -r /src/paddlex/* /dst/"
docker compose start captcha
# 常用运维
docker compose ps
docker compose restart captcha
docker compose down # 保留 volumes
docker compose down -v # 连模型缓存一起删(会重新下载)
config.yaml 设 llm.enabled: true,环境变量给 LLM_API_KEY,可调 model/base_url 用 OpenAI 兼容 API。data/),无需外部存储。