python - 使用 OpenCV 检测表格图像中的行数和列数
问题描述
我们如何通过 Opencv 获取 Image 表中的行数和列数。
获取表格中我正确的框的代码
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")
解决方案
这是一种潜在的方法:
获取二值图像。加载图像,转换为灰度,高斯模糊,然后是 Otsu 的阈值。
删除单元格内的文本。查找轮廓并使用过滤器
cv2.contourArea()
通过填充轮廓来删除文本cv2.drawContours()
反转图像。我们反转图像,使单元格为白色,背景为黑色
对单元格进行排序并对行/列求和。我们找到轮廓
top-to-bottom
,然后使用对轮廓进行排序imutils.contours.sort_contours
。接下来我们遍历轮廓并找到质心以获得(cX, cY)
坐标。这个想法是我们可以cY
通过使用偏移量来比较每个单元格的值,以确定它是新行还是同一行中的单元格。cY
如果值为 +/- 某个偏移值,则单元格应位于同一行中。如果它更大,则表示该单元格位于新行中。我们构建了一个模型表,其中表的长度为您提供行数,而任何索引的长度为您提供列数。
二进制图像
去除文字轮廓+倒置图像
这是遍历每个单元格以计算行数和列数的可视化
结果
Rows: 7
Columns: 4
代码
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()
推荐阅读
- java - 从 Oracle NOSQL 检索批量数据时,获取类型无法创建 Double:类 oracle.kv.impl.api.table.IntegerDefImpl
- c# - 如何导入带有小数值的 CSV 文件,避免将小数作为日期格式
- javascript - AngularJS:向 Angular-rating-icons 添加工具提示?
- java - Selenium Web 驱动程序不从 java 中的 cssSelector 返回值
- html - 不寻常的弹性盒行为
- html - 如何更改访问过的帖子颜色,或在上面贴上徽章?
- elixir - 关于 Gettext 模块上的 pattern_match_cov 的透析器警告
- c - 指向作为函数参数的指针的指针
- javascript - 如何使用相同的函数创建不同的对象?
- algorithm - 找到一个具有最大点数的圆 ((x,y,r));给定二维平面中的一组点(x,y)