news 2026/9/11 5:26:41

基于 PaddleOCR-VL 与 PP-OCRv6 的指定颜色发票验证码识别服务实践记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 PaddleOCR-VL 与 PP-OCRv6 的指定颜色发票验证码识别服务实践记录

一、为什么要做这个服务

在发票查验、票据流转、自动化录入等场景里,验证码并不总是简单的“看图输入字符”。有一类验证码会要求用户只识别某一种颜色的文字,例如提示识别红色、蓝色或黑色字符,而图片里还会混有干扰字符、背景纹理、噪声和相近色块。

直白的说:发票!发票!还是发票!这全都怪发票验真需要验证码!

镂空字体示例
实心字体示例
图片预处理结果

如果只把整张图直接丢给通用 OCR,效果往往不稳定。原因很直观:模型看到的是完整图片,而业务真正需要的是“指定颜色上的那一组字符”。当背景、干扰字、颜色相近或字体形态变化时,通用 OCR 很容易把不该识别的内容也读进去,或者漏掉真正需要的字符。

这次实践的目标不是做一个炫技 Demo,而是把验证码识别变成一个可维护、可复核、可持续迭代的服务。它需要满足几件事:

  • 能根据业务提示识别指定颜色的验证码文字;
  • 对常见颜色和字体变化保持稳定;
  • 能输出识别结果和可信度,便于上层业务判断是否采用;
  • 能记录失败案例,持续回流训练和评估;
  • 服务部署尽量轻量,便于在现有系统中集成。

二、整体思路

最初的想法很简单:找一个 OCR 模型,直接识别验证码图片。实际测试后发现,单纯依赖通用模型并不能稳定解决“指定颜色”这个问题。后来方案逐步调整为“视觉理解 + 文本识别 + 工程化预处理 + 质量闭环”的组合方式。

整体流程可以概括为:图片输入 -> 目标颜色处理 -> OCR/多模态识别 -> 结果判断 -> 失败回流

其中 PaddleOCR-VL 更适合在前期分析复杂图片结构、理解样本形态和辅助制定处理策略;PP-OCRv6 更适合作为轻量、稳定的文本识别主干,经过针对性微调后承接在线识别任务。

这里有一个很重要的取舍:线上服务追求的是稳定、响应速度和可解释的失败处理,不是把所有逻辑都塞进一个大模型里。因此最终的服务形态更偏工程化:模型负责识别,外围流程负责把输入整理到模型更擅长处理的状态。

三、第一阶段:从通用 OCR 到定制识别

刚开始测试时,我分别尝试过通用 OCR、视觉语言模型和已有验证码识别方案。通用 OCR 在清晰图片上表现不错,但一遇到颜色提示、干扰字符和背景混色,结果就开始飘。

典型问题包括:

  • 识别出了非目标颜色的字符;
  • 把背景纹理或干扰线当成文字;
  • 字符大小写不稳定;
  • 相似字符容易混淆;
  • 单个字符置信度很低,但整体平均分看起来还可以;
  • 某些字体下会出现漏字或多字。

这些问题说明,仅仅看最终识别文本是不够的,还要关注模型在每个字符上的稳定性、目标区域是否干净,以及同一类失败是否反复出现。

因此我把识别过程拆成几个相对独立的环节:

  • 颜色提示解析;
  • 图片规范化;
  • 指定目标内容提取;
  • OCR 识别;
  • 结果后处理;
  • 失败样例分类。

拆开之后,每个环节都可以单独评估。比如一张图识别错了,先判断是目标内容没有处理干净,还是 OCR 模型本身误判,或者是业务提示解析出现偏差。这样后续调试会清楚很多。

四、第二阶段:模型选择与训练策略

模型部分主要围绕 PaddleOCR-VL 和 PP-OCR系列 展开。

PaddleOCR-VL 的价值在于它对复杂文档和图像元素有更强的理解能力,适合用于分析样本结构、观察不同验证码形态,并帮助判断问题到底来自图像本身还是识别链路。它不是本文线上服务的唯一核心,但在方案验证阶段提供了很多帮助。

PP-OCR 则更适合落到实际识别服务里。它的文本识别能力、部署方式和推理效率都比较适合做在线服务。实践中,我没有直接使用原始模型“一把梭”,而是基于业务样本做了针对性训练和评估,让模型更熟悉当前验证码里的字符形态、字体变化和干扰模式。

