表格识别1-使用python-opencv实现表格识别

最近学习了一下opencv,做了个简单的小东西,就是识别图片中的表格,识别完成后再把数据写入到csv中保存起来。

环境准备:

下面先说下我们应该要准备下啥环境:
1.python安装完成(笔者python3.7)
2.tesseract(google的开源cr框架)
3.pytesseract(python对tesseract的封装,调用时是通过pytesseract去调用tesseract)
4.python的一些类库,numpy,matplotlib
5.特别提一下要导入opencv的包,安装的时候名称是opencv-python,使用的时候名称是cv2,
cv2不是说是opencv的版本二哦,意思是在cv的基础上做了提升和优化的意思,跟版本没关系。
这里还有一点要注意的,使用cv2报错的话,可能还要安装一下opencv-python-headless。

识别步骤:

我们先来简单说一下原理,是怎么识别表格的呢?是这样的,如果直接咱们把一张图片用tesseract去识别成中文,它会把表格的横竖线都算进去,
很多汉字都识别不出来,效果也很难看。所以我们要做的前置步骤就是识别出excel图片的一个个单元格,把每个单元格中的信息给识别出来,然后拼接
成一个csv,在最后转为excel只需要通过excel导入csv的数据就可以了。

注意事项:本人水平有限,目前还只能处理比较简单的方方正正的表格,下面的程序是可直接跑的。但是如果有一些图片的表格是拍出来斜着的,或有干扰物
比如有支笔在图片中,或有很复杂的水印,这种目前还处理不了,还在继续研究当中。有兴趣的小伙伴可以一起玩啊,加个微信一起讨论交流。

不过咱们也对这些情况有一些方案,还在处理当中:
1.如果表格是倾斜的咱们可以使用透射变换先将表格处理成正常的矩形;
2.简单的水印,可以通过灰度之后,选个合理的阈值,将图片二值化后,白黑图可以去掉水印。

这里咱们安利一个很好的python-opencv的入门教程:github地址

好了废话说完了,开始教程:
原图:

1.读入图片灰度化:

可以理解为把彩色图片变为灰色图片

raw = cv2.imread(src, 1)
# 灰度图片
gray = cv2.cvtColor(raw, cv2.COLOR_BGR2GRAY)
2.图片二值化:

可以理解为把图片变成只有黑白两种颜色,这样更方便处理,再说咱们处理表格也不需要颜色

binary = cv2.adaptiveThreshold(~gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 35, -5)
# 展示图片
cv2.imshow("binary_picture", binary)

二值化的图片:

3.识别出横线,竖线:

在此之后,如果图像不够清晰或者有小像素点,可以使用腐蚀,膨胀等操作让图片更清晰一点

rows, cols = binary.shape
scale = 40
# 自适应获取核值
# 识别横线:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (cols // scale, 1))
eroded = cv2.erode(binary, kernel, iterations=1)
dilated_col = cv2.dilate(eroded, kernel, iterations=1)
cv2.imshow("excel_horizontal_line", dilated_col)

# 识别竖线:
scale = 20
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, rows // scale))
eroded = cv2.erode(binary, kernel, iterations=1)
dilated_row = cv2.dilate(eroded, kernel, iterations=1)
cv2.imshow("excel_vertical_line:", dilated_row)

横线竖线的图:

4.计算出横竖线的焦点,就得到了每个单元格的坐标
# 将识别出来的横竖线合起来
bitwise_and = cv2.bitwise_and(dilated_col, dilated_row)
cv2.imshow("excel_bitwise_and", bitwise_and)

# 标识表格轮廓
merge = cv2.add(dilated_col, dilated_row)
cv2.imshow("entire_excel_contour:", merge)

# 两张图片进行减法运算,去掉表格框线
merge2 = cv2.subtract(binary, merge)
cv2.imshow("binary_sub_excel_rect", merge2)

new_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
erode_image = cv2.morphologyEx(merge2, cv2.MORPH_OPEN, new_kernel)
cv2.imshow('erode_image2', erode_image)
merge3 = cv2.add(erode_image, bitwise_and)
cv2.imshow('merge3', merge3)

# 将焦点标识取出来
ys, xs = np.where(bitwise_and > 0)

焦点图:

5.将坐标筛选下,临近的几个算一个:

这里说一下图片的数组中是(y,x)这样的,y在前面

# 横纵坐标数组
y_point_arr = []
x_point_arr = []
# 通过排序,排除掉相近的像素点,只取相近值的最后一点
# 这个10就是两个像素点的距离,不是固定的,根据不同的图片会有调整,基本上为单元格表格的高度(y坐标跳变)和长度(x坐标跳变)
i = 0
sort_x_point = np.sort(xs)
for i in range(len(sort_x_point) - 1):
    if sort_x_point[i + 1] - sort_x_point[i] > 10:
        x_point_arr.append(sort_x_point[i])
    i = i + 1
# 要将最后一个点加入
x_point_arr.append(sort_x_point[i])

i = 0
sort_y_point = np.sort(ys)
# print(np.sort(ys))
for i in range(len(sort_y_point) - 1):
    if (sort_y_point[i + 1] - sort_y_point[i] > 10):
        y_point_arr.append(sort_y_point[i])
    i = i + 1
