十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

仪表数字识别实战:传统图像处理方法的完整技术链路

仪表数字识别实战:传统图像处理方法的完整技术链路 仪表数字识别放在今天很多人第一反应是上深度学习。但我在实际项目里碰到的场景往往很尴尬客户给几十张现场照片要识别水表、电表、燃气表、压力表上的数字要求不高但必须马上跑通没有标注数据也没有GPU服务器。这种时候传统图像处理方法反而是最能救场的方案。这篇文章就基于我自己做过的几个仪表数字识别项目把整套传统方法的技术链路、踩坑经验、调参思路完整拆一遍包括图像预处理、表盘定位、数字切分、特征识别、稳定性优化以及为什么有些环节用传统方法比深度学习更靠谱。1. 为什么“先试传统方法”在仪表识别场景里依然是正确决策先说一个反直觉的结论在仪表数字识别这类字符种类极少、环境相对固定的任务里传统方法的上限并不低而且它的可解释性、调试效率和部署成本明显优于深度学习方案。我见过不少团队一开始就上了目标检测加分类网络结果卡在标注数据质量和现场光照适应上搞了几个月都没法验收。1.1 先把适用边界画清楚传统方法适合的仪表类型非常明确主要集中在以下三类机械式计数器水表、燃气表、电表上的数字滚轮字轮数字变化规律字体印刷清晰背景干净这是传统方法的主战场。电子数显表LCD或LED数码管显示数字笔画固定、亮暗对比强烈用阈值分割就能把数字区域完整抠出来。指针表盘附属的数字刻度这类场景稍微复杂一些但数字本身是印刷体配合表盘定位和几何校正后依然可以做。如果目标是把所有类型的仪表做成一个通用识别引擎那传统方法会非常吃力这种情况我确实建议上深度学习。但凡限定了具体表型、安装位置基本固定、光照可控制或可补偿传统方案都能达到99%以上的识别准确率。1.2 传统方法真正的三个优势第一是无需标注样本。传统方法不依赖大量标注图像只要你理解图像处理流程每类表只需要几张样张做模板、定参数即可。第二是调试链路透明。数字识别出错了你可以逐环节查看预处理、定位、切分的中间结果3分钟就能定位是哪一步出了问题。第三是部署极其轻量。OpenCV就能搞定一切一个几百KB的可执行程序就能跑在工控机上内存占用不过几十MB这在现场硬件预算有限的场景里是巨大的优势。我在一个水表识别项目里用的是树莓派加USB摄像头没有任何加速硬件单帧识别耗时也就80多毫秒。如果用深度学习模型光是推理框架就得几十MB起步现场还要考虑模型文件管理和版本更新复杂度完全不在一个量级。2. 采集端与预处理这一步没做好后面全都是白费功夫仪表数字识别的预处理不是简单转个灰度、做个二值化就完了。我总结的经验是预处理的目标不是让图像“好看”而是让后续定位和切分环节拿到一张结构稳定、噪声可控的图。这个目标决定了你在每个预处理步骤里的取舍。2.1 灰度化的两种路线选择多数仪表图像是从彩色摄像头采集的灰度化是第一步。我见过很多人直接用cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)这没什么问题但如果你面对的是彩色表盘、数字和背景的颜色差异明显那么在灰度化之前先做通道分离往往更有效。举个例子某电厂项目的压力表表盘是黄色底、黑色数字和红色指针这种情况下BGR三个通道里蓝色通道的表盘底色和数字对比度最差而红色通道里数字依然保持黑色且底色变亮了很多对比度反而更好。我当时用cv2.split分别查看三个通道的效果最后选定了对比度最高的红色通道做后续处理。同样的操作在另一个蓝色表盘的项目里就换成了蓝色通道。这不是什么高深技巧但很多人都想不到去试。正确做法是在预处理阶段就建立一个通道选择机制把灰度化、通道选择、亮度统计做成一个可配置的参数组而不是写死在代码里。2.2 去噪的度别把数字边缘也抹掉了去噪方面中值滤波和双边滤波的效果普遍比高斯滤波好。原因是仪表图像里的噪声多是椒盐噪声灰尘、反光点、传感器热噪声中值滤波对这类噪声的抑制能力很强同时能保持边缘锐利。具体参数上我习惯先用3×3的核试不行再往上加。5×5的中值滤波能把大部分噪点清掉但也会让细小的数字笔画变圆滑影响后面的特征提取所以不要一上来就选大核。双边滤波的优点是保边去噪对LCD数显仪的图像效果尤其好。但它的计算量大参数又敏感工业相机高分辨率图像上会拖慢处理速度我一般只在图像噪声确实严重时才用它。2.3 二值化自适应阈值不是万能药二值化是数字分割前的关键一步。全局Otsu阈值在光照均匀的室内场景表现很好但它对光照梯度非常敏感一旦图片半边亮半边暗Otsu会把暗部的数字和背景全压成黑色数字直接消失。这种场景我推荐直接上自适应阈值cv2.adaptiveThreshold它按邻域计算局部阈值对光照变化有天然抵抗能力。blockSize一般选15到31之间C值取5到15具体要看数字笔画粗细来调。需要特别提醒的是自适应阈值处理光照不均很好用但容易在平坦区域产生噪声块所以用它之前必须先做一次去噪处理后还要做一次开运算清理孤立噪点。如果你的相机位置固定、光照可控固定阈值加Otsu依然是首选因为它的输出最稳定后续参数调优最省心。各种算法没有绝对的好坏只有适合不适合当前场景。2.4 图像校正一个往往会被忽略的环节现场安装的摄像头和仪表很难保证完全垂直正对多少都会带一点透视畸变。传统方法对数字的形态比较敏感如果表盘旋转超过5度投影切分就会出大问题。校正思路不复杂机械表找表盘的矩形边缘数显表找数字区域的矩形外框然后用cv2.getPerspectiveTransform加cv2.warpPerspective把表盘拉正。矩形检测用形态学闭运算配合轮廓外接矩形来做比直接霍夫直线检测稳定。我在一个燃气表项目里因为现场安装支架有点歪拍出来的字轮整体倾斜了大约8度直接投影切分时字符上下边缘错位识别率只有85%。做了透视校正之后直接稳定在99%以上。所以预处理环节千万别省这一步。3. 表盘定位与数字区域提取让算法知道“往哪里看”预处理做完后下一步是从整张图中把仪表表盘或者数字区域从背景中分离出来。定位好坏直接决定后续数字切分和识别的难度。3.1 基于轮廓的定位套路最通用的定位流程是预处理后找边缘cv2.Canny低阈值和高阈值可以分别设定为50和150再配合形态学闭运算把断裂的边缘连接起来。提取轮廓cv2.findContours用RETR_EXTERNAL只取外轮廓。筛选轮廓按面积、宽高比、凸性三个指标过滤。仪表表盘的宽高比一般在0.5到2.0之间面积占整个图像的比例大于一个阈值比如5%凸包面积和轮廓面积比值接近1。这套流程对表盘背景单一的情况很有效。但现场有时候背景里会出现和表盘形状相似的物体比如圆形的阀门手轮。我处理这类误检的办法是多加一个线索表盘内部往往有文字和数字纹理密度明显高于周围背景可以计算候选区域内的梯度密度或二值化后的白色像素占比来过滤。这个方法简单但非常有效。3.2 先验知识驱动比纯视觉定位更稳如果你知道仪表是什么型号那就别把定位完全交给算法直接用先验知识辅助会稳得多。比如电表数字区的位置可能固定在整表的某个比例区域你在标定阶段记下这个归一化坐标运行时直接按比例裁剪再做一个小的偏移搜索微调即可。我的做法是在配置文件里保存每个表型的数字区ROI坐标、数字个数、单个数字的宽高比例运行时先按ROI裁剪出数字区域再在这个局部区域内做精细定位。这比每次全图找表盘更稳定、更快也大幅降低了误检率。3.3 数显表的定位相对简单但也有暗坑LCD或LED数显表的数字区域一般非常规整用亮度阈值加轮廓外接矩形的方式基本就能准确提取。但有一个常见坑LED数码管的笔画之间存在暗色空隙如果阈值分割参数调得不好单个数字会被拆成好几段轮廓外接矩形的坐标就会错乱。针对这个问题建议在定位数字区域时先对二值图做一次水平方向的闭运算核宽度取数字宽度的1.5倍左右让同一行的数字笔画连成一个整体这样外接矩形就能完整包住一整行数字。闭运算的核尺寸需要根据图像分辨率实测调整不能拍脑袋。4. 数字切分整个识别链路中最容易翻车的环节数字切分决定了每个字符子图的边界切分不准后面用什么分类器都白搭。可以毫不夸张地说仪表识别项目的成败八成取决于切分环节做得细不细。深度学习方案里这一步往往被RNN或者分割网络替代了但在传统方法里你必须自己手工搞定它。4.1 投影法最基本也最实用投影法的原理太简单了对数字区域的二值图做垂直投影统计每列有多少白像素有数字的列投影值高数字之间的间隔列投影值为0或接近0据此切分出每个字符。实际操作中直接投影会碰到三个典型问题数字笔画断裂比如数字“4”中间的空隙导致投影在字符内部出现局部极小值误切成两个字符。解决方法是设定一个最小字符宽度阈值低于阈值的间隔不认为是分割点。字符粘连机械表字轮切换时两个数字可能出现半个字的重叠投影值没有归零区间无法分割。这是最头疼的。噪声干扰二值化后的噪点会在投影图中产生毛刺让分割边界抖动。我的处理顺序是先对二值图像做开运算清理噪点然后做竖直方向的膨胀让同一字符的笔画粘连在一起接着计算垂直投影用滑窗找“低于动态阈值”的列区间作为候选分割点。动态阈值怎么算我习惯取整行投影最大值的10%到20%低于这个值且连续宽度大于2到3列的区间视为分割点。阈值定太高会把字符切碎定太低又无法分离粘连字符需要根据实际图像反复微调。4.2 粘连字符的进阶处理如果投影法无法分开粘连字符就要上更精细的分割策略。我按难度从低到高排列几种方案垂直投影配合局部极值搜索在投影曲线中找局部极小值点作为分割候选点只用投影最低点做分割。这个方法对轻度粘连有效。连通域分析加形态学重组先用连通域把每个独立笔画块找出来再根据先验的数字数量、单个数字宽度把这些连通域按几何位置重组为N个字符组。比如数字“8”可能会被拆成上下两个圈加上数字“1”被拆成三根线段这时候利用“一个数字宽度大约等于总宽度除以数字个数”这个约束是按坐标聚类合并出正确字符组的可靠方法。滴水算法模拟水从字符顶部沿谷底流下的路径来分割粘连字符。它适合笔画交叉性强的手写体对仪表印刷体来说效果也不错但实现复杂度高一些一般情况用不到。我实际项目中用到最多的还是第二种思路。先连通域分析再加宽度先验约束做合并反而比投影法更鲁棒。4.3 归一化是切分后的必做动作拿到每个字符子图后不能直接扔给识别模块。因为不同图像里字符的尺寸、位置、笔画粗细都有差异需要先做归一化。标准做法是将每个字符子图缩放到统一尺寸比如32×48或28×28保持数字宽高比不变。缩放时用最近邻插值还是双线性插值这里推荐双线性插值它不会像最近邻那样产生严重的锯齿保留的笔画形状更接近真实。缩放之后再做一次二值化把边缘插值产生的灰色像素重新判定为黑或白。另外还要处理笔画粗细不均。有的数字“1”特别细、有的“8”特别粗这会直接影响模板匹配的相似度。我的做法是对归一化后的字符做一次形态学标准化比如统一做一次细化和膨胀来调整笔画宽度或者在做特征提取时用能容忍笔画变化的特征而非像素级模板。5. 数字识别从模板匹配到特征工程方案切分归一化之后就到了识别环节。传统方法里识别大致有两条路线一条是模板匹配一条是特征加分类器。不同路线各有优劣我分别展开说。5.1 模板匹配简单直接但需要注意的两个坑模板匹配的思路是准备10个标准数字模板把待识别字符和每个模板做相似度计算取相似度最高的作为识别结果。OpenCV里常用cv2.matchTemplate加TM_CCOEFF_NORMED归一化相关系数。它的值在-1到1之间越接近1表示越相似。对于印刷体数字相关系数通常能达到0.9以上很容易区分。但模板匹配有两个大坑字体差异不同厂家的仪表字体不一样同一个“0”在不同设备上可能细长、扁宽、带缺口固定模板很容易翻车。解决办法是每个表型单独做一套模板或者用同一字体生成不同粗细、不同缩放的多个模板取最高相似度作为结果。相似数字混淆“0”和“8”、“6”和“5”、“7”和“1”在笔画缺失或者有污渍遮挡时会特别容易混淆。模板匹配无法主动利用数字的拓扑结构信息来排除这些混淆。我的经验是模板匹配只适合字体严格统一的场景。如果客户说“我们有好几种表型”那就别一条路走到黑要及时切换方案。5.2 特征加分类器在模板匹配和深度学习之间的平衡点当遇到多表型、多字体时我通常改用特征提取加分类器的路线。特征提取方案有很多种我在实际项目中偏好以下组合几何特征组包括数字的宽高比、笔画密度、孔洞数量、端点数量、交叉点数量。这些特征计算量小、可解释性强。比如“0”没有端点、“6”有一个孔洞一个端点、“8”有两个孔洞、“9”也有一个孔洞但位置和“6”不同。7个Hu矩对平移、旋转、缩放不敏感适合表征数字的整体形状。HOG特征对边缘方向信息描述能力强配合SVM分类器在印刷体数字上准确率很高是最实用的方案之一。SVM的核函数我一般选RBF核训练样本也不需要太多每个数字准备50到100个切分好的样本就够用了。如果C和gamma参数调得好这种方案对光照变化、笔画粗细变化的容忍度远高于模板匹配。5.3 先验后处理用逻辑规则兜底识别结果不能直接就信尤其是在切分质量一般的情况下。我习惯在识别后加一道逻辑校验检查识别结果是否满足该表型的数字范围。比如某燃气表的字轮范围是00000到99999但如果表头有小数位末位是固定的小数轮那么最后一位的识别结果可能需要特殊处理。利用校验位或数字跳变规律做修正。机械表字轮在进位时相邻两个轮子可能处于半切换状态出现半个数字重叠的图像这时识别结果会很乱。后处理规则可以判断出“正在进位”然后参考前后帧的读数推断当前值。多帧投票。这是最简单也最有效的后处理。在现场固定相机连拍几张图对每一帧分别识别最终统计每个位置出现次数最多的数字作为结果能有效过滤单帧的脏污和反光造成的偶发错误。6. 光照不均、反光和遮挡现场最常遇到的三个干扰源实验室里拍出来的测试图往往很干净一到现场就各种问题最常见的就是光照不均、反光和括号遮挡。这块必须专门处理否则整套算法可能直接废掉。6.1 光照不均的通用对策光照不均的表现是图像一部分亮、一部分暗数字在暗区几乎看不见。刚才提到自适应阈值能解决一部分问题但更彻底的方式是做光照校正用大核比如图像宽度1/20大小的核对灰度图做形态学闭运算得到的图就是近似背景光照分布。用原图除以背景图就能得到光照均匀化的结果。这个方法的本质是把低频的背景光照变化去掉保留高频的数字笔画信息比直接用自适应阈值更通用。6.2 反光问题处理仪表表面常有玻璃罩或透明塑料面板拍照时会产生局部反光表现为一大片亮斑把数字完全遮住。反光区域的特点是高亮、低纹理、边界锐利。处理反光的思路有两个一是拍摄时用偏振片物理消除反光这个方法最有效但现场未必有条件二是后处理时检测高亮区域用周围像素插值填补。检测高亮区域可以用灰度阈值加轮廓分析把亮度接近255且面积超过一定阈值的大块区域遮罩出来然后用cv2.inpaint做修复。修复效果取决于反光面积大小小面积反光处理效果很好大面积反光会引入明显伪像。所以拍照时角度稍微偏移一点让反光避开数字区域比任何算法都管用。6.3 数字遮挡与污损仪表数字区域有时候会被指针遮挡比如压力表的指针正好压在数字上。这种情况处理起来比较麻烦我的思路是识别前先检测并移除指针。具体方法是对数字区域的二值图做直线检测霍夫变换找到长而细的直线段如果它的方向、位置符合指针特征就在后续切分前把该直线段的像素置为背景。但指针如果与数字笔画重叠强行移除会破坏数字结构这时只能靠多帧不同角度的图像来做补充识别。更常见的污损是灰尘、机油、笔迹等覆盖在数字上处理方式是形态学开运算清除小污点对大面积污损则需要在切分时容忍笔画缺失这就要靠识别阶段的特征方案来兜底了。7. 实战案例复盘水表数字识别的完整调参链路最后用一个我实操过的项目来完整串联整篇文章的内容顺便把调参过程中的具体数值和踩坑点都交代清楚。这个项目是识别自来水厂的机械水表字轮数字摄像头俯视安装水表玻璃表面有纸质铭牌遮挡了一部分边缘光照来自侧上方日光灯。7.1 第一版流程与失败点初始方案是转灰度、高斯滤波、Otsu二值化、垂直投影切分、模板匹配识别。做出来之后在测试集上识别率大约93%看起来还行但一拿到现场就掉到80%以下。排查后发现两个核心问题现场水表玻璃反光Otsu二值化把反光区域和数字区域混在一起导致投影切分时字符宽度被撑大。字轮数字的字体和模板不一致模板匹配相似度普遍偏低。7.2 改进后的完整链路针对上面的问题我把流程调整成了这样通道选择观察现场照片后发现B通道里反光最弱数字和背景对比度最高于是选定B通道作为输入。预处理先中值滤波去噪再用大核闭运算估计背景光照并做光照校正。二值化改为自适应阈值blockSize取21C取10。处理后再用开运算清理噪点。表盘定位用轮廓面积和宽高比筛出字轮区域按先验ROI裁剪出数字行。切分垂直投影加动态阈值再结合连通域分析和数字个数先验做字符重组。针对可能与下一数字粘连的情况在进位帧采用前后帧投票机制。识别改用几何特征加HOG特征加SVM的方案为每类数字收集了约80个样本训练。识别后加读数范围校验和帧间一致性校验。这样改完之后现场准确率稳定在99%以上处理速度单帧大约100毫秒左右。7.3 这个项目给我的三点心得第一前面的特征和识别方案再先进也不如先把图像采集做好。后来我们把摄像头安装位置稍微调低、加了一个遮光罩反光问题大幅减少识别率又提升了一截。算法解决不了拍摄角度和光照设计上的偷懒。第二传统方法的调试一定不要默默调代码要把每个阶段的中间结果图保存下来看。我每次调试都会把灰度图、二值图、ROI裁剪图、切分结果图、最终识别结果图按顺序保存到一个debug文件夹里出了错瞟一眼就知道问题出在哪一环。第三面对现场的各种脏、乱、差与其不断优化算法去强行适配不如给现场提一些简单的物理改造建议比如加一个遮光罩、贴一张防反光膜、固定摄像头角度。这些改动成本低、见效快还不需要重新训练模型。传统仪表数字识别的方法论说穿了并不复杂难的是每一步的参数和流程都要贴合实际场景去调。如果你目前也在做类似的仪表识别项目我建议你先别急着堆模型把本文这套预处理、定位、切分、识别的链路走通一遍把每个环节的中间结果都看清楚再决定要不要引入更复杂的算法。等你跑通了这套基础链路再去对照深度学习方案你会更清楚地知道传统方法的限度在哪里也知道哪些步骤是任何方案都绕不开的基础。
返回列表