无法使用sort_contours构建七段数码管OCR。

5

我正在尝试构建一个OCR,用于识别如下所示的七段显示器

Original Image

使用OpenCV的预处理工具,我在这里得到了它。

threshold

现在我正在尝试按照此教程操作 - https://www.pyimagesearch.com/2017/02/13/recognizing-digits-with-opencv-and-python/

但是在这一部分中

digitCnts = contours.sort_contours(digitCnts,
    method="left-to-right")[0]
digits = []

我正在遇到错误 -

使用THRESH_BINARY_INV已经解决了该错误,但OCR仍然无法工作,任何修复方法都将是极好的

文件“/Users/ms/anaconda3/lib/python3.6/site-packages/imutils/contours.py”,第25行中的sort_contours函数:

key=lambda b: b1[i],reverse=reverse))

ValueError:解包值不足(期望2个,获得0个)

有任何想法如何解决这个问题并使我的OCR成为一个可用的模型

我的整个代码:

import numpy as np 
import cv2
import imutils
# import the necessary packages
from imutils.perspective import four_point_transform
from imutils import contours
import imutils
import cv2

# define the dictionary of digit segments so we can identify
# each digit on the thermostat
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
}

# load image
image = cv2.imread('d4.jpg')
# create hsv
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

 # set lower and upper color limits