y_point_arr.append(sort_y_point[i])

6.通过坐标把每个单元格图像取出来,然后使用pytesseract识别文字 :去除特殊符号后,data就是处理好的值

# 循环y坐标,x坐标分割表格
data = [[] for i in range(len(y_point_arr))]
for i in range(len(y_point_arr) - 1):
    for j in range(len(x_point_arr) - 1):
        # 在分割时,第一个参数为y坐标,第二个参数为x坐标
        cell = src[y_point_arr[i]:y_point_arr[i + 1], x_point_arr[j]:x_point_arr[j + 1]]
        cv2.imshow("sub_pic" + str(i) + str(j), cell)

        # 读取文字,此为默认英文
        # pytesseract.pytesseract.tesseract_cmd = 'E:/Tesseract-OCR/tesseract.exe'
        text1 = pytesseract.image_to_string(cell, lang="chi_sim+eng")

        # 去除特殊字符
        text1 = re.findall(r'[^\*"/:?\\|<>″′‖ 〈\n]', text1, re.S)
        text1 = "".join(text1)
        print('单元格图片信息:' + text1)
        data[i].append(text1)
        j = j + 1
    i = i + 1
6.最后把所有的信息写入csv

path是要写入的文件路径,data是数据

with open(path, "w", newline='') as csv_file:
      writer = csv.writer(file, dialect='excel')
      for index, item in enumerate(data):
          if index != 0 and index != len(data) - 1:
              writer.writerows([[item[0], item[1], item[2], item[3], item[4], item[5]]])

写入表格数据:

总结:

1.完成之后就大致知道了表格识别的原理,并且对opencv的api有了一定的了解和熟悉,这里放上github的地址:项目地址,觉得还可以点点star,fork啥的哈。
2.不过这个介绍是说完了大致流程,其实过程中还有遇到很多坑,直接运行github的项目有可能跑出的结果和我不一样,
那是因为一个是要下载tesseract的中文数据集,第二是这个里面数学和几个文字竟然识别不出来,需要手动给tesseract增加
一些训练集。这个增加训练数据集应该后面会再出一篇文章。
3.弄完这个表格识别,准备在看下图片矫正和去除水印,复杂的表格识别也会使用到的。

参考资料:

1.https://github.com/tesseract-ocr/tesseract
2.https://pypi.org/project/pytesseract/
3.https://blog.csdn.net/muxiong0308/article/details/80969355
4.https://www.cnblogs.com/HL-space/p/10547259.html

欢迎关注我的公众号:我想问问天,更多干货等着你~

  • 67
    点赞
  • 274
    收藏
    觉得还不错? 一键收藏
  • 30
    评论
实现这个任务,需要使用Python中的OpenCV和Pandas库。具体步骤如下: 1. 首先,使用OpenCV读取图片,将其转换为灰度图像并进行阈值处理,以便于检测表格线。 ```python import cv2 import numpy as np # 读取图片并转换为灰度图像 img = cv2.imread('image.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 应用阈值处理,以便于检测表格线 thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] ``` 2. 检测表格线。我们可以使用HoughLinesP函数来检测直线,然后筛选出水平和垂直线。 ```python # 检测表格线 horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 1)) vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 25)) horizontal_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel) vertical_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel) # 筛选出水平和垂直线 lines = cv2.HoughLinesP(horizontal_lines + vertical_lines, 1, np.pi/180, 100, minLineLength=100, maxLineGap=10) ``` 3. 将检测到的表格线画出来,并将表格线的坐标存储在列表中。 ```python # 画出检测到的表格线 for line in lines: x1, y1, x2, y2 = line[0] cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 存储表格线的坐标 horizontal_points = [] vertical_points = [] for line in lines: x1, y1, x2, y2 = line[0] if abs(x1 - x2) < 10: vertical_points.append((x1, y1)) elif abs(y1 - y2) < 10: horizontal_points.append((x1, y1)) ``` 4. 根据表格线的坐标,将表格分割成单元格。 ```python # 对表格线的坐标进行排序 horizontal_points = sorted(horizontal_points, key=lambda x: x[1]) vertical_points = sorted(vertical_points, key=lambda x: x[0]) # 将表格分割成单元格 cells = [] for i in range(len(horizontal_points) - 1): for j in range(len(vertical_points) - 1): x1, y1 = vertical_points[j] x2, y2 = vertical_points[j+1] x3, y3 = horizontal_points[i] x4, y4 = horizontal_points[i+1] cell = img[y3:y4, x1:x2] cells.append(cell) ``` 5. 使用Pandas库将单元格数据转换为Excel文件。 ```python import pandas as pd # 将单元格数据转换为DataFrame data = [] for cell in cells: gray_cell = cv2.cvtColor(cell, cv2.COLOR_BGR2GRAY) text = pytesseract.image_to_string(gray_cell, lang='eng', config='--psm 6') data.append(text.strip()) df = pd.DataFrame(np.array(data).reshape(len(horizontal_points) - 1, len(vertical_points) - 1)) # 将DataFrame写入Excel文件 df.to_excel('table.xlsx', index=False, header=False) ``` 完整代码如下:
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值