由于基础模型都是开源的,没有特殊点,这里不展开训练集构造、字符表、增强方式、训练参数和模型转换过程,只记录几个比较有价值的经验:

  • 样本质量比样本数量更重要。低质量标签会把模型带偏,尤其是相似字符和大小写问题。
  • 失败样例要单独管理。把所有样本混在一起训练,很容易看不到真正影响线上效果的长尾问题。
  • 评估集要贴近线上分布。只看随机测试集准确率,不能代表真实服务稳定性。
  • 颜色、字体、长度和干扰类型要分维度观察。总体准确率上升,不代表每一类样本都变好了。
  • 模型版本要可追溯。每次训练、转换、评估和上线都要能对应到明确版本。

训练完成后,我把模型转换成更适合服务部署的形式,减少线上环境对训练框架的依赖。这样服务启动更轻,部署也更可控。

五、第三阶段:服务化改造

模型可用之后,真正麻烦的地方才开始:如何让它稳定地跑在服务里。

一个可用的验证码识别服务,不能只提供“传图片、返回文本”这么简单。它还需要处理异常输入、颜色提示缺失、图片格式异常、模型加载失败、置信度过低、识别为空、耗时过长等情况。

我把服务接口设计成比较克制的形式:业务侧提交图片和必要的上下文信息,服务侧返回识别文本、目标颜色、置信度、耗时和状态信息。上层业务可以根据状态决定是否继续、重试或进入人工处理。

输出结果:

{ 'code': 200, 'skipped': False, 'input_path': 'val_data/标注后_检查后0528/宇羊UA_黑色on2an9w11a.png', 'processed_path': None, 'target_color': '黑色', 'rec_text': '宇羊UA', 'rec_score': 0.9386486262083054, 'message': '识别完成' } { 'code': 200, 'skipped': False, 'input_path': 'val_data/标注后_检查后0528/彤字沙RY_黑色nuwmyn6oho.png', 'processed_path': None, 'target_color': '黑色', 'rec_text': '彤字沙RY', 'rec_score': 0.9984363555908203, 'message': '识别完成' } { 'code': 200, 'skipped': False, 'input_path': 'val_data/val12/巨帙EQ_黑色73f7ddf9e3.png', 'processed_path': None, 'target_color': '黑色', 'rec_text': '巨帙EQ', 'rec_score': 0.9948489367961884, 'message': '识别完成' }

当然也有错误案例:

{ 'code': 200, 'skipped': False, 'input_path': 'val_data/val12/3EUF3T_黑色84ec6a4288.png', 'processed_path': None, 'target_color': '黑色', 'rec_text': '3毛UF3T', 'rec_score': 0.9205710689226786, 'message': '识别完成' } { 'code': 200, 'skipped': False, 'input_path': 'val_data/val12/页Bp35E_黑色797c6e9cdf.png', 'processed_path': None, 'target_color': '黑色', 'rec_text': '页8P35E', 'rec_score': 0.8862185974915823, 'message': '识别完成' }

线上流程大致如下:

服务化时重点做了几件事:

  • 模型启动时加载,避免每次请求重复初始化;
  • 临时文件使用隔离目录,识别完成后及时清理;
  • 返回结构保持稳定,方便业务侧接入;
  • 异常信息分级记录,避免把敏感图片和请求细节直接打进日志;
  • 对低置信度结果做保守处理,不强行返回看似可用的答案;
  • 离线评估脚本和在线服务共用同一套核心识别逻辑,减少“测试好、上线差”的情况。

这一步的关键不是把接口写复杂,而是让每一个失败都能被定位。验证码识别服务最怕“偶尔错一下但不知道为什么错”。只要失败能被归类、能复现、能回流,模型就可以持续变好。

六、第四阶段:失败案例闭环

这类任务的迭代过程很像做一个小型数据飞轮:线上遇到失败,离线还原问题,人工确认标签,再进入下一轮训练和评估。

我把失败样例大致分成几类:

  • 低置信度样例;
  • 相似字符混淆;
  • 目标字符缺失;
  • 非目标内容误识别;
  • 字体变化明显的样例;
  • 背景或干扰特别重的样例;
  • 提示信息异常或颜色无法判断的样例。

