如何识别图片中的表格数据

??在很多时候,我们的数据来源形式是多种多样的,有时候数据(或表格)也会呈现在图片中。那么,我们如何来获取图片中的有用数据呢?当一张图片中含有表格数据的时候,我们可以用OpenCV识别表格中的直线,然后再用OCR技术识别其中的文字。

??本文仅作为如何识别图片中的表格的一个例子,希望能给读者一些启示。笔者用到的工具如下:

  • opencv
  • pyteressact
  • numpy

我们用opencv来识别表格中的直线,用pyteressact来识别单元格文字,用numpy做数值处理。我们要识别的示例图片(AI.png)如下:

我们分以下几步进行识别:

  1. 识别表格中的横线,即分割记录(每一行)的横线;
  2. 识别表格中的竖线,即每个列的分割线;
  3. 找到数据所在的单元格;
  4. 利用pyteressact识别单元格的文字。

识别表格中的横线

??识别横线之前,我们先创建一个图片表格识别类(ImageTableOCR),如下:

# -*- coding: utf-8 -*-
import cv2
import pytesseract
import numpy as np

class ImageTableOCR(object):

    # 初始化
    def __init__(self, ImagePath):
        # 读取图片
        self.image = cv2.imread(ImagePath, 1)
        # 把图片转换为灰度模式
        self.gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)

其中self.image为RGB模块的图片,self.gray为灰度模式的图片。

??接下来,我们识别图片中的分割两条记录的横线。注意到,相邻两条记录之间的颜色是不一致的,因此,我们利用图片灰度化后,每一行像素的平均值的差的绝对值来作为相邻两条记录的分割线,这样就能检测出分割两条记录的横线了。具体的识别横线的函数的Python代码如下:(接以上代码)

    # 横向直线检测
    def HorizontalLineDetect(self):

        # 图像二值化
        ret, thresh1 = cv2.threshold(self.gray, 240, 255, cv2.THRESH_BINARY)
        # 进行两次中值滤波
        blur = cv2.medianBlur(thresh1, 3)  # 模板大小3*3
        blur = cv2.medianBlur(blur, 3)  # 模板大小3*3

        h, w = self.gray.shape

        # 横向直线列表
        horizontal_lines = []
        for i in range(h - 1):
            # 找到两条记录的分隔线段,以相邻两行的平均像素差大于120为标准
            if abs(np.mean(blur[i, :]) - np.mean(blur[i + 1, :])) > 120:
                # 在图像上绘制线段
                horizontal_lines.append([0, i, w, i])
                cv2.line(self.image, (0, i), (w, i), (0, 255, 0), 2)

        horizontal_lines = horizontal_lines[1:]
        # print(horizontal_lines)
        return horizontal_lines

首先对图片进行二值化处理,再进行两次中值滤波,这样是为了使相邻两条记录之间的像素区别尽可能大。然后对该图片中的每一行的像素进行检测, 以相邻两行的平均像素差大于120为标准, 识别出分割两条记录的横线。识别后的横线如下:(图片中的绿色线段)

识别表格中的竖线

??在这一步中,我们利用opencv中的Hough直线检测方法来检测图片中的竖线。完整的Python代码如下:(接以上代码)

    #  纵向直线检测
    def VerticalLineDetect(self):
        # Canny边缘检测
        edges = cv2.Canny(self.gray, 30, 240)

        # Hough直线检测
        minLineLength = 500
        maxLineGap = 30
        lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength, maxLineGap).tolist()
        lines.append([[13, 937, 13, 102]])
        lines.append([[756, 937, 756, 102]])
        sorted_lines = sorted(lines, key=lambda x: x[0])

        # 纵向直线列表
        vertical_lines = []
        for line in sorted_lines:
            for x1, y1, x2, y2 in line:
                # 在图片上绘制纵向直线
                if x1 == x2:
                    print(line)
                    vertical_lines.append((x1, y1, x2, y2))
                    cv2.line(self.image, (x1, y1), (x2, y2), (0, 0, 255), 2)

        return vertical_lines

