工业踩坑实录(十七):从40分到高分:工业零件OCR,通用模型一上来就给我打脸
从40分到高分工业零件OCR通用模型一上来就给我打脸工业零件上印一行字你以为直接丢给OCR就能认。现实是通用模型跑上去准确率四十来分跟瞎猜差不多。2026-05-08 更新发这篇文章之前收到一条私信提醒说公众号「开麦视觉AI」有篇文章跟本系列的内容高度相似。我去看了下第一性原理那篇、SOP状态机那两篇核心框架、技术路线、案例细节都跟我的文章对得上还标了原创。感谢提醒。说明这些内容还是被认可的有人愿意花时间读、花时间写本身就是一种肯定。不过还是想友情提醒一下借鉴可以标原创不太合适。创作不易互相尊重。关于作者我接触视觉整整10年。工业视觉、烟草、煤矿等行业都有深度开发经验。从硬件选型、算法开发、模型训练到上位机开发及部署都在一线磨过。之前是多家公司人工智能团队的技术负责人。现在自己创业了还在继续做视觉落地这件事。作者说这个故事发生在学校。当时有个比赛任务是识别工业零件上的文字。零件上印着型号、批次号、生产日期、参数规格这些信息要求算法自动识别出来跟标准答案比对打分。这活儿我一看就觉得简单。OCR嘛成熟的很开源框架一大堆直接调接口跑不就完了。第一次提交得分四十来分。四十来分是什么概念一百个零件六十个认错或者认不出来。你以为你在做OCR实际上你在参加一个比随机猜测强多少的测试。这个成绩把我打醒了。我花了一个多星期折腾从通用OCR调参到自己搭一套检测校正识别的流水线最后把分数提上去了。不是我算法多牛是我终于理解了——工业零件上的OCR跟文档OCR根本不是同一个问题。回头看这段经历对我后来的工作影响很大。后来做工业项目里但凡涉及OCR的场景我第一反应都是先定位再识别再也不敢直接把整张图丢给OCR了。这是第十七篇。开场工业零件上的文字跟你想的不一样先说说工业零件上的文字长什么样。日常接触的OCR识别的是文档、名片、路牌、车牌这些东西。背景干净、字体标准、排列规整、光照均匀。这种场景任何一个成熟的OCR框架都能处理得很好。工业零件不一样。零件表面的文字来源有很多种有的是钢印打上去的字符凹下去或者凸起来有的是激光打标一束激光灼烧金属表面留下的痕迹有的是喷码机喷上去的墨水字符还有的是丝网印刷或者蚀刻。每种工艺产生的文字外观都不一样。钢印字的边缘有金属挤压的痕迹笔画粗细不均匀激光打标的字颜色偏浅对比度低有些材质上几乎看不清喷码的字容易模糊还可能被油污覆盖。更头疼的是零件表面本身的材质——金属反光、铸铁粗糙表面、塑料光滑表面——都会严重干扰识别。而且文字不一定是水平的。很多零件形状不规则文字跟着零件表面的弧度走可能是倾斜的、弯曲的、甚至倒着的。你拿一个通用OCR框架去识别这种图它训练数据里根本没见过这种样本能认出四十来分已经算它尽力了。第一个坑通用OCR为什么在工业零件上拉胯比赛官方给的baseline方案是Tesseract。Tesseract基于LSTM在通用场景下效果不错但它是为文档OCR设计的对工业字符的容忍度很低。直接调接口整张零件图丢进去让它自己找文字区域再识别。结果四十来分。我开始分析原因。问题出在哪呢。背景太复杂。OCR框架的文字检测模块是训练来识别文档里的文字行的。文档的文字行有什么特征——背景是白色或浅色的文字是深色的对比度高排列整齐。但工业零件的照片里背景是金属表面、油污、划痕、加工纹理文字检测模块根本不知道该把哪个区域当成文字。光照不均匀。金属表面的反光是OCR的噩梦。你拿手机拍一个金属零件照片里总有一块特别亮的高光区域。高光区域里如果有文字直接被白光淹没了。就算文字不在高光区域里周围不均匀的光照也会让同一个字符的不同部分亮度差异很大OCR模型提取到的特征是破碎的。文字非标准。通用OCR模型的训练数据主要是印刷字体——宋体、黑体、Arial这些。钢印、激光打标、喷码这些工业字符的形态跟印刷字体差别很大。钢印字有挤压变形笔画边缘不规则激光打标字可能只有灰度变化没有明显的黑白边界喷码字可能晕染扩散。OCR模型没见过这些样式自然认不出来。还有一个很容易忽略的问题字符太小。很多零件上印的型号或批次号字符高度只有十几个像素。在这种分辨率下OCR模型根本提取不到足够的特征来做分类。所以通用OCR在工业零件上拉胯不是模型不行是场景不匹配。你让一个只会做阅读理解的模型去做野外生存题它当然做不好。第二个坑文字检测是OCR的灵魂但普通检测框不够用意识到通用OCR不行之后我开始拆解问题。OCR本质上两步先检测文字在哪再识别文字是什么。第一步不准第二步再强也没用。所以我决定先把文字检测做好。当时常用的文字检测方案有两种EAST模型和四向分类器。EAST在处理长文本时效果不理想经常截断四向分类器更粗糙不管文字实际角度是多少都只能按0、90、180、270四个方向硬分。我用了一个主流的文字检测模型输出的是水平的矩形框。它能告诉你图里这一块区域有文字框出来给你。这个方案听起来合理但在工业零件上遇到了一个致命问题文字经常是歪的。零件表面的文字不一定水平排列。有的文字跟着零件的弧面走有的是倾斜打印的有的是零件本身摆放就不水平。水平矩形框能框住文字但框进去的是一块倾斜的、包含大量背景的矩形区域。你把这个区域裁出来交给识别模型识别模型面对的是一坨歪着的文字加上一圈无用的背景噪声。识别模型怎么训练的它训练数据里的文字全是水平的、正的。你突然塞给它一堆倾斜的文字它当然认不出来。就像你让一个人去读一份倒着拿的报纸他可能认得出几个字但准确率肯定大打折扣。而且水平框的框选精度也成问题。文字是歪的框是正的框的四个角必然有一个或两个角偏离文字区域多框进去的是金属表面的纹理和反光少框进去的是文字的边缘笔画。检测结果已经失真了后面的识别再怎么努力都是在失真的输入上做文章。所以我得换一种检测方式。不是检测哪里有文字而是检测哪里有文字、文字有多长、文字朝哪个方向。我选择了YOLO OBBOriented Bounding Box。YOLO OBB是YOLO目标检测的扩展输出的检测框带有角度信息通过四个角点加一个旋转角度来精确定位。不管文字朝哪个方向检测框都能紧贴文字区域旋转。这是当时我觉得最合适的方案——速度快精度高还能直接拿到角度值用于后续校正。第三个坑旋转框检测仿射变换思路对但细节全是坑我找到了旋转框检测的方案。普通的目标检测模型输出的是正矩形框但旋转框检测模型输出的框带有角度信息能够紧贴文字区域不管文字朝哪个方向都能精确框选。这个方案解决了文字定位的问题。但定位只是第一步你还得把歪着的文字变成正的才能让识别模型正常工作。注意这里有一个很容易忽略的中间步骤。你不能直接对整张原图做仿射变换——原图太大了里面有零件、有背景、有各种干扰。正确的做法是先用旋转框的坐标从原图上把文字区域抠出来得到一张只包含文字的小子图。然后对这张子图做仿射变换拉正。这样变换的运算量小而且不会把原图上其他区域的干扰带进来。抠图→拉正两步不能省顺序也不能反。先抠再拉子图里只有文字和少量周边背景仿射变换的精度有保障你要是对整张大图做变换计算量大不说坐标转换的累积误差也会更大。拉正操作用的是仿射变换。原理很简单你知道文字区域的旋转角度你就能计算出一种几何变换把这个歪着的子图映射成一个水平的子图。变换之后文字就是正的了识别模型就能正常识别。思路很清晰对吧。检测定位→拉正→识别三步走。但实际做的时候每一步都有坑。坑在旋转框的检测精度。旋转框的角度偏差哪怕只有两三度变换之后的文字就会有一个轻微的倾斜。这个倾斜对人眼来说可能看不出来但对识别模型来说足够造成干扰。识别模型是对水平文字训练的你给它一个倾斜两三度的文字字符之间的相对位置关系就变了卷积特征就偏了准确率就开始掉。这个问题怎么解决没有银弹只能提高检测模型的角度回归精度。多加训练数据、调损失函数权重、用更好的backbone这些常规操作都得试。另外还有一个取巧的办法如果文字是水平排列的你可以约束检测框的角度范围只在-45度到45度之间回归这样模型不用学360度的角度精度自然更高。坑在检测框的大小。旋转框要紧贴文字区域不能多也不能少。多框了拉正之后的图像里就混进了金属表面的纹理、划痕、反光这些东西会干扰识别少框了文字的边缘笔画被切掉识别模型看到的是残缺的字符。这个问题的调试过程很折磨人。你得标注很多数据标注的时候特别小心地让框贴合文字边界不能松也不能紧。训练完之后还得一个个样本看检测结果哪里框大了一点哪里框小了一点来回调整。坑在仿射变换本身的精度。仿射变换是线性变换它只能处理旋转、平移、缩放和剪切这几种线性变形。如果零件表面是弯曲的文字跟着弧面弯曲仿射变换就拉不直了拉出来还是弯的。碰到这种情况可能需要用透视变换或者更复杂的非线性变换。好在我当时做的比赛数据集里文字基本都是平面的最多就是倾斜没有严重的弯曲。所以仿射变换够用了。标注工具的坑。文字检测模型的训练需要大量标注数据。我用的是X-AnyLabeling一个支持旋转框标注的开源工具。标注流程是用快捷键O创建旋转形状然后用zxcv四个键微调角度——z大角度逆时针x小角度逆时针c小角度顺时针v大角度顺时针。手动标完几百张之后我把训练好的模型导出成ONNX格式加载到X-AnyLabeling里做自动标注再手动修正检测结果。半自动的方式比纯手动快了三倍以上。角度标准化的弯路。标注过程中我注意到比赛数据的文本角度分布很不均匀大部分集中在0度和90度附近。我一开始想了个聪明的办法把所有角度离散化成8个分类0、45、90、135、180、225、270、315度写了个脚本批量转换标注文件里的角度值。训练完发现效果很差因为这种硬分类丢掉了角度的连续性信息——35度的字被强制归到0度和真正的0度字混在一起训练模型学不到精细的角度区分。最后放弃了这个方案直接用连续角度回归效果反而更好。第四个坑识别模型对工业字体的适应问题文字拉正了可以交给识别模型了。这里我没有用Tesseract而是选择了CRNNCTC架构——CNN提取图像特征RNN处理字符之间的序列关系CTC解码输出最终文本。选CRNN不选Tesseract的原因很具体Tesseract基于LSTM训练数据以文档场景为主对工业字符的适应性差CRNN的CNN部分可以提取更深层的图像特征RNN部分能捕捉字符间的上下文关系CTC解码不需要预先做字符分割。对于工业零件上长度不一、样式各异的文字串CRNN的端到端能力更合适。CRNN在标准印刷字体上的效果很好但放在工业字符上又得调。钢印字符。钢印字的特点是笔画边缘有挤压变形。你看一个钢印的A它不是印刷体那种干净的三角形加横杠而是三角形的两个边可能不太对称横杠的两端可能有点歪整个字符看起来有一种被砸过的感觉。CRNN的卷积核是按标准字体的边缘特征训练的钢印的变形边缘会让特征提取产生偏差。激光打标字符。激光打标的字有时候对比度非常低。你肉眼能看出来这里有个字但把它裁成小图丢给模型模型可能觉得这只是一片灰蒙蒙的区域。这种情况下需要做图像增强——提高对比度、做二值化处理让字符和背景的界限更清晰。喷码字符。喷码的字最容易模糊尤其是当喷嘴堵塞或者喷码距离不合适的时候。模糊的字符笔画会扩散、粘连3和8分不清6和9分不清1和7的分不清。这种情况下识别模型的容错能力就很关键了。我的做法是用工业场景的字符数据对CRNN做微调。好在工业字符的种类有限——主要是数字、大写英文字母和少量特殊符号字符集不大。手工标注几百张零件图片上的文字用这些数据微调模型效果提升非常明显。lmdb数据集制作的坑。CRNN训练需要lmdb格式的数据集不是普通的图片文件夹。用官方的脚本把标注好的文本图像转成lmdb时我踩了三个坑。第一个坑磁盘空间。脚本的默认配置是把lmdb数据库大小设为1TB。我本地磁盘没那么多空间直接报错。查了半天才发现是map_size参数的问题改成10G就行。看起来是个配置问题但报错信息是乱码Windows中文编码问题根本看不出是磁盘空间不足。第二个坑字符过滤。比赛数据里的文本包含各种特殊符号有些符号不在CRNN的字符集字典里。训练的时候遇到未知字符loss直接变成NaN整个训练就崩了。我写了个脚本把所有不在字典里的字符筛掉重新生成干净的训练集。第三个坑epoch数。脚本里默认写了1亿轮。是的你没看错1亿。我当时没注意这个参数训练启动后发现要跑到什么时候一算发现按我当时的GPU速度得跑好几天。赶紧停掉改成一个合理的数字。看起来很低级的错误但当你连续调了一天参数、脑子已经转不动的时候这种问题就是容易忽略。这里有一个经验值得分享微调的时候不要只用正确样本也要加一些困难样本。比如反光严重的、模糊的、油污覆盖的。模型只在干净样本上训练的话到了现场遇到脏数据还是会崩。故意加一些噪声样本进去模型的鲁棒性会好很多。那条路到底是什么把整个过程串起来我最终用的方案是四步流水线。第一步旋转框检测。用一个旋转框目标检测模型在整张零件图上找到所有文字区域的位置和角度。输出的是每个文字区域的旋转bbox和角度值。这一步解决的是文字在哪、文字朝哪的问题。第二步抠图。根据检测到的旋转bbox从原图上裁出文字区域的小子图。这一步解决的是把文字从复杂的零件背景中分离出来的问题。原图上可能有反光、油污、加工纹理全部留在背景里不要带进后续流程。第三步仿射变换校正。根据检测到的角度对抠出来的子图做仿射变换把倾斜的文字拉正。这一步解决的是文字是歪的这个问题。第四步CRNN识别。把拉正后的文字子图送进微调过的CRNN模型输出识别结果。这一步解决的是文字是什么这个问题。四步各管各的每一步只解决一个具体问题不指望一步到位。这个分工的思路看起来朴素但效果比直接用一个端到端模型好得多。为什么因为工业场景太复杂了你没法用一个模型同时学好找到文字、拉正文字和识别文字这三件事。每个任务的最佳优化方向不一样检测任务需要关注位置的精确性和角度的准确性校正任务需要关注几何变换的精度识别任务需要关注字符特征的区分度。把这些任务拆开每个模块专注做好一件事整体效果反而更好。当然这个方案的代价也很明显。三步流水线意味着三个模型要维护、三个环节可能出错、三个地方要调参。任何一个环节出问题最终结果都会受影响。工程复杂度比直接调一个OCR接口高了很多。但你想拿高分就得付这个代价。一些细节上的教训关于图像预处理。仿射变换之前做不做预处理对最终结果影响很大。我试过在变换前做直方图均衡化和对比度增强效果有好有坏。对比度增强能帮助反光严重的区域看清文字但也会放大噪声。最后我的经验是预处理要做但要保守。轻度增强就够了别把图像搞得面目全非。关于抠图的像素扩展。旋转框裁剪文字区域的时候如果框紧贴文字边缘容易把字符的笔画切掉。我后来在每个方向多加了10个像素的边距识别准确率立刻涨了一截。这个trick很简单但很管用——多出来的10个像素是空白背景不会干扰识别但能保证字符的边缘笔画完整。关于字符分割。有些零件上的文字是连续排列的比如ABC123这样的型号。旋转框检测会把这一整串文字框成一个区域CRNN能处理这种序列识别但你得确保框选的起止位置准确。框多了一个字符、少了一个字符识别结果可能就完全不一样了。关于后处理纠错。识别结果出来之后可以加一层简单的后处理逻辑。比如如果识别结果是AB3D但你知道这个零件的型号格式是ABC开头后面跟数字那D很可能是个误识别的0。这种基于业务规则的纠错有时候比提升模型精度更直接有效。回头看为什么一开始就错了文章写到现在回头复盘一下。我一开始犯的最大错误是把工业OCR当成了文档OCR。我默认了OCR框架就是做文字识别的丢进去一张有文字的图它应该能认出来。这个默认假设在文档场景下是对的在工业场景下是错的。文档OCR和工业OCR的区别不在于模型架构的差异在于输入数据的分布差异。文档里的文字是干净的、标准的、均匀排列的工业零件上的文字是脏的、非标准的、随意摆放的。你拿在干净数据上训练出来的模型去处理脏数据效果差是必然的。正确的做法是先理解你的数据长什么样再选择或者训练合适的模型。这个道理放在任何机器学习任务里都成立但在工业场景里特别重要因为工业数据的脏程度经常超出你的想象。我的第二个错误是低估了工程细节的重要性。仿射变换的角度精度、检测框的大小控制、图像预处理的强度这些看起来不起眼的细节每一个都能让准确率波动好几个百分点。学术界的人可能觉得这些是工程技巧不值得写论文但在实际项目里这些细节决定了你的方案能不能用。不是总结的总结如果你正在做或者即将做工业零件的OCR识别我把关键的坑点列一下。别直接用通用OCR。工业零件上的文字跟文档文字是两个物种通用模型在工业场景下的准确率可能低到让你怀疑人生。先用通用模型跑一版baseline看看实际效果你就知道问题的严重性了。检测比识别更重要。文字区域找不准、角度测不对后面的识别模型再强也白搭。把精力放在检测上旋转框检测是工业OCR的标配别用水平框凑合。拉正这一步不能省。识别模型是对水平文字训练的你给它歪的输入它就给你歪的输出。仿射变换或者透视变换根据实际文字的变形程度选择。识别模型要微调。工业字符的样式钢印、激光打标、喷码跟标准印刷体差别大用工业场景数据微调一个识别模型投入产出比很高。工程细节决定成败。角度精度、框选精度、预处理参数、后处理规则这些细节加起来对准确率的影响可能比换一个更好的模型还大。打包部署的坑。模型训练完了我用pyinstaller把整个推理程序打包成exe。在开发机上测试没问题发到另一台3090的机器上一跑——卡死。排查了半天最后发现是pyinstaller打包的OpenCV版本和目标机器上的CUDA驱动不兼容。工业项目里训练环境能跑和部署环境能跑之间永远隔着一条沟别指望一次打包就能通。最后说一句可能不太好听的话工业OCR没有银弹。每一个现场都是不同的每一个零件表面的文字都有自己的特点。你不可能做一个通用的工业OCR系统卖给所有客户你只能针对具体场景做适配。这个适配的过程就是工业视觉工程师的核心价值。四十来分到高分差的不是算法是对场景的理解。作者头帕王子系列专栏工业视觉踩坑实录如果觉得有用点赞关注不迷路 如果你也在做类似的工业视觉项目希望这篇文章能帮你少走些弯路。有问题欢迎留言或加我好友讨论。 相关专栏工业视觉踩坑实录