分类之后,训练就不再是盲目加数据,而是针对问题补数据。比如某一类字符经常混淆,就重点看这类字符的样本是否足够、标签是否统一、评估集中是否覆盖;某一类颜色容易漏字,就回到图像处理和样本分布上检查。

为了你好:不要只盯平均准确率。验证码识别的线上体验常常取决于最难的那一小撮样例。平均分很漂亮,但如果某类关键场景反复失败,服务依然不可靠。

所以最终评估我更关注:

  • 不同颜色下的识别稳定性;
  • 不同长度验证码的结果分布;
  • 单字符最低置信度;
  • 错误样例是否集中在少数字符;
  • 新版本相比旧版本是否引入新的退化;
  • 服务耗时是否满足业务要求。

这些指标比单个“准确率数字”更有指导意义。

七、踩坑总结

这次实践里印象最深的几个坑:

第一,通用 OCR 不等于业务可用。模型在普通文字识别上表现好,不代表它能理解“只识别某一种颜色”的业务意图。

第二,预处理不是越强越好。处理过度会让字符笔画断裂,处理不足又会留下干扰信息。好的处理方式应该服务于模型,而不是追求视觉上看起来很干净。

第三,标签一致性非常重要。大小写、相似字符、中文字符和异常样例如果没有统一规范,训练效果会变得很不稳定。

第四,失败样例比成功样例更值钱。成功样例证明服务能跑,失败样例告诉你下一步该往哪里优化。

第五,线上服务要保守。低置信度结果宁愿交给上层重试或人工处理,也不要为了“看起来自动化”而强行通过。

八、结果评估

经过多轮训练、评估和失败样例回流后,服务已经可以比较稳定地处理指定颜色验证码识别任务。当前版本主要覆盖常见颜色提示和常见字符组合,能够返回识别文本、目标颜色、置信度和状态信息,也能把异常情况纳入后续分析。

在内部评测口径下,不同模型路线和字体场景的表现如下。这里的“镂空字体”和“实心字体”使用两组对应验证集评估:原图用于 PP-OCR 服务链路复测,黑白处理图用于 PaddleOCR-VL/多模态路线评估。

方案字体场景准确率说明
PP-OCR 定制识别链路镂空字体约 96%在已有字体样式下整体表现稳定,适合作为轻量服务链路
PP-OCRv6 定制识别链路镂空字体约 86%新字体带来了明显的字体迁移问题,需要继续补充样例和做针对性迭代
PaddleOCR-VL实心字体95%+基于黑白处理图评估,两种字体场景均达到 95% 以上
PaddleOCR-VL实心字体95%+泛化稳定性更好,但线上部署成本和响应耗时需要综合权衡

从这个对比可以看出,PP-OCR 定制链路在原有字体上准确率更高,但遇到新字体时会出现一定退化;多模态模型路线在两种字体下都能达到 95% 以上,泛化稳定性更好。后续如果继续迭代,可以把 PP-OCRv6 的轻量部署优势和多模态模型的泛化能力结合起来,让线上服务在速度和准确率之间取得更好的平衡。

本地单进程 QPS 测试口径如下:

  • 测试样本:来自未标注数据中随机抽取1000张和600张,然后进行人工标注制作;
  • 测试方式:每个模型预热 10 张,正式计时 1 轮;
  • 测试范围:原图进入识别脚本,包含颜色目标处理、OCR 推理和结果解码;
  • 并发方式:单进程串行调用,不代表多进程或服务网关并发上限。
验证集脚本/方案统一大小写准确率字符级准确率QPS平均耗时P95 耗时结论
镂空字体原图captcha_recognizer_v3_onnx.py95.36%98.26%24.0241.63 ms57.14 ms原有字体下稳定,可作为旧版本基线
镂空字体原图captcha_recognizer_v6_onnx.py94.29%97.95%39.5725.27 ms35.40 ms吞吐更高,综合部署价值更好
实心字体原图captcha_recognizer_v3_onnx.py1.31%13.60%23.7342.14 ms52.47 ms对新字体基本不适配,不建议继续作为新字体主链路
实心字体原图captcha_recognizer_v6_onnx.py86.07%94.21%24.4340.93 ms52.58 ms新字体场景可用,但仍有继续提升空间
当前环境captcha_recognizer_v3.py-----PaddleOCR/Paddle 推理初始化失败,报libpaddle相关错误,需要修复环境后重测
当前环境val_merged.py95%+----多模态路线在两种字体上准确率均达到 95% 以上;当前 Python 环境缺少torch,QPS 需在部署机补测