首先我们对灰度图片进行Canny边缘检测,在此基础上再利用Hough直线检测方法识别图片中的直线,要求识别的最大间距为30,线段长度最小为500,并且为竖直直线(x1 == x2),当然,也有一些人为的因素,那就是笔者自己添加了两条竖直直线([[13, 937, 13, 102]],[[756, 937, 756, 102]])。运行上述方法,输出的结果如下:

[[13, 937, 13, 102]]

[[75, 937, 75, 102]]

[[77, 937, 77, 102]]

[[270, 937, 270, 104]]

[[272, 937, 272, 102]]

[[756, 937, 756, 102]]

识别竖直直线后的图片如下:(图片中的红色线段)

可以看到,图片六条竖直的线段都已经完整标记出来了。

识别图片中的单元格

??在识别图片中的单元格之前,我们先来识别每个单元格所在的顶点,也就是上述识别后的横线与竖线的交点。完整的Python代码如下:(接以上代码)

    # 顶点检测
    def VertexDetect(self):
        vertical_lines = self.VerticalLineDetect()
        horizontal_lines = self.HorizontalLineDetect()

        # 顶点列表
        vertex = []
        for v_line in vertical_lines:
            for h_line in horizontal_lines:
                vertex.append((v_line[0], h_line[1]))

        #print(vertex)

        # 绘制顶点
        for point in vertex:
            cv2.circle(self.image, point, 1, (255, 0, 0), 2)

        return vertex

顶点检测后的图片如下:(图片中的蓝色点即为每个单元格的顶点)

由此可见,我们识别出来的单元格的顶点是正确的。接着,我们把这些单元格取出来,代码如下:(接以上代码)

# 寻找单元格区域
    def CellDetect(self):
        vertical_lines = self.VerticalLineDetect()
        horizontal_lines = self.HorizontalLineDetect()

        # 顶点列表
        rects = []
        for i in range(0, len(vertical_lines) - 1, 2):
            for j in range(len(horizontal_lines) - 1):
                rects.append((vertical_lines[i][0], horizontal_lines[j][1],                               vertical_lines[i + 1][0], horizontal_lines[j + 1][1]))

        # print(rects)
        return rects

以第一个单元格为例,其图像如下:

识别单元格的文字

??在识别出图片中表格的单元格后,我们可以对该单元格图片进行文字识别,我们使用的OCR工具为Teressact, 其Python的接口为pyteressact 。具体的Python代码如下:(接以上代码)

