【问题标题】:detect checkboxes from a form using opencv python使用opencv python从表单中检测复选框
【发布时间】:2020-10-29 05:37:45
【问题描述】:

给定牙科表格作为输入,需要使用图像处理找到表格中存在的所有复选框。我在下面回答了我目前的方法。有没有更好的方法来查找低质量文档的复选框?

样本输入:

【问题讨论】:

    标签: python opencv image-processing information-retrieval opencv-python


    【解决方案1】:

    这是我们可以解决问题的一种方法,

    import cv2
    import numpy as np
    image=cv2.imread('path/to/image.jpg')
    
    ### binarising image
    gray_scale=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    th1,img_bin = cv2.threshold(gray_scale,150,225,cv2.THRESH_BINARY)
    

    定义垂直和水平内核

    lineWidth = 7
    lineMinWidth = 55
    kernal1 = np.ones((lineWidth,lineWidth), np.uint8)
    kernal1h = np.ones((1,lineWidth), np.uint8)
    kernal1v = np.ones((lineWidth,1), np.uint8)
    
    kernal6 = np.ones((lineMinWidth,lineMinWidth), np.uint8)
    kernal6h = np.ones((1,lineMinWidth), np.uint8)
    kernal6v = np.ones((lineMinWidth,1), np.uint8)
    

    检测水平线

    img_bin_h = cv2.morphologyEx(~img_bin, cv2.MORPH_CLOSE, kernal1h) # bridge small gap in horizonntal lines
    img_bin_h = cv2.morphologyEx(img_bin_h, cv2.MORPH_OPEN, kernal6h) # kep ony horiz lines by eroding everything else in hor direction
    

    寻找垂直线

    ## detect vert lines
    img_bin_v = cv2.morphologyEx(~img_bin, cv2.MORPH_CLOSE, kernal1v)  # bridge small gap in vert lines
    img_bin_v = cv2.morphologyEx(img_bin_v, cv2.MORPH_OPEN, kernal6v)# kep ony vert lines by eroding everything else in vert direction
    

    合并垂直和水平线以获得块。添加膨胀层以消除小间隙

    ### function to fix image as binary
    def fix(img):
        img[img>127]=255
        img[img<127]=0
        return img
    
    img_bin_final = fix(fix(img_bin_h)|fix(img_bin_v))
    
    finalKernel = np.ones((5,5), np.uint8)
    img_bin_final=cv2.dilate(img_bin_final,finalKernel,iterations=1)
    

    对二进制图像应用连通分量分析以获得所需的块。

    ret, labels, stats,centroids = cv2.connectedComponentsWithStats(~img_bin_final, connectivity=8, ltype=cv2.CV_32S)
    
    ### skipping first two stats as background
    for x,y,w,h,area in stats[2:]:
        cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)
    

    【讨论】:

    • 非常好的解决方案!
    • 嗨@Sreekiran,感谢您的解决方案,它为我节省了一个小时,但是在检测到这些框后,我怎样才能将它们构建成一行?
    【解决方案2】:

    你也可以使用contours来解决这个问题。

    # Reading the image in grayscale and thresholding it
    Image = cv2.imread("findBox.jpg", 0)
    ret, Thresh = cv2.threshold(Image, 100, 255, cv2.THRESH_BINARY)
    

    现在执行两次膨胀和腐蚀以连接框内的虚线。

    kernel = np.ones((3, 3), dtype=np.uint8)
    Thresh = cv2.dilate(Thresh, kernel, iterations=2)
    Thresh = cv2.erode(Thresh, kernel, iterations=2)
    

    在带有 cv2.RETR_TREE 标志的图像中查找轮廓以获取所有具有父子关系的轮廓。 For more info on this.

    Contours, Hierarchy = cv2.findContours(Thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    

    现在检测到图像中的所有框以及所有字母。我们必须消除检测到的字母、非常小的轮廓(由于噪声),以及那些内部包含较小框的框。

    为此,我正在运行一个 for 循环迭代检测到的所有轮廓,并使用此循环将每个轮廓的 3 个值保存在 3 个不同的列表中。

    • 第一个值:轮廓面积(轮廓包围的像素数)
    • 第二个值:Contour 的bounding rectangle 信息。
    • 第三个值:轮廓面积与其边界矩形面积之比。
    Areas = []
    Rects = []
    Ratios = []
    for Contour in Contours:
        # Getting bounding rectangle
        Rect = cv2.boundingRect(Contour)
    
        # Drawing contour on new image and finding number of white pixels for contour area
        C_Image = np.zeros(Thresh.shape, dtype=np.uint8)
        cv2.drawContours(C_Image, [Contour], -1, 255, -1)
        ContourArea = np.sum(C_Image == 255)
    
        # Area of the bounding rectangle
        Rect_Area = Rect[2]*Rect[3]
        
        # Calculating ratio as explained above
        Ratio = ContourArea / Rect_Area
       
        # Storing data
        Areas.append(ContourArea)
        Rects.append(Rect)
        Ratios.append(Ratio)
    

    过滤掉不需要的轮廓:

    • 获取面积小于 3600(此图像的阈值)且比率 >= 0.99 的轮廓的索引。 该比率定义轮廓与其边界矩形的重叠程度。在这种情况下,所需的轮廓是矩形的,它们的这个比率预计为“1.0”(0.99 用于保持小噪声阈值)。
    BoxesIndices = [i for i in range(len(Contours)) if Ratios[i] >= 0.99 and Areas[i] > 3600]
    
    • 现在最终轮廓是索引“BoxesIndices”处的轮廓中没有子轮廓的轮廓(这将提取最里面的轮廓),如果它们有子轮廓,那么这个子轮廓不应该是索引处的轮廓之一“BoxesIndices”。
    FinalBoxes = [Rects[i] for i in BoxesIndices if Hierarchy[0][i][2] == -1 or BoxesIndices.count(Hierarchy[0][i][2]) == 0]
    

    Final output image

    【讨论】:

    • 尝试运行我们的代码,但得到了这个伙伴:C_Image = np.zeros(Gray.shape, dtype=np.uint8) NameError: name 'Gray' is not defined
    • @Chau,只需将 Gray.shape 更改为 Thresh.shape。我在输入答案时犯了一个错误。对不起
    猜你喜欢
    • 2020-11-14
    • 1970-01-01
    • 2017-04-30
    • 2015-05-24
    • 1970-01-01
    • 2019-09-24
    • 1970-01-01
    • 2019-09-11
    • 2016-07-05
    相关资源
    最近更新 更多