You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

七段数码管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()

关键修复说明

  1. 解决轮廓为空的报错:

    • 添加了轮廓检测的判断逻辑,如果没有检测到数码管或数字轮廓,会给出提示并提前退出,避免后续报错
    • 调整了数字轮廓的宽高筛选条件,适配更多可能的数码管尺寸(你可以根据自己的图像再微调w_digit和h_digit的范围)
  2. 阈值处理修正:

    • 移除了冗余的阈值代码,统一使用THRESH_BINARY_INV + cv2.THRESH_OTSU自动计算最佳阈值,确保七段管的亮部是白色(非零像素),这样后续的段检测逻辑才能正确工作
  3. 变量与坐标错误修复:

    • 原代码中错误使用未定义的thresh变量,改为正确的threshold2
    • 初始化了output图像(output = roi.copy()),避免后续绘制时出错
    • 段检测的坐标计算从使用全局的w/h改为当前数字ROI的roiW/roiH,解决了坐标错位导致的段检测错误
  4. 鲁棒性提升:

    • 添加了图像加载失败的判断
    • 对DIGITS_LOOKUP的匹配添加了异常捕获,避免因段检测错误导致程序崩溃
    • 增加了中间步骤的图像显示(如阈值处理后的图像),方便调试

内容的提问来源于stack exchange,提问作者Mansi Shukla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:29:36