# 识别单元格中的文字
    def OCR(self):
        rects = self.CellDetect()
        thresh = self.gray

        # 特殊字符列表
        special_char_list = ‘ `[email protected]#$%^&*()-_=+[]{}|\\;:‘’,。《》/?ˇ‘
        for i in range(20):
            rect1 = rects[i]
            DetectImage1 = thresh[rect1[1]:rect1[3], rect1[0]:rect1[2]]

            # Tesseract所在的路径
            pytesseract.pytesseract.tesseract_cmd = ‘C://Program Files (x86)/Tesseract-OCR/tesseract.exe‘
            # 识别数字(每行第一列)
            text1 = pytesseract.image_to_string(DetectImage1, config="--psm 10")
            print(text1, end=‘-->‘)

            # 识别汉字(每行第二列)
            rect2 = rects[i+20]
            DetectImage2 = thresh[rect2[1]:rect2[3], rect2[0]:rect2[2]]
            text2 = pytesseract.image_to_string(DetectImage2, config=‘--psm 7‘, lang=‘chi_sim‘)
            text2 = ‘‘.join([char for char in text2 if char not in special_char_list])
            print(text2, end=‘-->‘)

            # 识别汉字(每行第三列)
            rect3 = rects[i+40]
            DetectImage3 = thresh[rect3[1]:rect3[3], rect3[0]:rect3[2]]
            text3 = pytesseract.image_to_string(DetectImage3, config=‘--psm 7‘, lang=‘chi_sim‘)
            text3 = ‘‘.join([char for char in text3 if char not in special_char_list])
            print(text3)

识别后的结果如下:

I-->度一-->开放的人一智能服务平台

2-->肌讯-->互联网综合服务

3-->标为-->人一智能自动化业务、智能屹片

4-->阿里巴巴-->互联网综合服务

5-->平安集口-->人T智能金融研发平仄

6-->华大基因-->精准检测、医疗数据运营服务

d-->搜狗-->综合人T智能解决方案平台

8-->一科大讯飞-->智能语音技术

9-->一中利创汤-->智能终端平台技术

10-->珍山集团-->SaaS级智能营销云平台

i-->商汤科技-->人工智能视觉深度学习平台

12-->神州泰岳-->综合类软件产品及服务

13-->寒武红科技-->深度学对专用的智能盂片

14-->汉王科技-->文字识别技术与智能交工

15-->全志刑技-->智能芯片设计

16-->face旷视科技-->人T智能产品和行业解夷方案

17-->创略科技-->智能客户数据平台

18-->海云数据-->企业级大数据整体运营与分析服务

19-->影渭科技-->视觉技术、智能影像生产企业

20-->智蹈智能-->智能机器人技术提供和平台运萧

下面,我们来统计一下识别的准确率。在原来的表格中,一共是20个数字加上280个汉字(包括标点)加上10个英语字母(包括标点),对于识别的结果,其中数字类识别正确17个,汉字正确256个,英语字母8个,因此,总的识别的准确率为90.6% ,识别的结果还是可以的。

总结

??本文仅作为如何识别图片中的表格的一个例子,希望能给读者一些启示。对于不同的图片表格,需要具体问题具体分析。

??虽然笔者尽可能把整个过程写得简单明了,但其中的探索过程却是很复杂的。而且值得注意的是,在本文中的检测横线的方法仅适用于相邻两条记录有颜色差别的表格图片。

??完整的Python代码如下,希望能给大家一些思考。

# -*- coding: utf-8 -*-
import cv2
import pytesseract
import numpy as np

class ImageTableOCR(object):

    # 初始化
    def __init__(self, ImagePath):
        # 读取图片
        self.image = cv2.imread(ImagePath, 1)
        # 把图片转换为灰度模式
        self.gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)

    # 横向直线检测
    def HorizontalLineDetect(self):

        # 图像二值化
        ret, thresh1 = cv2.threshold(self.gray, 240, 255, cv2.THRESH_BINARY)
        # 进行两次中值滤波
        blur = cv2.medianBlur(thresh1, 3)  # 模板大小3*3
        blur = cv2.medianBlur(blur, 3)  # 模板大小3*3

        h, w = self.gray.shape

        # 横向直线列表
        horizontal_lines = []
        for i in range(h - 1):
            # 找到两条记录的分隔线段,以相邻两行的平均像素差大于120为标准
            if abs(np.mean(blur[i, :]) - np.mean(blur[i + 1, :])) > 120:
                # 在图像上绘制线段
                horizontal_lines.append([0, i, w, i])
                # cv2.line(self.image, (0, i), (w, i), (0, 255, 0), 2)

        horizontal_lines = horizontal_lines[1:]
        # print(horizontal_lines)
        return horizontal_lines

    #  纵向直线检测
    def VerticalLineDetect(self):
        # Canny边缘检测
        edges = cv2.Canny(self.gray, 30, 240)

        # Hough直线检测
        minLineLength = 500
        maxLineGap = 30
        lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength, maxLineGap).tolist()
        lines.append([[13, 937, 13, 102]])
        lines.append([[756, 937, 756, 102]])
        sorted_lines = sorted(lines, key=lambda x: x[0])

        # 纵向直线列表
        vertical_lines = []
        for line in sorted_lines:
            for x1, y1, x2, y2 in line:
                # 在图片上绘制纵向直线
                if x1 == x2:
                    # print(line)
                    vertical_lines.append((x1, y1, x2, y2))
                    # cv2.line(self.image, (x1, y1), (x2, y2), (0, 0, 255), 2)

        return vertical_lines

    # 顶点检测
    def VertexDetect(self):
        vertical_lines = self.VerticalLineDetect()
        horizontal_lines = self.HorizontalLineDetect()

        # 顶点列表
        vertex = []
        for v_line in vertical_lines:
            for h_line in horizontal_lines:
                vertex.append((v_line[0], h_line[1]))

        #print(vertex)

        # 绘制顶点
        for point in vertex:
            cv2.circle(self.image, point, 1, (255, 0, 0), 2)

        return vertex

    # 寻找单元格区域
    def CellDetect(self):
        vertical_lines = self.VerticalLineDetect()
        horizontal_lines = self.HorizontalLineDetect()

        # 顶点列表
        rects = []
        for i in range(0, len(vertical_lines) - 1, 2):
            for j in range(len(horizontal_lines) - 1):
                rects.append((vertical_lines[i][0], horizontal_lines[j][1],                               vertical_lines[i + 1][0], horizontal_lines[j + 1][1]))

        # print(rects)
        return rects

    # 识别单元格中的文字
    def OCR(self):
        rects = self.CellDetect()
        thresh = self.gray

        # 特殊字符列表
        special_char_list = ‘ `[email protected]#$%^&*()-_=+[]{}|\\;:‘’,。《》/?ˇ‘
        for i in range(20):
            rect1 = rects[i]
            DetectImage1 = thresh[rect1[1]:rect1[3], rect1[0]:rect1[2]]

            # Tesseract所在的路径
            pytesseract.pytesseract.tesseract_cmd = ‘C://Program Files (x86)/Tesseract-OCR/tesseract.exe‘
            # 识别数字(每行第一列)
            text1 = pytesseract.image_to_string(DetectImage1, config="--psm 10")
            print(text1, end=‘-->‘)

            # 识别汉字(每行第二列)
            rect2 = rects[i+20]
            DetectImage2 = thresh[rect2[1]:rect2[3], rect2[0]:rect2[2]]
            text2 = pytesseract.image_to_string(DetectImage2, config=‘--psm 7‘, lang=‘chi_sim‘)
            text2 = ‘‘.join([char for char in text2 if char not in special_char_list])
            print(text2, end=‘-->‘)

            # 识别汉字(每行第三列)
            rect3 = rects[i+40]
            DetectImage3 = thresh[rect3[1]:rect3[3], rect3[0]:rect3[2]]
            text3 = pytesseract.image_to_string(DetectImage3, config=‘--psm 7‘, lang=‘chi_sim‘)
            text3 = ‘‘.join([char for char in text3 if char not in special_char_list])
            print(text3)

    # 显示图像
    def ShowImage(self):
        cv2.imshow(‘AI‘, self.image)
        cv2.waitKey(0)
        # cv2.imwrite(‘E://Horizontal.png‘, self.image)

