ARTICLE DETAIL

资讯详情

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

OpenCV数码管数字识别实战:七段码特征与小数点处理全解析

OpenCV数码管数字识别实战:七段码特征与小数点处理全解析 简介一套面向毕业设计/课程设计的优秀OpenCV数码管数字识别系统完整支持小数点识别适用于计算机、自动化、电子信息、物联网等专业学生及OpenCV进阶学习者。资源共18个文件以Python源码py/ipynb、SVM模型数据dat、设计说明与READMEmd/txt、测试图片jpg/png及数据表格xlsx为主压缩包仅约2.93MB代码模块划分清晰便于直接复现和二次改造。内容覆盖数码管图像检测、数字与小数点识别、SVM模型预测及可视化演示提供主窗口程序与算法入口可直观观察识别效果并调整参数配套文档能帮助快速配置环境、理解代码结构与排错思路显著降低上手门槛。目前已有63人学习参考适合作为毕业设计、课程设计或项目初期演示的基础方案也可作为图像识别与机器学习结合应用的入门样例便于在此基础上扩展新功能。1. 数码管数字识别为什么一个小数点会成为评分分水岭打开一张数码管照片用一套 OpenCV 脚本把上面亮着的数字读出来还要能识别小数点——这件看似不大的事恰恰是水表电表读数、加热台温度采集、老化柜监控这些场景里真正常用的能力。基于 OpenCV 的数码管数字识别系统能做成优秀毕设靠的不是多深的模型而是把图像处理里最实在的几步走扎实通道拆分、二值化、数字分割、特征判定再加上被大多数人忽略的小数点处理。适合两类人一类是正在做 OpenCV 图像处理项目练手的开发者另一类是选毕设题目、希望交付物完整可演示的学生。难点不在算法而在小数点这类小而关键的部件怎么稳定识别。2. 识别前的图像处理分割比识别更能决定整个系统的成败2.1 通道拆分与二值化为什么红色数码管不能直接转灰度数码管最常见的是红色其次是绿色、蓝色、黄绿色。很多入门做法是拿到照片直接cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)这在红色数码管上会吃大亏。OpenCV 读入的是 BGR 三通道红色数码管的发光区域主要由 R 通道承载转灰度时 B、G 通道的暗值会把整体亮度拉低数码管边缘容易和黑色背景糊在一起。更好的做法是先拆分通道再按数码管颜色选对应通道做灰度图。import cv2 import numpy as np img cv2.imread(meter.jpg) b, g, r cv2.split(img) # 红色数码管取 R 通道绿色取 G 通道蓝色取 B 通道 gray r.copy() # 高斯滤波去噪ksize 必须是正奇数sigma 传 0 让 OpenCV 自动算 blur cv2.GaussianBlur(gray, (5, 5), 0) # OTSU 自动阈值thresh 传 0由算法自动寻找最佳分割点 _, bin_img cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 统一约定为“数字白、背景黑”后面所有逻辑都依赖这个约定 if np.mean(bin_img) 127: bin_img cv2.bitwise_not(bin_img) # 闭运算连接断笔kernel 从 3x3 起步别一上来就 5x5 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) bin_img cv2.morphologyEx(bin_img, cv2.MORPH_CLOSE, kernel, iterations1) cv2.imwrite(bin.png, bin_img)这段代码里有三个容易被忽略的参数。第一个是高斯核(5, 5)数码管照片通常在室内拍摄噪点不多3x3 也够但 5x5 能更稳地压掉传感器噪点第二个是 OTSU 的阈值传 0因为 OTSU 不看传入的 thresh 值它内部遍历 0 到 255 找类间方差最大的分割点第三个是np.mean(bin_img) 127的判断用于处理“亮背景暗数字”和“暗背景亮数字”两种相反情况。数码管自身发光时通常是暗背景亮数字但如果有环境光把背板照亮OTSU 可能把背景判成白色这个均值判断能自动纠正方向。这里要特别提醒通道选择是很多人翻车的地方。红色数码管转灰度后亮度往往不如直接取 R 通道高原因是 G、B 通道对红光响应极弱混进灰度值里只会拖低对比度。同理绿色数码管优先取 G 通道蓝色数码管优先取 B 通道。如果拿不准颜色可以用np.maximum(np.maximum(b, g), r)取三个通道逐像素最大值能兼容大多数发光颜色代价是背景里的彩色噪点也会被放大分割时再靠面积过滤。2.2 投影法把一串数字切成单字水平投影与垂直投影配合数码管识别和手写数字识别有个本质区别手写数字通常已经对齐在格子或固定画布里而数码管是摄像机对着表盘拍数字在画面里的位置、大小、倾斜都不固定。所以必须先做分割把“一串数字”切成“单个数字”后续才能逐个识别。投影法是这个场景里最朴素也最可靠的手段先把二值图按行累加白色像素找到数字所在的行区域再在这个行区域内按列累加把每个数字的左右边界切出来。# 水平投影统计每一行的白色像素数量 h_proj np.sum(bin_img, axis1) // 255 # 扫描行找出所有包含白色像素的连续区间 row_blocks [] in_block False for i, v in enumerate(h_proj): if v 0 and not in_block: start i in_block True elif v 0 and in_block: row_blocks.append((start, i)) in_block False if in_block: row_blocks.append((start, len(h_proj))) digits [] for (r0, r1) in row_blocks: roi bin_img[r0:r1, :] # 垂直投影统计每一列白色像素数量 v_proj np.sum(roi, axis0) // 255 in_block False for j, v in enumerate(v_proj): if v 0 and not in_block: c0 j in_block True elif v 0 and in_block: digits.append((r0, r1, c0, j)) in_block False if in_block: digits.append((r0, r1, c0, len(v_proj)))// 255是因为二值图像素值只有 0 和 255求和后除以 255 得到的是这一行里白色像素的具体数量。用v 0判断而非v 0.5是为了避免浮点误差。扫描时用in_block标志位记录连续区间遇到 0 就封口这样即使数字中间有断裂只要断裂处还有一行白色像素就不会被误切成两段。投影法有两个前提必须满足。第一图像里不能有大面积边框或干扰文字否则投影会把边框也算进去把整个数字区域撑大第二数码管基本处于水平方向如果照片倾斜超过 10 度垂直投影会把相邻数字的笔画连在一起分割结果就是两个字粘成一个宽块。常见做法是先用最小外接矩形或霍夫直线检测出倾斜角做一次仿射校正后再投影这一步对摄像头拍手持照片几乎是必须的。2.3 用连通域分析过滤噪点面积、宽高比与位置三重筛选投影分割得到的候选块里通常混着两类垃圾一类是数码管背板上的污渍、反光斑另一类是小数点。小数点不是垃圾但它的面积和数字差一个数量级必须和数字区分开单独处理。用一个cv2.connectedComponentsWithStats就能拿到所有连通块的位置和面积信息筛选规则按“面积、宽高比、位置”三个维度来定。# 连通域分析8 连通四邻域容易把倾斜数字的斜线切断 num_labels, labels, stats, centroids cv2.connectedComponentsWithStats( bin_img, connectivity8 ) candidates [] for i in range(1, num_labels): # 0 是背景跳过 x, y, w, h, area stats[i] # 1. 面积过滤去掉极小块噪点阈值按图像分辨率调整 if area 30: continue # 2. 高度和宽度过滤太窄太矮的不可能是数字 if h 10 or w 4: continue # 3. 宽高比过滤数字接近竖矩形横线或污渍会被排除 aspect w / h if aspect 0.2 or aspect 1.8: continue candidates.append((x, y, w, h)) # 按 x 坐标排序保证输出顺序和读数顺序一致 candidates.sort(keylambda item: item[0])stats[i]返回的依次是左上角 x、左上角 y、框宽 w、框高 h、面积 area。注意 area 是连通块的实际像素数不是w * h这对排除细长噪点很有用——细长噪点的w * h很大但area很小。宽高比阈值 0.2 到 1.8 是经验值标准数字的宽高比大约在 0.4 到 0.7 之间留出 1.8 的上限是为了兼容斜体或加粗的数码管字体。筛选完之后按 x 坐标排序很重要。投影法切出来的候选框顺序在正拍时就是从左到右但一旦做了倾斜校正或图像翻转顺序可能颠倒。排序后把结果传给识别模块比依赖投影顺序更稳。到这里预处理和分割的产物就是一组干净的候选框每个候选框要么是数字要么是小数点后面两章分别处理。3. 数字识别用七段码特征还是模板匹配我选前者这是判断逻辑3.1 七段码特征提取把每个数字拆成 7 个独立采样区数码管这个硬件本身把数字定义成了固定结构上面一横 a右上竖 b右下竖 c下面一横 d左下竖 e左上竖 f中间一横 g。这 7 段的亮灭组合直接对应 0 到 9不需要训练模型不需要样本集规则是硬件设计时就固化了。cd4511 这类七段数码管驱动芯片在硬件侧做的就是同一件事只不过它把 4 位二进制码翻译成段信号而我们在软件侧把段信号反推回数字。def extract_segments(roi): h, w roi.shape # 把字符区域按 3x3 划分每段对应一个格子中心 seg_pos { a: (0, 1), # 上横 b: (1, 2), # 右上竖 c: (2, 2), # 右下竖 d: (2, 1), # 下横 e: (2, 0), # 左下竖 f: (1, 0), # 左上竖 g: (1, 1), # 中横 } feat {} for name, (i, j) in seg_pos.items(): # 取每个格子中心的小块避免段边界处的毛刺干扰 cell roi[i*h//3:(i1)*h//3, j*w//3:(j1)*w//3] ratio np.mean(cell) / 255.0 feat[name] 1 if ratio 0.3 else 0 return feat这个函数的核心是把 7 段分别映射到 3x3 网格中的固定位置。i*h//3和(i1)*h//3把整个 ROI 切成三等份j控制水平方向的位置。这里有个细节为什么取整格而不是取中心点因为数码管段本身有宽度中段 g 和上段 a 在 3x3 网格里只隔一条缝隙取中心点可能正好落在缝隙里导致误判取整个格子做平均值统计抗噪能力强得多。ratio 0.3这个阈值的意思是这块采样区域里超过 30% 的像素是亮的就认为该段点亮。数码管一般 7 段全亮时笔画约占取样区的一半以上30% 留了余量。如果发现识别结果经常把 8 误判成 0多半是这个阈值偏高把中段 g 的微弱亮光滤掉了反过来把 7 误判成 1则是阈值偏低把不该亮的段算成了亮。按实际照片分辨率缩放后这个阈值通常在 0.25 到 0.4 之间微调。3.2 汉明距离做容错匹配允许一两段误判仍能输出正确数字提取出特征后下一步是把 7 段亮灭组成的特征向量和标准表做匹配。标准表就是数码管的“真值表”顺序固定为 a、b、c、d、e、f、g数字abcdefg01111110101100002110110131111001401100115101101161011111711100008111111191111011直接比对特征向量和标准表完全相等就命中。但实际照片里经常出现某一小段被污渍遮挡或反光干扰导致一段误判。这时用汉明距离取“最相似”的数字比严格相等更可靠。SEG_TABLE { 0: (1, 1, 1, 1, 1, 1, 0), 1: (0, 1, 1, 0, 0, 0, 0), 2: (1, 1, 0, 1, 1, 0, 1), 3: (1, 1, 1, 1, 0, 0, 1), 4: (0, 1, 1, 0, 0, 1, 1), 5: (1, 0, 1, 1, 0, 1, 1), 6: (1, 0, 1, 1, 1, 1, 1), 7: (1, 1, 1, 0, 0, 0, 0), 8: (1, 1, 1, 1, 1, 1, 1), 9: (1, 1, 1, 1, 0, 1, 1), } def recognize_digit(roi): feat_vec tuple(extract_segments(roi)[k] for k in abcdefg) best_digit, best_dist None, 99 for digit, key in SEG_TABLE.items(): dist sum(1 for i in range(7) if feat_vec[i] ! key[i]) if dist best_dist: best_dist dist best_digit digit # 距离大于 1 时置信度很低返回 None 让上层决定是否报警 if best_dist 1: return None return best_digit这里把best_dist 1的识别结果判为不可信返回None。这是一个很重要的工程习惯宁可让系统说“我没看懂”也不要硬给一个错误读数。数码管识别最常见的错误是 0 和 8 混淆0 的特征是七段中只有 g 不亮8 是全亮当照片里中段 g 有轻微反光时0 会被识别成 8。允许距离为 1 刚好能容忍这种单段误判但如果距离达到 2说明至少两段异常这时候强出结果没有意义。3.3 为什么不推荐优先上模板匹配或 MNIST 模型模板匹配的思路是把每个候选框 resize 到固定尺寸再和预先存好的 0 到 9 模板做归一化相关度比较取得分最高的作为识别结果。这个方案在字体完全统一、粗细一致、无倾斜时确实简单但它有个致命弱点对尺度极其敏感。同一块数码管焦距一变笔画粗细就变模板里的细体数字就匹配不上粗体数字不同厂家的数码管字体差异更大模板库要跟着换。MNIST 手写数字识别这类深度学习方案在毕设选题里曝光率很高但它解决的是另一个问题手写数字姿态多变、笔画无规则需要模型从大量样本里学出不变性。数码管的几何结构是固定的只有 7 段亮灭组合用深度模型属于大材小用而且需要额外处理数据标注、训练时长、模型部署这些包袱。七段码特征方案的优势在于逻辑完全可解释答辩时能把每一条识别规则讲清楚不依赖样本集换一块数码管不用重新训练。当然如果是验证码类数码管、或者磨损严重导致笔画大面积缺失的照片七段码特征会失效这种场景下模板匹配和深度模型确实更有优势。但对毕设和常规仪表识别七段码特征加汉明距离是投入产出比最高的方案。4. 小数点识别面积小、易粘连需要单独设计分割策略4.1 小数点的三个固有特征小面积、贴底、偏右小数点在这类系统里被单独拿出来说是有原因的。它的像素面积通常只有数字的十分之一到二十分之一在二值化后的图像里就是个不起眼的小圆点。更麻烦的是红色数码管的小数点亮度并不比数字低但投影分割时它因为太窄垂直投影的列宽往往只有 2 到 4 个像素很容易被当作噪点过滤掉。很多入门方案做完整套数字识别才发现小数点丢了这时候再回头改分割逻辑代价很大。小数点在图像里有三个稳定特征可以利用一是面积小但又不是噪点那种几像素的杂讯二是位置贴近数字区域的底部也就是所在行区域的下三分之一三是它总在某个数字的右下角或左下角水平位置上紧跟在一个数字候选框旁边。这三个特征组合起来足以和数字、噪点区分开。把小数点识别独立出来做而不是塞进七段码特征里是因为它在 3x3 网格里正好落在 d 段或 g 段的位置硬加进特征向量会污染数字本身的段判断。4.2 基于连通域的小数点定位代码与判定阈值实际的定位做法是在做完 2.3 节的连通域筛选后不急着把所有候选框都送进数字识别而是先按面积和位置把候选框分成“可能是数字”和“可能是小数点”两类。小数点候选框单独进入一个验证流程用中心点位置和背景区域的平均亮度做二次确认。def classify_candidates(bin_img, candidates, row_range): r0, r1 row_range # 数字区域的行范围 digits_cands [] dot_cands [] for (x, y, w, h) in candidates: cx x w // 2 cy y h // 2 area w * h # 特征小面积 接近正方形的宽高比 中心点落在行区域下半部分 if area 400 and 0.6 w / h 1.4 and cy r0 (r1 - r0) * 2 / 3: dot_cands.append((x, y, w, h)) else: digits_cands.append((x, y, w, h)) return digits_cands, dot_cands def confirm_dot(bin_img, dot_cand, threshold0.6): x, y, w, h dot_cand # 取候选框向外扩展 2 像素的区域统计亮像素占比 roi bin_img[max(0, y-2):yh2, max(0, x-2):xw2] ratio np.mean(roi) / 255.0 return ratio thresholdcy r0 (r1 - r0) * 2 / 3这一行是核心判定数字区域的底部三分之一范围内才是小数点可能存在的位置。因为小数点物理上总在数字下方摄像机正拍时它的 y 中心点一定比数字框的 y 中心点靠下。area 400这个面积上限需要按图像分辨率调整如果整张图是 1920x1080 拍的数字区域高约两三百像素小数点面积可能有一两百如果图像只有 640x480面积上限要降到 100 左右。0.6 w/h 1.4用来过滤横条和竖条小数点接近圆形宽高比在 1 附近。confirm_dot里把 ROI 向外扩展了 2 个像素目的是把小数点的边缘光晕也算进去。数码管发光时周围有一圈散射光二值化后实际白色区域比发光管本身略大扩展后统计亮度占比更稳。threshold0.6的意思是扩展区域内超过六成像素是白色才确认有小数点。如果阈值设太低背板上的反光点会被误判成小数点设太高亮度不足的数码管小数点会被漏掉。4.3 多层确认策略先数字后小数点避免误报把小数点识别放在数字识别之后做是有意为之。先识别出数字序列和每个数字的位置再回头看候选框里有没有满足条件的小点这个顺序能避免一个很常见的错误小数点配件老化导致亮度不足时被当作噪点丢掉。多层确认的思路是第一层用连通域找候选第二层用位置和形状初筛第三层用亮度确认三层全过才输出一个点号。# 识别主线流程 digits_cands, dot_cands classify_candidates(bin_img, candidates, (r0, r1)) result_digits [] for cand in digits_cands: x, y, w, h cand roi bin_img[y:yh, x:xw] d recognize_digit(roi) if d is not None: result_digits.append((x, d)) # 保存 x 坐标后续和点号位置对齐 confirmed_dots [] for cand in dot_cands: if confirm_dot(bin_img, cand): confirmed_dots.append(cand) text [] for x, d in result_digits: text.append(str(d)) # 如果某个小数点的水平位置紧跟在当前数字右边插入点号 for (dx, dy, dw, dh) in confirmed_dots: if abs(dx - (x w)) 8: # 距离阈值按数字宽度缩放 text.append(.) break插入点号的逻辑是看小数点候选框的左边界dx和当前数字候选框的右边界x w之间的距离。距离阈值取 8 像素是按常规分辨率估的更稳的做法是把它设成当前数字宽度的四分之一threshold w // 4。如果小数点距离当前数字太远那它可能属于下一位数字需要等到遍历到那一位时再判断。这套流程走完输出的就是类似[2, 2, 0, ., 4]的字符序列直接.join()就是可读读数字符串。如果走了全套流程后确认点数为零但原图里肉眼可见小数点优先检查 2.1 节的闭运算核大小——3x3 闭运算通常没关系但 5x5 闭运算可能直接把小数点填进数字的边角里这也是第 5 章要展开说的坑。5. 数码管识别避坑清单从环境安装到断笔粘连的排查记录5.1 安装 OpenCV 时最容易翻车的三个环境问题现象在 Anaconda Prompt 里敲pip install opencv-python安装过程没有报错但进入 Python 环境执行import cv2直接抛ModuleNotFoundError: No module named cv2。原因pip 装进了当前 shell 对应的 Python 环境但你启动 Python 时用的可能是另一个虚拟环境。Anaconda 的 base 环境和自建环境是隔离的很多人装完就忘等要用时才发现解释器路径不对。解决先执行python -m pip install opencv-python用python -m pip而不是裸pip确保装到当前解释器所在环境。装完执行python -c import cv2; print(cv2.__version__)验证能打印出版本号才算成功。顺带提醒opencv-python和opencv-contrib-python只能装一个两个都装会互相覆盖import 时可能出现找不到xfeatures2d这类子模块的怪问题。5.2 二值化方向和形态学参数选错输出全是 0 或 8现象预处理后图像在显示器上看是正常的黑底白字但识别结果全是 8或者数字区域整个糊成一团。原因二值化方向反了。cv2.threshold的THRESH_BINARY表示大于阈值置 255、小于阈值置 0THRESH_BINARY_INV是反过来的。数码管照片的亮区是数字本身如果用反了背景变成白色、数字变成黑色投影分割时把整块背景当成一个巨大连通域数字反而成了背景里的空洞。至于全 8通常是闭运算核太大比如用(5, 5)甚至更大的核把数码管段与段之间的黑色缝隙全部填平了所有数字都变成实心矩形7 段全亮自然全判成 8。解决二值化后立即加一行均值判断用 2.1 节里的np.mean(bin_img) 127自动反色统一成白字黑底。形态学闭运算的核从(3, 3)开始每增大一档都重新看一眼中间缝隙是否还存在。判断依据很简单如果发现数字内部没有黑色间隙说明闭运算过强。5.3 小数点粘连、断笔与倾斜三个典型识别失败现场现象一小数点离下一位数字特别近闭运算后直接和数字的右下竖条 b 段或 c 段黏在一起连通域分析把它们当成一个块识别结果从 4 变成 9 或 8。原因形态学闭运算的膨胀操作会把距离小于核尺寸的两个区域连成一片。小数点直径通常只有几个像素和数字的间距也可能只有几个像素3x3 核在膨胀阶段就可能把两者桥接。解决把闭运算拆成“先闭运算再重新连通域分析”两步。第一次闭运算只作用于数字候选区域小数点候选在闭运算之前就通过面积初筛单独拎出来等数字识别完再按 4.3 节的距离判断决定是否插入点号。顺序不能反先做闭运算再筛小数点小数点已经没了。现象二数码管老化或拍摄反光导致某个数字缺一段比如 9 的左下竖 e 段没亮识别结果变成 3。原因七段码方案对缺段是容忍的但容忍范围只有一段。e 段丢失后9 的特征从1111011变成1111001这正好是 3 的特征。解决这是硬件本身的物理问题算法上能做的是把识别结果和上一帧做对比如果连续几帧都是同一个数字但偶尔跳成别的值取众数作为最终结果。视频流场景里这个招数很实用单张照片场景下只能接受best_dist 1时返回None让上层知道这张图没读懂。现象三用手拿着手机斜着拍数码管投影分割时数字的相邻两列笔画在垂直方向重叠两个数字被切成一个宽块。原因投影法假设数字是竖直排列、互不重叠的一旦图像倾斜超过十几度相邻数字的投影在列方向上交错垂直投影找不到零值分界点。解决先检测倾斜角再做仿射校正。常见做法是用cv2.minAreaRect找整个数字区域的最小外接矩形对矩形比原始boundingRect更接近实际倾斜角或者用cv2.HoughLinesP检测数码管最长的水平边缘线算出角度后用cv2.getRotationMatrix2D旋转。校正做完再走投影分割这个过程我一般是放在 2.2 节之前当作预处理的一个固定步骤。6. 进阶用分块自适应阈值与结构化校验提升现场鲁棒性6.1 分块自适应阈值解决整体光照不均的最后一公里OTSU 全局阈值在光线均匀的室内测试图上表现很好但一到现场就露怯数码管上方有灯管下方有阴影同一张图里不同区域的最佳二值化阈值可能差出 50 个灰度级。全局阈值算计中间值结果上半部分数字被阈值吞掉下半部分背景反光被当成数字。解决思路很简单——把图像切成小块每块独立算 OTSU再拼回去。def block_adaptive_binary(img, block64): h, w img.shape out np.zeros_like(img) for i in range(0, h, block): for j in range(0, w, block): cell img[i:iblock, j:jblock] # 小块内明暗相对均匀全局阈值在这里失效的机率低得多 _, th cv2.threshold(cell, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) out[i:iblock, j:jblock] th return outblock的取值要和你数码管的成像大小挂钩。如果数字区域高度在图像里约占 100 像素block 取 64 比较合适确保每个数字横跨一两个块如果 block 取得比一个数字还小可能把数字内部较暗的笔画和较亮的背景拆到不同块里造成同一个数字被不同阈值割裂。这个方法唯一的代价是不能再用整图的均值判断来反色需要改成每块判断或者直接约定图像为暗背景亮数字。6.2 结构化校验让识别结果变成可信读数的最后一道关很多数码管显示的方向是固定的比如温控仪显示25.0、电压表显示220.4、计数器显示00128。这意味着识别结果要符合一些硬性约束位数固定、小数点位置固定、某些位不能为 0。利用这些信息做校验能把个别字符误判直接拦下来。def validate_reading(digits, dot_index): # 拼成完整字符串 text .join(str(d) for d in digits) if dot_index is not None: text text[:dot_index] . text[dot_index:] # 以温控仪为例格式必须是 XX.X最多 4 位有效数字 parts text.split(.) if len(parts) ! 2: return None if len(parts[0]) ! 2 or len(parts[1]) ! 1: return None # 最高位不允许是 0 if parts[0][0] 0: return None return text校验规则要结合具体读数范围来写。最高位为 0 在多数仪表里是非法的直接拒绝数值超出合理范围比如温度计读出 99.9 而你已知量程上限 60八成是识别错了。这种校验代码比再多调两个图像参数都管用因为它从语义层面过滤了低级错误。结构化校验的灵活性在于你可以先跑一遍识别把不满足规则的候选结果按汉明距离升序逐一尝试替换其中某个字符直到找到满足规则的组合。我在实际做这类系统时最后一步永远是规则校验而不是盯着识别准确率调参。现在拿到一组数码管照片我第一件事永远是先确认小数点和位数这个习惯帮我避开了大量返工。希望帮到你。本文还有配套的精品资源点击获取
返回列表