从吞吐结果看,ONNX 化后的 PP-OCRv6 识别链路更适合当前在线部署:在原有字体和新字体验证集上,它的速度和泛化表现更均衡。Paddle 原生版本和多模态验证脚本不是不能用,而是当前机器环境没有满足完整压测条件,因此不把它们写成确定 QPS,避免误导。

从工程角度看,这个项目最大的收获不是某一次模型准确率提升,而是建立了一套可持续迭代的流程:

  • 有数据入口;
  • 有模型训练;
  • 有服务部署;
  • 有在线记录;
  • 有失败回流;
  • 有版本评估。

只要这个闭环存在,后续遇到新的字体、新的颜色、新的干扰形态,都可以按同一套流程继续迭代。

九、结语

整体来看,不难,就是标注数据非常麻烦,网上相关资料比较少,也没什么特别的,都是真实数据撑起的准确率,其他的也没有见到非常亮点的模型。。。

另外也客气一下吧:指定颜色验证码识别看起来是一个很小的 OCR 问题,但真正落地时会牵涉到图像处理、模型训练、服务部署、日志脱敏、失败样例管理和业务兜底。单点能力并不难,难的是把它做成一个稳定、可维护、可解释的服务。

参考

  • PaddleOCR 官方项目:https://github.com/PaddlePaddle/PaddleOCR
  • 非常感谢前期数据标注项目:ddddocr项目
  • 任务初期文章参考:【2020.06】国税总局发票查验平台验证码最新获取方法_自动报税 手机验证码怎么获取-CSDN博客
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 3:08:48

Deno入门到实战:核心特性、权限模型与本地文件API服务开发指南

之前在业务迭代中切换到 Deno 做内部工具时,最深的感受是:Node.js 十余年积累下来的生态很丰富,但工程体验里“权限边界模糊、依赖管理冗杂、TypeScript 配置成本高”这些问题一直没被根本性解决。Deno 的出现补上了这块短板,尤其…

作者头像 李华
网站建设 2026/9/3 14:27:03

基于鱼鹰优化算法(OOA)的BP神经网络初始权值优化与回归预测

简介:本资源面向机器学习初学者与Matlab建模实践者,提供一种融合新型元启发式算法的BP神经网络回归预测完整实现方案,适用于多输入单输出的工程预测、数据分析等实际场景。压缩包共6个文件(4个核心m脚本、1个Excel数据集、1个备份…

作者头像 李华
网站建设 2026/9/3 3:15:30

1/8砖400W DC-DC与PMBus数字电源管理实战解析

一块不到六厘米长、两指宽的金属基板,输入36V到75V,输出12V/400W,还能用两根信号线实时读电压、电流、温度、故障状态,甚至在线修改输出电压——这就是我最近在48V母线项目里用的一款1/8砖DC-DC转换器。电源圈里做板卡的工程师&am…

作者头像 李华
网站建设 2026/9/2 19:50:37

物理AI核心技术解析:从VLM、VLA到WAM的数学原理与工程实践

物理AI这个概念正在以极强的势头冲进大众视野。无论是能叠衣服的机械臂,还是能在仓库里自主搬箱的移动机器人,背后都绕不开三个缩写:VLM、VLA、WAM。很多人看到这些词的第一反应是“又一个新名词”,但物理AI真正难的地方不是名词&…

作者头像 李华
网站建设 2026/9/3 1:12:37

TechNist实战解析 手写中文数字图像分类项目怎么做

TechNist 这道 Kaggle 竞赛,表面上是经典手写数字识别的变体,实际更接近中文场景下的轻量级视觉分类练习。任务目标很明确:基于约 1.2 万张手写中文数字图片,完成 15 个类别的分类预测,并按竞赛要求生成提交结果。 这…

作者头像 李华
网站建设 2026/9/4 8:26:23

从零实现开发者贡献识别系统:多维事件模型与代码实战

开发团队在衡量成员贡献时,通常会把“代码提交量”“PR 数量”“解决 Issue 数”当作最直观的数据。但在实际业务迭代中,这种单一维度的评估方式常常引发争议:有人写了很多代码但大部分在返工,有人一直在帮助团队做 Code Review 却…

作者头像 李华