low_val = (60,180,160)
high_val = (179,255,255)
# Threshold the HSV image 
mask = cv2.inRange(hsv, low_val,high_val)
# find contours in mask
ret, cont, hierarchy = cv2.findContours(mask,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# select the largest contour
largest_area = 0
for cnt in cont:
    if cv2.contourArea(cnt) > largest_area:
        cont = cnt
        largest_area = cv2.contourArea(cnt)

# get the parameters of the boundingbox
x,y,w,h = cv2.boundingRect(cont)

# create and show subimage
roi = image[y:y+h, x:x+w]
cv2.imshow("Result", roi)


#  draw box on original image and show image
cv2.rectangle(image, (x,y),(x+w,y+h), (0,0,255),2)
cv2.imshow("Image", image)

grayscaled = cv2.cvtColor(roi,cv2.COLOR_BGR2GRAY)
retval, threshold = cv2.threshold(grayscaled, 10, 255, cv2.THRESH_BINARY)
retval2,threshold2 = cv2.threshold(grayscaled,125,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imshow('threshold',threshold2)
cv2.waitKey(0)
cv2.destroyAllWindows()
# find contours in the thresholded image, then initialize the
# digit contours lists
cnts = cv2.findContours(threshold2.copy(), cv2.RETR_EXTERNAL,
    cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
digitCnts = []

# loop over the digit area candidates
for c in cnts:
    # compute the bounding box of the contour
    (x, y, w, h) = cv2.boundingRect(c)
    # if the contour is sufficiently large, it must be a digit
    if w >= 15 and (h >= 30 and h <= 40):
        digitCnts.append(c)
# sort the contours from left-to-right, then initialize the
# actual digits themselves
digitCnts = contours.sort_contours(digitCnts,
    method="left-to-right")[0]
digits = []


# loop over each of the digits
for c in digitCnts:
    # extract the digit ROI
    (x, y, w, h) = cv2.boundingRect(c)
    roi = thresh[y:y + h, x:x + w]

    # compute the width and height of each of the 7 segments
    # we are going to examine
    (roiH, roiW) = roi.shape
    (dW, dH) = (int(roiW * 0.25), int(roiH * 0.15))
    dHC = int(roiH * 0.05)

    # define the set of 7 segments
    segments = [
        ((0, 0), (w, dH)),  # top
        ((0, 0), (dW, h // 2)), # top-left
        ((w - dW, 0), (w, h // 2)), # top-right
        ((0, (h // 2) - dHC) , (w, (h // 2) + dHC)), # center
        ((0, h // 2), (dW, h)), # bottom-left
        ((w - dW, h // 2), (w, h)), # bottom-right
        ((0, h - dH), (w, h))   # bottom
    ]
    on = [0] * len(segments)

    # loop over the segments
    for (i, ((xA, yA), (xB, yB))) in enumerate(segments):
        # extract the segment ROI, count the total number of
        # thresholded pixels in the segment, and then compute
        # the area of the segment
        segROI = roi[yA:yB, xA:xB]
        total = cv2.countNonZero(segROI)
        area = (xB - xA) * (yB - yA)

        # if the total number of non-zero pixels is greater than
        # 50% of the area, mark the segment as "on"
        if total / float(area) > 0.5:
            on[i]= 1

    # lookup the digit and draw it on the image
    digit = DIGITS_LOOKUP[tuple(on)]
    digits.append(digit)
    cv2.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 1)
    cv2.putText(output, str(digit), (x - 10, y - 10),
        cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 255, 0), 2)
# display the digits
print(u"{}{}.{}{}.{}{} \u00b0C".format(*digits))
cv2.imshow("Input", image)
cv2.imshow("Output", output)
cv2.waitKey(0)

一个帮助在修复我的OCR方面将是伟大的。

1
如果您查看OpenCV有关轮廓的文档,它提到要查找的对象应为白色,背景应为黑色。请尝试使用THRESH_BINARY_INV而不是THRESH_BINARY。错误提示说没有找到任何轮廓。 - Rick M.
但仍然没有打印出我要求的任何内容。我请求一个OCR修复,请您在您的系统上运行并验证错误是什么,@RickM。 - Mansi Shukla
1
如果您有兴趣自己实现此排序功能(以获得更多控制权),则可以参考此答案 - ZdaR
你能否提供完整的代码作为答案?我是新手,正在按照教程学习。您的帮助将不胜感激。@DanMašek - Mansi Shukla
1
@MansiShukla 我会在今天结束前放上我尝试过的内容 :) - Rick M.
显示剩余5条评论
2个回答

2
我认为你创建的查找表适用于七位数码显示,而不是七位OCR。由于显示器的大小是固定的,我建议你尝试将其分割成独立的区域,并使用模板匹配或K-means进行识别。
以下是我的预处理步骤:
(1)在HSV中找到浅绿色的显示屏。
mask = cv2.inRange(hsv, (50, 100, 180), (70, 255, 255))

enter image description here enter image description here

(2) 尝试通过投影分离并识别标准的七位数,使用查找表: 输入图像描述 输入图像描述 (3) 在检测到的绿色显示屏上进行尝试。

enter image description here


@MansiShukla 看看这里和这里。然后将两个东西连接起来 ;) - lucians

1

所以,正如我在评论中所说,有两个问题:

  1. 你试图在白色背景上找到黑色轮廓,这与OpenCV文档相反。这可以通过使用THRESH_BINARY_INV标志来解决,而不是THRESH_BINARY

  2. 由于数字没有连接,因此无法找到数字的完整轮廓。因此,我尝试了一些形态学操作。以下是步骤:

First Threshold

2a)使用以下代码打开上面的图像:

threshold2 = cv2.morphologyEx(threshold, cv2.MORPH_OPEN, np.ones((3,3), np.uint8))

Opening

2b) 对之前的图像进行膨胀操作:

threshold2 = cv2.dilate(threshold2, np.ones((5,1), np.uint8), iterations=1)

Dilation

2c) 将图像的顶部裁剪以分离数字,因为膨胀到顶部边界:

height, width = threshold2.shape[:2]
threshold2 = threshold2[5:height,5:width]

注意,不知何故这里显示的图片没有我所说的白色边框。尝试在新窗口中打开图像,你就会明白我的意思。

Final cropping

因此,在解决这些问题后,轮廓线非常好,如下所示,与预期的一样:supposed to be

cnts = cv2.findContours(threshold2.copy(), cv2.RETR_EXTERNAL,
                        cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)

digitCnts = []

# loop over the digit area candidates
for c in cnts:
    # compute the bounding box of the contour
    (x, y, w, h) = cv2.boundingRect(c)
    # if the contour is sufficiently large, it must be a digit
    if w <= width * 0.5 and (h >= height * 0.2):
        digitCnts.append(c)
# sort the contours from left-to-right, then initialize the
# actual digits themselves
cv2.drawContours(image2, digitCnts, -1, (0, 0, 255))
cv2.imwrite("cnts-sort.jpg", image2)

如下所示,轮廓线以红色绘制。

Contours

现在,为了判断数字是否是代码,这部分有些问题,我认为是查找表的问题。如下图所示,所有数字的边界矩形都被正确地裁剪,但查找表无法识别它们。
# loop over each of the digits
j = 0
for c in digitCnts:
    # extract the digit ROI
    (x, y, w, h) = cv2.boundingRect(c)
    roi = threshold2[y:y + h, x:x + w]
    cv2.imwrite("roi" + str(j) + ".jpg", roi)
    j += 1

    # compute the width and height of each of the 7 segments
    # we are going to examine
    (roiH, roiW) = roi.shape
    (dW, dH) = (int(roiW * 0.25), int(roiH * 0.15))
    dHC = int(roiH * 0.05)

    # define the set of 7 segments
    segments = [
        ((0, 0), (w, dH)),  # top
        ((0, 0), (dW, h // 2)), # top-left
        ((w - dW, 0), (w, h // 2)), # top-right
        ((0, (h // 2) - dHC) , (w, (h // 2) + dHC)), # center
        ((0, h // 2), (dW, h)), # bottom-left
        ((w - dW, h // 2), (w, h)), # bottom-right
        ((0, h - dH), (w, h))   # bottom
    ]
    on = [0] * len(segments)

    # loop over the segments
    for (i, ((xA, yA), (xB, yB))) in enumerate(segments):
        # extract the segment ROI, count the total number of
        # thresholded pixels in the segment, and then compute
        # the area of the segment
        segROI = roi[yA:yB, xA:xB]
        total = cv2.countNonZero(segROI)
        area = (xB - xA) * (yB - yA)

        # if the total number of non-zero pixels is greater than
        # 50% of the area, mark the segment as "on"
        if area != 0:
            if total / float(area) > 0.5:
                on[i] = 1

    # lookup the digit and draw it on the image
    try:
        digit = DIGITS_LOOKUP[tuple(on)]
        digits.append(digit)
        cv2.rectangle(roi, (x, y), (x + w, y + h), (0, 255, 0), 1)
        cv2.putText(roi, str(digit), (x - 10, y - 10),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 255, 0), 2)
    except KeyError:
        continue

我已经阅读了你在问题中提到的网站,从评论中看来,LUT中的一些条目可能是错误的。所以我将把这个问题留给你去解决。以下是发现的单个数字(但未被识别):

1 7 5 8 5 1 1

或者,您可以使用Tesseract来识别这些检测到的数字。

希望能对您有所帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接