Python 使用OpenCV检测表图像中的行数和列数

Python 使用OpenCV检测表图像中的行数和列数,python,image,opencv,image-processing,computer-vision,Python,Image,Opencv,Image Processing,Computer Vision,如何通过Opencv获得图像表中的行数和列数 用于在表中获取我正确获取的框的代码 contours, hierarchy = cv2.findContours(img_final_bin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) def sort_contours(cnts, method="left-to-right"): # initialize the reverse flag and sort index reverse = False i =

如何通过Opencv获得图像表中的行数和列数

用于在表中获取我正确获取的框的代码

contours, hierarchy = cv2.findContours(img_final_bin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

def sort_contours(cnts, method="left-to-right"):
# initialize the reverse flag and sort index
reverse = False
i = 0
# handle if we need to sort in reverse
if method == "right-to-left" or method == "bottom-to-top":
    reverse = True
# handle if we are sorting against the y-coordinate rather than
# the x-coordinate of the bounding box
if method == "top-to-bottom" or method == "bottom-to-top":
    i = 1
# construct the list of bounding boxes and sort them from top to
# bottom
boundingBoxes = [cv2.boundingRect(c) for c in cnts]
(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
    key=lambda b:b[1][i], reverse=reverse))
# return the list of sorted contours and bounding boxes
return (cnts, boundingBoxes)

(contours, boundingBoxes) = sort_contours(contours, method="top-to-bottom")

似乎一个简单的解决方案是首先从左到右查看每个像素是否为黑色(这表示我们找到了一列。然后对行执行相同的操作(如果从上到下每个像素为黑色,则表示它找到了一行)

一个复杂的问题是行的宽度,这意味着在找到白色之前,只能将其计算为1行/列


我可以为此编写代码,但我现在不在家,所以也许其他人可以编写代码,我稍后会删除我的答案。我知道这可能是一条评论,但我没有50%的声誉。

这里有一个可能的方法:

  • 获取二值图像。加载图像,转换为灰度,然后

  • 删除单元格内的文本。查找轮廓并使用过滤器通过填充轮廓来删除文本

  • 反转图像。我们反转图像,使单元格为白色,背景为黑色

  • 对单元格进行排序并对行/列求和。然后,我们使用从
    上到下对轮廓进行排序。接下来,我们迭代轮廓并找到相应的值以获得
    (cX,cY)
    坐标。我们可以比较每个单元格的
    cY
    值,通过使用偏移量来确定它是新行还是同一行中的单元格。如果
    cY
    值是+/-某个偏移量值,则单元格应该在同一行中。如果大于该值,则表示该单元格在新行中。我们构建了一个模型表,其中表的长度为行,而任何索引的长度为列数


  • 二值图像

    删除文本轮廓+反转图像

    这是一个通过迭代每个单元格来计算行数和列数的可视化过程

    结果

    Rows: 7
    Columns: 4
    
    代码

    将numpy导入为np
    从imutils导入等高线
    进口cv2
    #加载图像、灰度、高斯模糊、大津阈值
    image=cv2.imread('1.jpg')
    灰色=cv2.CVT颜色(图像,cv2.COLOR\u BGR2GRAY)
    模糊=cv2.高斯模糊(灰色,(5,5),0)
    thresh=cv2.threshold(模糊,0,255,cv2.thresh\u二进制\u INV+cv2.thresh\u OTSU)[1]
    #查找轮廓并删除单元格内的文本
    cnts=cv2.查找对象(阈值、cv2.RETR\u树、cv2.链近似值、简单值)
    如果len(cnts)==2个其他cnts[1],则cnts=cnts[0]
    对于碳纳米管中的碳:
    面积=cv2。轮廓面积(c)
    如果面积小于4000:
    cv2.拉深轮廓(阈值[c],-1,0,-1)
    #反转图像
    反转=255-阈值
    偏移量,旧值,第一个=10,0,真值
    可视化=cv2.CVT颜色(反转,cv2.COLOR_GRAY2BGR)
    #找到等高线,从上到下排序,然后汇总列/行
    cnts=cv2.findContours(反转、cv2.RETR\u树、cv2.CHAIN\u近似、简单)
    如果len(cnts)==2个其他cnts[1],则cnts=cnts[0]
    (CNT,u)=等高线。等高线排序(CNT,方法=“从上到下”)
    对于碳纳米管中的碳:
    #找到质心
    M=cv2.力矩(c)
    cX=int(M[“m10”]/M[“m00”])
    cY=int(M[“m01”]/M[“m00”])
    #新行
    如果(abs(cY)-abs(old_cY))>偏移量:
    如果首先:
    行,表=[],[]
    第一个=错误
    旧的
    表.追加(行)
    行=[]
    #同行单元格
    
    如果((abs(cY)-abs(old_cY))另一种方法是首先验证它是否是一个真实的表,因为该hough线变换可以使用,一旦完成,您可以使用上面由fellow解释的方法。

    请将您的示例图像添加到帖子:)由于限制,无法上载确切的文件,但以下是示例图像。需要在pandas dataframe中转换此图像。使用上述代码从图像中提取框,并使用tesserocr提取这些框中的内容。如果我们可以知道确切数字中的列数和行数,我们可以在pandas和中创建空数据框然后将方框数据放入pandas表中,从而从图像中获取pandas表。请建议是否有其他方法从该图像获取pandas表。我们如何确定面积,即4000,可以是任何预定的阈值面积值。它只是过滤掉任何小的噪声颗粒
    import numpy as np
    from imutils import contours
    import cv2
    
    # Load image, grayscale, Gaussian blur, Otsu's threshold
    image = cv2.imread('1.jpg')
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (5,5), 0)
    thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
    
    # Find contours and remove text inside cells
    cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    for c in cnts:
        area = cv2.contourArea(c)
        if area < 4000:
            cv2.drawContours(thresh, [c], -1, 0, -1)
    
    # Invert image
    invert = 255 - thresh
    offset, old_cY, first = 10, 0, True
    visualize = cv2.cvtColor(invert, cv2.COLOR_GRAY2BGR)
    
    # Find contours, sort from top-to-bottom and then sum up column/rows
    cnts = cv2.findContours(invert, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    (cnts, _) = contours.sort_contours(cnts, method="top-to-bottom")
    for c in cnts:
        # Find centroid
        M = cv2.moments(c)
        cX = int(M["m10"] / M["m00"])
        cY = int(M["m01"] / M["m00"])
    
        # New row
        if (abs(cY) - abs(old_cY)) > offset:
            if first:
                row, table = [], []
                first = False
            old_cY = cY
            table.append(row)
            row = []
    
        # Cell in same row
        if ((abs(cY) - abs(old_cY)) <= offset) or first:
            row.append(1)
    
        # Uncomment to visualize 
        '''
        cv2.circle(visualize, (cX, cY), 10, (36, 255, 12), -1) 
        cv2.imshow('visualize', visualize)
        cv2.waitKey(200)
        '''
    
    print('Rows: {}'.format(len(table)))
    print('Columns: {}'.format(len(table[1])))
    
    cv2.imshow('invert', invert)
    cv2.imshow('thresh', thresh)
    cv2.waitKey()