ImagePath = ‘E://AI.png‘
imageOCR = ImageTableOCR(ImagePath)
imageOCR.OCR()

注意:本人现已开通微信公众号: Python爬虫与算法(微信号为:easy_web_scrape), 欢迎大家关注哦~~

原文地址:https://www.cnblogs.com/jclian91/p/10167308.html

时间: 2024-11-06 07:29:42

如何识别图片中的表格数据的相关文章

深入学习使用ocr算法识别图片中文字的方法

公司有个需求,简单点说需要从一张图片中识别出中文,通过python来实现,当然其他程序也行,只要能实现,而小编主要学习python,所以就提了python.一个小白在网上遨游了一天,终于找到一丝丝思绪,特意在此分享,希望大神提出宝贵的意见. 今天还是在学习OCR算法中,但是好像自己摸索确实比较难一点,而且python实现图片中文识别的方法还是不多,所以我打算记录一下自己学习的过程.今天看到一个菜鸟都可以用的开源项目,那就是OCR开源项目tesseract,可能对于还是菜鸟的我来说,最好不过了,可

numpy 构建深度神经网络来识别图片中是否有猫

目录 1 构建数据 2 随机初始化数据 3 前向传播 4 计算损失 5 反向传播 6 更新参数 7 构建模型 8 预测 9 开始训练 10 进行预测 11 以图片的形式展示预测后的结果 搭建简单神经网络来识别图片中是否有猫 代码借鉴地址:纯用NumPy实现神经网络 搭建一个简单易懂的神经网络来帮你理解深度神经网络 通过简单的猫识别的例子来帮你进一步进行理解 本代码用 numpy 来实现,不含有正则化,批量等算法 这里我们先来理清楚神经网络的步骤 (1) 构建数据.我们要构建出这样的一个数据,sh

