使用python对表格图像中的内部表进行计数

使用python对表格图像中的内部表进行计数,python,Python,我得到了一个图像(一个矩形的表格),它又被分成了一些内部表格。问题是使用python获取这些内部表的计数。有人能告诉我怎么做吗?检查下面的示例图像 图像示例 我需要获得count=78个表您可以将图像转换为二进制,计算图像行上的像素强度之和,并计算其中的峰值,然后计算图像列上的像素强度之和,并计算其中的峰值。这是图像中水平线和垂直线的数量。然后N=(水平线的数量-1)*(垂直线的数量-1) 示例代码(使用and): 来自PIL导入映像的>> >>>将numpy作为np导入 >>>im=np.a

我得到了一个图像(一个矩形的表格),它又被分成了一些内部表格。问题是使用python获取这些内部表的计数。有人能告诉我怎么做吗?检查下面的示例图像

图像示例


我需要获得count=78个表

您可以将图像转换为二进制,计算图像行上的像素强度之和,并计算其中的峰值,然后计算图像列上的像素强度之和,并计算其中的峰值。这是图像中水平线和垂直线的数量。然后N=(水平线的数量-1)*(垂直线的数量-1)

示例代码(使用and):

来自PIL导入映像的
>>
>>>将numpy作为np导入
>>>im=np.array(Image.open('cJyt5.png')。convert('L'))
>>>阈值=np.直方图(im,2)[1][1]
>>>im2=im<阈值
>>>垂直线=总和(np.平均值(im2,轴=0)>0.5)
>>>水平线=总和(np.平均值(im2,轴=1)>0.5)
>>>(垂直线-1)*(水平线-1)
78
如果线条超过1像素厚,则需要稍微更改代码,以不考虑
np.mean(…)>0.5
中的多个连续
True


一个简单的技巧是做
sum(np.max(0,np.diff(np.mean(…)>0.5))
来计算从
False
True
的变化次数。(未经测试)

我提出了一个适用于您的示例图像的解决方案,但我不能保证它适用于其他人,因为它未经测试,可能很脆弱

说明:

  • 通过将所有非白色值设置为0,将所有白色值设置为1,将图像转换为二进制
  • 查找同时包含0和1的第一条水平和垂直像素线
  • 计算该行中值为0的“挤压”像素数,然后减去1
  • 将步骤3中的水平线值乘以步骤3中的垂直线值,得到单元数
  • 依赖项:

    • PIL(
      pip安装枕
    执行:

    python main.py./path/to/grid.png


    从集合导入计数器
    从itertools导入dropwhile,groupby
    导入系统
    从PIL导入图像
    def将_转换为_二进制(im):
    #首先转换为8位值,然后转换为二进制值。
    返回im.convert('L').point(如果v<255,则lambda v:0,否则为1,模式为1'))
    定义第一个交点(像素2d):
    #如果它只有一个像素值,它就是一个边框
    返回下一个(dropwhile(lambda x:len(set(x))==1,像素2d))
    def单元_相交(像素1d):
    #计算非连续0的数量并减去1。
    返回计数器([g[0]表示groupby中的g(像素1d)])[0]-1
    def转置(lst):
    返回列表(地图(列表,邮政编码(*lst)))
    def重塑E2D(lst,尺寸):
    为范围(0,len(lst),size)中的i返回[lst[i:i+size]
    def main():
    使用Image.open(sys.argv[1])作为img:
    bin\u data=list(将\u转换为\u二进制(img).getdata())
    断言(已排序(计数器(bin_数据).values())==
    已排序(计数器(img.getdata()).values())
    bin_lst=重塑2D(bin_数据,img.宽度)
    水平=第一个交叉点(bin\u lst)
    垂直=第一个交叉点(换位(本底))
    打印(单元格相交(水平)*单元格相交(垂直))
    如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
    main()
    
    @RRSS完成。现在请考虑投票/接受答案。谢谢感谢@fferri提供的sol。它可以在给定的图像和小比例图像上工作,但在大比例图像上无法工作,即使按照您的建议进行了少量更改。(大比例尺意味着图像是由相机实时拍摄的)哦,恐怕你的问题是关于在你的计算机生成的图像中用1像素粗线识别表格。您可以很容易地绕过该限制,但它肯定不适用于非直线非轴对齐线。对于来自摄影机的图像,我建议使用opencv,尤其是对输入图像应用hough变换。打开另一个问题,我将在那里回答。@RRSS请记住包含您要识别的图像的实际示例,即来自摄影机的图像。我在这一点上遇到了一些错误“断言(已排序(计数器(bin_数据)。值())==已排序(计数器(img.getdata())。值()”,它表示“断言错误”。该错误是否发生在示例图像中?如果在断言之前创建计数器并打印出内容,您会得到什么?这是我得到的:
    Counter({1:95882,0:7203})Counter({(255,255,255,255):95882,(218,220,221,255):7203})
    >>> from PIL import Image
    >>> import numpy as np
    >>> im = np.array(Image.open('cJyt5.png').convert('L'))
    >>> threshold = np.histogram(im, 2)[1][1]
    >>> im2 = im < threshold
    >>> vert_lines = sum(np.mean(im2, axis=0) > 0.5)
    >>> horiz_lines = sum(np.mean(im2, axis=1) > 0.5)
    >>> (vert_lines - 1) * (horiz_lines - 1)
    78
    
    from collections import Counter
    from itertools import dropwhile, groupby
    import sys
    
    from PIL import Image
    
    
    def convert_to_binary(im):
        # first converts to 8-bit values, then to binary.
        return im.convert('L').point(lambda v: 0 if v < 255 else 1, mode='1')
    
    
    def first_intersection(pixels2d):
        # if it only has one pixel value it's a border
        return next(dropwhile(lambda x: len(set(x)) == 1, pixels2d))
    
    
    def cells_intersected(pixels1d):
        # count the number of non-consecutive 0s and subtracts 1.
        return Counter([g[0] for g in groupby(pixels1d)])[0] - 1
    
    
    def transpose(lst):
        return list(map(list, zip(*lst)))
    
    
    def reshape2d(lst, size):
        return [lst[i:i + size] for i in range(0, len(lst), size)]
    
    
    def main():
        with Image.open(sys.argv[1]) as img:
            bin_data = list(convert_to_binary(img).getdata())
            assert(sorted(Counter(bin_data).values()) == 
                   sorted(Counter(img.getdata()).values()))
            bin_lst = reshape2d(bin_data, img.width)
            horizontal = first_intersection(bin_lst)
            vertical = first_intersection(transpose(bin_lst))
            print(cells_intersected(horizontal) * cells_intersected(vertical))
    
    
    if __name__ == '__main__':
        main()