七段数码管OCR开发遇sort_contours报错及功能失效问题求助
修复七段数码管OCR的报错与识别问题
我看了你的七段数码管OCR代码,那个ValueError: not enough values to unpack (expected 2, got 0)本质是因为没有检测到符合条件的数字轮廓,导致digitCnts为空,调用sort_contours时返回空列表没法解包。虽然你用THRESH_BINARY_INV解决了报错,但代码里还有不少细节问题导致识别失败,比如变量名不一致、阈值处理逻辑混乱、轮廓筛选条件不合理、ROI提取错误以及输出图像未初始化等。
下面是修复后的完整代码,我会在后面逐一说明关键修复点:
import numpy as np import cv2 import imutils from imutils.perspective import four_point_transform from imutils import contours # 定义七段数码管的段映射字典 DIGITS_LOOKUP = { (1, 1, 1, 0, 1, 1, 1): 0, (0, 0, 1, 0, 0, 1, 0): 1, (1, 0, 1, 1, 1, 1, 0): 2, (1, 0, 1, 1, 0, 1, 1): 3, (0, 1, 1, 1, 0, 1, 0): 4, (1, 1, 0, 1, 0, 1, 1): 5, (1, 1, 0, 1, 1, 1, 1): 6, (1, 0, 1, 0, 0, 1, 0): 7, (1, 1, 1, 1, 1, 1, 1): 8, (1, 1, 1, 1, 0, 1, 1): 9 } # 加载图像 image = cv2.imread('d4.jpg') if image is None: print("无法加载图像,请检查文件路径!") exit() # HSV颜色分割提取数码管区域 hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) low_val = (60, 180, 160) high_val = (179, 255, 255) mask = cv2.inRange(hsv, low_val, high_val) # 寻找最大轮廓(数码管区域) contours_list, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours_list: print("未检测到数码管轮廓!") exit() largest_area = 0 largest_cnt = None for cnt in contours_list: area = cv2.contourArea(cnt) if area > largest_area: largest_area = area largest_cnt = cnt # 获取数码管ROI x, y, w, h = cv2.boundingRect(largest_cnt) roi = image[y:y+h, x:x+w] output = roi.copy() # 初始化输出图像 # 预处理:灰度化 + 二值化(反相,确保亮段为白色) grayscaled = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) # 使用THRESH_BINARY_INV + OTSU,自动计算最佳阈值 retval2, threshold2 = cv2.threshold(grayscaled, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 寻找数字轮廓 cnts = cv2.findContours(threshold2.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) digitCnts = [] # 筛选数字轮廓(根据实际图像调整宽高阈值) for c in cnts: (x_digit, y_digit, w_digit, h_digit) = cv2.boundingRect(c) # 调整宽高条件,适配你的数码管尺寸 if w_digit >= 10 and h_digit >= 25 and h_digit <= 50: digitCnts.append(c) # 按从左到右排序数字轮廓 if digitCnts: digitCnts, _ = contours.sort_contours(digitCnts, method="left-to-right") else: print("未检测到数字轮廓!") cv2.imshow("ROI", roi) cv2.imshow("Threshold", threshold2) cv2.waitKey(0) cv2.destroyAllWindows() exit() digits = [] # 遍历每个数字轮廓进行识别 for c in digitCnts: (x_digit, y_digit, w_digit, h_digit) = cv2.boundingRect(c) digit_roi = threshold2[y_digit:y_digit + h_digit, x_digit:x_digit + w_digit] # 计算七段的尺寸参数 (roiH, roiW) = digit_roi.shape dW = int(roiW * 0.25) dH = int(roiH * 0.15) dHC = int(roiH * 0.05) # 定义七段的坐标(使用当前ROI的尺寸,而非全局) segments = [ ((0, 0), (roiW, dH)), # 顶部段 ((0, 0), (dW, roiH // 2)), # 左上段 ((roiW - dW, 0), (roiW, roiH // 2)), # 右上段 ((0, (roiH // 2) - dHC), (roiW, (roiH // 2) + dHC)), # 中间段 ((0, roiH // 2), (dW, roiH)), # 左下段 ((roiW - dW, roiH // 2), (roiW, roiH)), # 右下段 ((0, roiH - dH), (roiW, roiH)) # 底部段 ] on = [0] * len(segments) # 检测每个段是否点亮 for (i, ((xA, yA), (xB, yB))) in enumerate(segments): segROI = digit_roi[yA:yB, xA:xB] total_non_zero = cv2.countNonZero(segROI) area = (xB - xA) * (yB - yA) # 如果非零像素占比超过50%,认为该段点亮 if total_non_zero / float(area) > 0.5: on[i] = 1 # 查找对应的数字 try: digit = DIGITS_LOOKUP[tuple(on)] digits.append(digit) # 在输出图像上绘制结果 cv2.rectangle(output, (x_digit, y_digit), (x_digit + w_digit, y_digit + h_digit), (0, 255, 0), 1) cv2.putText(output, str(digit), (x_digit - 5, y_digit - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) except KeyError: # 如果匹配不到,标记为未知 digits.append('?') cv2.rectangle(output, (x_digit, y_digit), (x_digit + w_digit, y_digit + h_digit), (0, 0, 255), 1) cv2.putText(output, '?', (x_digit - 5, y_digit - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) # 输出识别结果 if digits: # 根据你的数码管位数调整格式,这里假设是6位:XX.XX.XX print(u"{}{}.{}{}.{}{} °C".format(*digits) if len(digits)==6 else f"识别结果: {''.join(map(str, digits))} °C") else: print("未识别到任何数字!") # 显示结果 cv2.imshow("原始图像", image) cv2.imshow("数码管ROI", roi) cv2.imshow("阈值处理后", threshold2) cv2.imshow("识别结果", output) cv2.waitKey(0) cv2.destroyAllWindows()
关键修复说明
解决轮廓为空的报错:
- 添加了轮廓检测的判断逻辑,如果没有检测到数码管或数字轮廓,会给出提示并提前退出,避免后续报错
- 调整了数字轮廓的宽高筛选条件,适配更多可能的数码管尺寸(你可以根据自己的图像再微调
w_digit和h_digit的范围)
阈值处理修正:
- 移除了冗余的阈值代码,统一使用
THRESH_BINARY_INV + cv2.THRESH_OTSU自动计算最佳阈值,确保七段管的亮部是白色(非零像素),这样后续的段检测逻辑才能正确工作
- 移除了冗余的阈值代码,统一使用
变量与坐标错误修复:
- 原代码中错误使用未定义的
thresh变量,改为正确的threshold2 - 初始化了
output图像(output = roi.copy()),避免后续绘制时出错 - 段检测的坐标计算从使用全局的
w/h改为当前数字ROI的roiW/roiH,解决了坐标错位导致的段检测错误
- 原代码中错误使用未定义的
鲁棒性提升:
- 添加了图像加载失败的判断
- 对
DIGITS_LOOKUP的匹配添加了异常捕获,避免因段检测错误导致程序崩溃 - 增加了中间步骤的图像显示(如阈值处理后的图像),方便调试
内容的提问来源于stack exchange,提问作者Mansi Shukla
相关产品推荐
相关产品推荐