erlang shell表格数据对齐

最近在erlang shell做一些测试,为了让测试结果数据显得更直观,想对齐需要打印的数据,做成像表格一样的效果. 开始的想法是在数据中插入tab.当然,erlang也有对tab的支持,但实际效果不理想. 对tab的支持如下: 1> io:format("No1~sNo2~n1~s2~n", ["\t", "\t"]). No1        No2 1             2 ok 但数据的长度超过8个字符就有问题,那有没有一种可以

freemarker导出word——让表格数据行数 列数自动变化

行数.列数变化只需定义一个List<List<T>> freemarker遍历的话,只需要使用freemarker的标记性语言<#list report.qc_third_agentTable as  table2_tr>遍历即可,如图 实现的效果 freemarker导出word--让表格数据行数 列数自动变化,布布扣,bubuko.com

Easy-ui Datagrid表格数据的编辑与提交

前言 jQuery EasyUI是一组基于jQuery的UI 插件集合体,而jQuery EasyUI可以打造出功能更加丰富且美观的UI界面.开发者不需要了解复杂的javascript和css样式,只需要了解html标签. 一.    easy-ui基本知识 1.            easy-ui引用js顺序详解 引用Jquery的js文件: <script src="jquery-easyui-1.3.4/jquery-1.8.0.min.js" type="te

jetty+bootstrap Carousel+springMVC+mybatis实现表格数据的轮播

index.jsp <%@ page language="java" pageEncoding="UTF-8"%> <%@include file="/taglibs.jsp"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd&quo

小结--通过水晶报表打印表格数据步骤

最近项目用到水晶报表来打印表格数据,现在温故一下: 1.安装CRforVS_13_0水晶报表. 2.在vs2010中创建.xsd 文件,往里面创建一个DataTable,并设置每个字段的数据类型. 3.创建.rpt(报表文件),在里面画要显示的表格,然后绑定数据库字段(右击数据库字段-数据库专家-我的连接-展开树节点找到新建的DataTable)将DataTable中字段拖入表格相应位置. 4.在上层页面的打印事件中New出新建的DataTable,接着将要打印的表格数据(例如:DataGrid

将HTML表格的每行每列转为数组,采集表格数据

将HTML表格的每行每列转为数组,采集表格数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 //将HTML表格的每行每列转为数组,采集表格数据 <? function get_td_array($table) { $table = preg_replace("'<table[^>]*?>'si","",$table); //OSPHP.COm.CN

Unity —— protobuf 导excel表格数据

前言: 之前使用NPOI插件编写的导表工具,其实就是直接将数据进行序列化,解析时还需要进行反序列化,步骤比较繁复,最近看到Google的一个开源的项目protobuf,不仅可以用于进行excel表格数据的导出,还能直接用于网络通信协议的定制. 一.protobuf简介: protobuf是由google公司发布的一个开源的项目,是一款方便而又通用的数据传输协议.所以我们在Unity中也可以借助protobuf来进行数据存储和网络协议两方面的开发,这里先说说数据存储部分的操作,也就是: 将.xls