首页 / 资讯中心 / 文章详情

多合一图像识别系统实践:从OCR到万物分类的架构与部署

多合一图像识别系统实践:从OCR到万物分类的架构与部署 ★ FEATURED ARTICLE
最近有个朋友找我聊需求说他们公司内部想上一套图像识别能力要涵盖银行卡、身份证、营业执照、车牌、驾驶证、行驶证、票据这些证件票据还要能认植物、动物、车型、Logo。一开始我想的是这些东西各有各的成熟方案挨个找接口对接不就行了。但越往下谈越发现不对十几类识别如果每一个都单独部署、单独维护、单独出文档那后期就是一场灾难而且很多场景根本没法提前固定我这回只会传身份证图片进来先得知道该走哪条通道。所以我最终做的不是一堆识别脚本而是一套多合一识别系统一个入口进来内部按功能路由到对应模型最后统一输出结构化结果。这套系统覆盖11种识别功能包括证件票据类和万物分类类两大方向。这篇文章把整个项目从需求拆解、技术选型、模型训练到部署排坑的完整过程记录下来适合正在做OCR或多场景图像识别的开发者参考也适合需要做技术选型、但又不想被每个单点方案的供应商绑死的朋友们看。1. 项目概述与整体设计思路1.1 11种识别功能背后的三类技术问题项目标题里的11项功能看着很多但如果把它们拆开归类本质上是三类完全不同的技术问题。第一类是标准OCR类。身份证、银行卡、营业执照、通用票据、驾驶证、行驶证这几项的核心都是文字识别最终输出的是结构化字段。比如身份证要出姓名、性别、民族、住址、身份证号营业执照要出统一社会信用代码、企业名称、法定代表人等。这一类的问题不在于能不能识别文字而在于怎么把文字准确映射到字段。第二类是检测加识别类。车牌号、车型就是典型。车牌要处理的是先定位车牌区域再对区域里的字符做识别。这里难点不在字符本身而在车牌的定位、倾斜校正、颜色信息蓝牌、黄牌、绿牌、白牌。车型则是细粒度分类问题先定位车辆区域再判断具体是什么品牌什么款型比如奥迪A6L 2020款和奥迪A4L 2020款之间的差别非常细微。第三类是纯分类类。植物、动物、Logo这三项本质上就是图像分类图片进来输出一个类别标签。植物要能区分到科属种动物要能区分常见物种Logo要能区分品牌标识。分类模型的难点主要集中在数据多样性和特征粒度上。把11个功能归成三类之后技术路线就清楚了OCR那一类共享一套检测和识别的底座车辆相关走目标检测加细粒度分类路线万物分类走图像分类路线。这个划分直接决定了后面的模型选型也决定了整个系统的架构。1.2 为什么不做成11个独立脚本一个很自然的想法是身份证识别成熟方案多我单独接一个车牌识别也有现成接口单独接一个植物识别再单独搞一个每个功能各管各的互不影响。表面上看起来清晰实际用起来问题一大堆。首先是资源浪费。11个模型如果单独部署至少11个推理服务每个都得开内存、开显存、做运维监控。真正跑起来的时候大部分服务可能都在空转。多合一之后模型可以共享特征提取底座共用一套推理框架只要一台带GPU的机器就能承载大部分负载。其次是调用方的体验。用户不会关心你背后是哪个OCR引擎还是哪个分类框架他们只想知道传一张图片进去返回一个JSON。如果11个功能走11个不同的API调用方要维护11套鉴权、11套参数格式、11套错误码这对下游业务来说就是一场灾难。我当时的设计是对外暴露一个统一接口比如POST /recognize请求参数里带biz_type业务类型和image图片内部根据业务类型路由到不同模型最后统一返回{code, message, data}结构。用户只需要关心业务类型选没选对剩下的全部由系统内部消化。这才是多合一的价值所在。1.3 系统整体架构的划分整个系统我从逻辑上分成四层。接入层负责接收图片、做基础校验大小、格式、清晰度同时做图片预处理比如把EXIF旋转信息扶正、转换颜色空间、统一分辨率范围。路由层根据请求里带的业务类型将图片分发给对应识别链路。这一步逻辑很简单但特别重要因为一旦路由错了后面再好的模型也白搭。我在设计时额外加了一个兜底策略如果前端没法确定业务类型可以走自动路由。自动路由用一个轻量级分类模型先判断图片属于证件票据类还是车辆类还是万物分类类再进入对应的细分链路。识别层是核心包含文本检测、方向分类、文本识别、目标检测、细粒度分类、通用分类等模型。不同链路可以并行也可以串行比如车型识别链路需要先跑目标检测框出车辆位置再跑分类模型。输出层统一进行字段格式化、置信度过滤、后处理校正。比如身份证号有校验位规则营业执照统一社会信用代码有固定位数这些都可以在后处理做二次校验识别错了还能想办法纠回来。2. 技术选型与核心原理2.1 OCR类功能的技术路线检测、方向分类、文本识别三段式OCR类的功能我没有选端到端的模型而是采用了业内最稳定的三段式结构文本检测、方向分类、文本识别。第一步文本检测是在整张图上找出哪里有文字输出文本框的位置和角度。比如身份证上各个字段的位置基本固定但检测这一步仍然不能省因为你不能假设拍照角度永远端正。检测框质量的优劣直接影响后面识别的准确率检测框偏了文字内容就容易被截断。第二步方向分类解决的是文字转了多少度的问题。手机拍照经常出来横着的图、倒着的图。如果图片旋转了90度或180度识别引擎基本必挂。方向分类器是一个很轻量的分类模型只负责判断当前文字区域是否需要旋转以及旋转多少度。一般分为0度、90度、180度、270度四类。这一步看起来多余但对于证件和票据场景是保命的一环。第三步文本识别对校正后的文字区域做序列识别输出具体的文字内容。业界主流做法是基于注意力机制的序列模型把文字区域的图像特征解码成字符序列。这一步输出的是一串字符串之后再由后处理逻辑把字符串映射到对应字段比如身份证号位置附近的识别结果就赋给身份证号字段。三段式最大的优点是每一段都可以单独调优。检测不准就强化检测数据识别不准就强化识别数据不用整个模型重新训练。我实际做下来90%的OCR问题都出在数据上而不是模型结构上三段式结构能让你把数据问题精确锁定到某一环。2.2 检测类功能的技术路线目标检测加序列识别车牌识别不能直接对整张图做OCR原因很简单车牌在整个画面里通常只占很小一块区域而且字符排列紧凑背景干扰多。直接整图识别模型会把精力浪费在无关区域上。所以车牌识别链路是先用目标检测模型在整图里框出车牌位置然后把车牌区域裁切出来再做倾斜校正、颜色分类、字符识别。这里的字符识别原理和OCR差不多但字符集是限定死的省份简称、字母、数字。限定字符集的好处是识别模型可以做得更准不容易把0识别成O把1识别成I。车型识别则是另一个思路。目标检测先把车辆从背景中分离出来然后送到一个细粒度分类模型。车型的难点在于同品牌不同款型的差异非常细微以及不同拍摄角度下的同款车外观差异可能很大这需要分类模型有很强的特征提取能力。我用的方案是在检测模型之后接一个专门做车辆细粒度分类的模型数据集中包含几百个常见车型类别再通过数据增强模拟不同角度、不同光照、不同遮挡下的情况。2.3 万物分类类功能分类模型与数据增强植物、动物、Logo这三项任务从模型角度都是分类问题但实际难点很不一样。植物识别依赖叶片形状、花朵颜色、纹理脉络这些细节特征而且不同植物在野外环境下拍摄的背景差异极大。如果模型只学了单一背景下的特征换个环境就认不出来。动物识别也类似但动物的姿态变化比植物更夸张同一种动物趴着、站着、跑着外观差异可能比不同动物还大。Logo识别则完全依赖图形特征和色彩特征有些Logo字符少、纯图形有些Logo是文字加图形的组合不同来源的Logo图片质量和风格也差异巨大。我对这三类功能的处理方式不是各搞一个大模型而是共用一个分类底座在底座之上按业务需求微调出三个分支模型。共用底座的好处是部署时只需保留一套特征提取网络三个分支头参数很小整体占用的资源和推理耗时都很可控。2.4 为什么选择自研而不是直接调用收费接口做之前我也评估过直接买第三方接口证件类、车牌类识别非常成熟准确率高按次收费接入也快。但最后没选这条路有几个原因。第一是数据合规问题。身份证、银行卡、营业执照这些证照信息属于敏感程度很高的数据走第三方接口意味着图片要出域在很多业务场景下是无法满足合规要求的。第二是成本问题。按次收费看着便宜但量一大就变成固定支出而且国内证件识别业务的调用频率往往集中在白天业务高峰期一年下来这笔费用相当可观。自研部署在自己的服务器上边际成本趋近于零。第三是定制化问题。第三方接口的字段格式是固定的返回什么你就得接什么。自研的话字段可以随便扩展比如营业执照我要额外识别成立日期经营范围票据我要按自定义模板提取字段这些第三方接口很难做到。当然自研这条路也不是白给的需要有人力做数据标注、模型训练、持续迭代。我的建议是对识别准确率要求极高、或者数据量特别大的场景自研是划算的如果只是内部小范围试水还是买接口更务实。3. 11种识别功能逐一拆解3.1 证件票据类身份证、银行卡、营业执照、通用票据、驾驶证、行驶证身份证识别是所有证件识别里需求最刚性的一项。关键字段是姓名、性别、民族、出生日期、住址、身份证号码。身份证有国徽面和个人信息面两面客户传上来的照片可能是任意一面所以路由层要加一个朝向判断先判别国徽面还是人像面再走对应字段模板。身份证号码有严格的编码规则前6位地区码、中间8位出生日期、第17位性别位、最后一位校验位我拿这些规则做了后处理校验识别出明显不合法的身份证号就主动打回重试比让下游业务系统报错要友好得多。银行卡识别核心是卡号、发卡行、有效期和持卡人姓名如果有。卡号通常是凸印或平印的16到19位数字位置不确定所以同样要先检测再识别。银行卡卡号有Luhn算法校验Luhn不过的卡号可以直接判定识别失败。很多银行卡卡片背景花纹复杂干扰多我用专门的卡号检测模型先定位卡号区域再做裁切识别实测比整图OCR准确率高不少。营业执照识别的关键是统一社会信用代码。这个代码是18位由登记管理部门代码、机构类别代码、行政区划码、主体标识码和校验码组成。18位数字加字母的结构让后处理校验非常方便。除此之外还需要识别企业名称、法定代表人、经营范围、注册资本、成立日期、营业期限等这些字段位置不固定全靠文本检测定位后按语义归类。通用票据识别是最开放的一类因为票据格式五花八门有定额发票、增值税发票、小票、收据。通用方案是先做版面分析把票据上的文字区域按位置关系聚类成票头、主体表格、金额区、二维码区再对金额、日期、发票号码这类关键词做模式匹配。完全识别任意票据是不现实的我做了两层默认走通用OCR提取全量文本再根据客户需要配置几个常用字段模板做定向抽取。驾驶证识别主要字段是证号、姓名、准驾车型、有效期限。驾驶证正页和副页字段不同副页还有一个条形码条形码区域可以单独检测后尝试解码解码出来的证号能大幅提高整体识别准确率。行驶证识别和驾驶证结构不同行驶证首页是车辆照片加车牌号副页是各种参数号牌号码、车辆类型、所有人、住址、使用性质、品牌型号、车辆识别代号、发动机号码、注册日期、发证日期。其中品牌型号经常是大众牌FV7187这种带生僻字和数字的组合容易误识别需要做专门的词典纠错。3.2 车辆类车牌号、车型车牌号识别在国内场景下字符集相对固定省份简称不含港澳台约30个、字母和数字。车牌颜色是一个极强的先验信息蓝牌普通燃油车、绿牌新能源、黄牌大型车、白牌特种车、黑牌涉外。颜色分类在检测阶段顺带做了因为检测模型输出的车牌区域可以直接取中心像素判断颜色。车牌识别最头疼的是倾斜和模糊。裁切出来的车牌区域往往不是正的需要根据文本框四角坐标做仿射变换校正。遇到雨天、泥点、晚上灯光反射字符区域会出现粘连和残缺这些情况靠模型本身难以完全解决我在这块配合了基于字符间隔的先验做分割修复效果能提升几个点。车型识别针对的是汽车品牌和型号。常见车型数据库大概覆盖两千多个款式但实际业务通常只关心几百个热门款。识别链路是检测车辆位置裁切车身区域送入车型细粒度分类模型。车型分类和车牌识别经常是配合使用的一个出牌号一个出车辆品牌型号可以用品牌信息交叉验证比如识别结果是绿牌新能源但车型分类出了燃油车版本那肯定有一路出错了可以触发重新推理。3.3 万物分类类植物、动物、Logo植物识别我做了两类支持常见园林植物和室内绿植。模型输出的类别包含科属种三层信息比如天南星科-龟背竹属-龟背竹。单张图片可能同时包含多株植物这种情况分类模型只能输出最显著的类别。我做了一个简单策略如果检测模块发现有多个明显的植物主体区域就先切分再分别分类这样一张合影图也能出多个结果。动物识别的类别数量我控制在常见物种范围内大概是几十种猫科、犬科、鸟类、水生生物等。动物识别的关键在于姿态变化大一只狗的正面、侧面、背面可能看起来像不同物种。我引入了多视角数据增强把每张训练图片做随机旋转、翻转、裁剪模拟不同视角让模型学到不管从哪个角度拍它都是这只狗。Logo识别是这里面工程上最琐碎的一项。Logo的来源五花八门有网站截图、商品实拍、广告牌、名片清晰度差异极大Logo本身还可能被扭曲、被部分遮挡。我的处理策略是收集尽可能多的高清Logo模板然后用模板匹配的方式生成大批合成训练数据把贴图合成到各种真实背景上模型见多了不同场景下渲染的Logo泛化能力自然就上来了。3.4 多合一系统中的共用组件除了各功能自己的模型链路系统里还有几个共用组件。图像方向校正模块会对所有输入图片做EXIF旋转修正因为很多手机拍出来的照片实际方向和显示方向不一致不提前校正后面所有模型都会跑偏。清晰度评估模块判断图片是否模糊、是否过曝置信度过低就直接拒绝识别而不是硬着头皮返回一个错误结果。这两个通用模块成本很低但能整体抬高所有功能的上限。4. 实操过程从数据到可用的完整搭建流程4.1 数据准备公开数据集、自采数据与数据增强所有识别模型数据都是最硬的瓶颈。我建数据的思路是公开数据集打底自采数据补长尾。公开数据集里车牌、车型、通用OCR类相对丰富植物和Logo也有现成资源但证件类身份证、银行卡、营业执照的公开数据非常少而且涉及隐私基本只能靠自采和合成。我做了三类数据补充第一是找业务方收集脱敏后的真实样本第二是把真实证照模板和背景随机合成通过变换光照、透视、角度模拟实拍效果第三是纯合成用字体渲染工具生成无真实信息的模拟证件专门用来扩充字符覆盖率。数据增强是所有模型通用的关键一环。下面这段代码展示了我对图片做的常见增强组合包含随机旋转、透视变换、亮度扰动、加噪声和模糊这些操作用来模拟真实拍照场景中的各种劣化import cv2 import numpy as np import imgaug.augmenters as iaa seq iaa.Sequential([ iaa.Affine( rotate(-10, 10), perspective0.05, translate_percent{x: (-0.05, 0.05), y: (-0.05, 0.05)} ), iaa.Multiply((0.7, 1.3)), # 亮度变化 iaa.AdditiveGaussianNoise(scale(0, 0.03 * 255)), # 高斯噪声 iaa.GaussianBlur(sigma(0.0, 1.2)) # 轻微模糊 ], random_orderTrue) def augment_image(img, label): img_aug seq(imageimg) return img_aug, label具体到不同类型的任务数据增强策略略有侧重。证件类需要更多的透视变换和光照扰动因为实拍证件经常是斜着拍的车牌类需要模拟运动模糊和夜间低照度植物动物类则更依赖随机裁剪和色彩抖动。增强不是越多越好过于激进的增强反而会把模型的训练方向带歪这一点需要自己多试几轮感觉一下。4.2 模型训练与评估要点OCR类模型我采用两阶段训练策略先在大规模通用文本数据上做预训练再用业务数据做微调。通用数据让模型学会文字长什么样业务数据让模型学会你的场景里文字长什么样。两阶段的好处是业务数据量不需要特别大就能达到不错的效果几百张高质量标注样本足以让识别率有明显提升。车牌识别模型相比之下更轻量输入是裁切后的车牌区域字符集固定在几十个字符以内所以模型规模不用太大。我用的训练批大小是64学习率初始为1e-3配合余弦衰减整体训练时间很短。车型和万物分类模型走的是标准的图像分类训练流程。这里有一个关键点类别不均衡问题非常严重。有些热门车型样本几百张冷门车型可能只有几张。我用重采样加类别权重两种方式同时处理让模型不会为了省事把所有样本都预测成高频类别。评估指标不能只看整体准确率。识别类的指标我拆成了字段级准确率和样本级准确率身份证识别字段级F1更有意义因为偶尔一个姓名识别错误可能比十个地址识别错误都严重。分类类的指标用Top-1和Top-5准确率车型和植物识别场景Top-5指标特别重要因为细粒度分类本身存在训练标签和拍摄外观不一致的情况给出Top-5候选让业务方二次选择比硬给一个结果更实用。4.3 系统集成与推理加速模型训练好之后面临的是部署问题。我的部署方案是先做模型转换压缩推理效率再封装成微服务。模型转换的核心工具是ONNX和对应的推理引擎这一步能把模型的推理速度提升数倍而且减少运行时依赖。转换时有一个容易踩的坑原始框架里的动态shape和自定义算子不一定兼容需要做固定shape或者重写部分算子。服务端我选择的是支持并发请求的异步Web框架所有识别接口暴露为统一的API。请求进来先做图片解码和预处理再根据业务类型分发到对应模型的推理队列。多路模型共用同一个GPU资源我通过对每个模型设置独立的工作线程数和最大批处理大小来控制资源占用避免某个大流量模型把整张卡占死。整个集成环节还有一个不起眼但存储开销很大的点图片的临时存储。我在预处理阶段把图片以WebP格式压缩缓存识别完成后自动清理避免线上跑几个月硬盘被临时文件塞满。这类细节不做的时候没人提做了之后维护省心得多。5. 常见问题与排查技巧实录5.1 识别结果不稳定的排查思路多合一系统上线后最常被问的问题就是为什么这张图识别对了另一张同类型的图就识别错了。排查这类问题我的固定套路是三步走。第一步复现把失败图片单独拿出来重新跑一遍看是哪一链路出的问题。OCR类就看是检测框偏了还是文字识别错了检测类就看是目标没框出来还是框出来了分类错了。这一步能定位出大概的环节。第二步分析图片特征。把失败图片和成功图片对比看差异出在什么地方。是光线差异、拍摄角度、图片模糊、还是目标太小三类最典型的劣化因素对应不同的处理手段模糊图用更鲁棒的增强策略角度问题需要补充更多透视样本目标太小则需要考虑使用超分模型做预处理。第三步定向补数据把失败样本作为困难样本挖掘出来加入训练集重新微调一轮。我发现均值难样本挖掘这个策略非常有效每一轮模型迭代后把预测置信度在0.4到0.7之间的样本挑出来重新标注再训练几轮之后准确率能肉眼可见地往上走。5.2 证件类常见的识别错误证件类最常见的问题有两个反光和遮挡。身份证过塑膜反光、银行卡卡面反光、行驶证上的水印干扰这些都会让字符区域出现高光斑块导致识别结果缺字或错字。我做了两层处理图像层面引入反光区域检测检测到高光就把该区域的对比度做均衡化把被反光吃掉的字符轮廓尽量显现出来模型层面在训练时用模拟反光的合成样本增强模型本身的抗干扰能力。第二个问题是生僻字。身份证、营业执照里出现的生僻字和繁体字公开预训练模型覆盖不到识别出来全是乱码。我的做法是建立业务专用词典在识别后处理阶段做一个约束解码识别模型输出的候选序列如果包含词典中的词优先保留如果出现看起来合理但在词典中不存在的字符组合触发二次校对。这种方法对证件类字段纠错效果明显。5.3 性能瓶颈与优化方向多合一系统在GPU上稳定运行后遇到的最大瓶颈是并发。一开始每个模型独立动态加载并发一高就出现CUDA显存溢出。后来我把所有模型统一管理在服务启动时一次性加载进显存配合按优先级调度显存占用反而降下来了。另一个性能问题是OCR类的检测、方向分类、识别三段是串行的一张大图识别下来耗时偏高。优化方向有两个第一是前处理时限制最长边把图片缩放到模型能接受的最大分辨率内减少无效像素计算第二是做批处理把同类型的多个请求合并成一个批次并行推理整体吞吐提升明显。实测在单张GPU卡上证件类识别的单张平均耗时可以从一秒钟以上压到两百毫秒左右这个量级在大量业务场景中已经够用了。5.4 常见问题速查表问题现象可能原因解决方案图片模糊识别结果乱码输入分辨率过低增加清晰度评估拒绝或提示重拍训练时加入模糊增强证件识别漏字段文本检测框偏了检查检测模型补标注边框样本增大检测框扩展比例车牌字符搞混数字1和字母I、数字0和O混淆限定字符集增加同类易混淆字符的样本均衡车型识别置信度低拍摄角度过于刁钻接入多视角增强开启Top-5候选输出植物识别多株植物只能出一个结果单分类模型只能选最显著类别增加主体检测模块检测多个主体后分别分类反光区域识别错误高光覆盖字符反光检测配合对比度均衡补充模拟反光训练数据并发一高就OOM多模型同时加载无调度模型统一生命周期管理启动时预加载必要时排队推理方向不对导致识别全乱未做方向分类检查方向分类器是否启用确认输入图的EXIF已修正6. 一些踩坑后的体会整个项目做下来我最深的体会是一个多合一识别系统真正难的不是单个模型的准确率而是把十一个模型的链路串起来之后系统依然稳定可靠。识别准确率可以从模型和数据层面去补但系统稳定性、字段校验、错误兜底这些工程细节一旦没做好整个系统随时都有可能崩在你面前。有几个经验想分享给大家。第一路由层一定要做兜底业务方传错业务类型是必然事件不是偶然事件没有兜底路由就会出现身份证图片被送到车牌模型里这种低级错误。第二统一输出结构看起来只是小事实际是下游接入体验的核心字段名、错误码、置信度阈值这些规范越早定越好。第三所有模型都要保留版本管理能力某个模型更新后如果效果反而变差要能一键回滚。不然线上出了问题你连到底是谁改坏的都查不出来。后续这个系统还可以继续扩展。方向上我可以想到三个一是把端点侧部署做起来让识别能力跑在客户自己的设备上减少服务器压力二是引入多模态大模型做模糊字段的语义纠错比如上下文推断被遮挡的字段内容三是把现在固定类型的分类升级成开放集识别遇到没见过的类别也能给出一个未知类的提示而不是硬猜一个答案。这些方向我目前都在逐步验证等有阶段性成果了再写出来分享。
阅读完成 · 觉得有帮助?
咨询建站