Tesseract OCR:解析表格单元格

Posted

技术标签:

【中文标题】Tesseract OCR:解析表格单元格【英文标题】:Tesseract OCR: Parsing table cells 【发布时间】:2017-11-13 13:03:48 【问题描述】:

我正在使用 cmd 中的 Tesseract-OCR v4.0.0 (alpha?) 从如下所示的表格的 png 中提取文本:

我希望 Tesseract-OCR 在继续下一个之前解析一个单元格中的内容。我不想继续“行”中的下一个词。

预期:

. . . John Smith 07 March,2017 Chicago Milwaukee Detroit Pacific Ocean . . .

实际

. . . John Smith 07 March,2017 Chicago Pacific Ocean Milwaukee Detroit . . .

我试过了:

使用 --psm 标志从 0 到 13 更改页面分段。结果要么通常相同,但有细微差别,要么不可读。

有没有其他方法可以将 Tesseract 配置为在进入下一个单元格之前读取一个单元格的所有内容?另外,有什么解决办法吗?

【问题讨论】:

可能想看看我的帖子是否有答案,是的,--psm 6 通常可以解决问题? 【参考方案1】:

我有时会回答这个问题,但我看到很少有人问同样的问题。

我这里使用的解决方案是在使用tesseract之前使用Opencv对图像进行预处理。之后需要进行一些安排。 抱歉,我的代码退出时间很长,我认为有些代码可以缩短。但无论如何它都能完成工作。 我无法逐行解释代码,但我添加了 cmets 希望它可以大致了解正在发生的事情。

import cv2 
import numpy as np
import pytesseract 
pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract"

读取图像和过滤

table = cv2.imread("Table.png")
# adding some Border around image 
table= cv2.copyMakeBorder(table,20,20,20,20,cv2.BORDER_CONSTANT,value=[255,255,255])

去除图像中的噪点

table_c = cv2.GaussianBlur(cv2.cvtColor(table,cv2.COLOR_BGR2GRAY),(3,3),0,0)
# Threshold
_,thre = cv2.threshold(table_c,200,255,cv2.THRESH_BINARY,cv2.THRESH_OTSU)

仅在图像中获取行

kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(100,1))
morph = cv2.morphologyEx(thre,cv2.MORPH_CLOSE,kernel)
contours,h = cv2.findContours(morph, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
rows = [None]*len(contours)
for i, c in enumerate(contours):
    rows[i] = cv2.boundingRect(cv2.approxPolyDP(c, 3, True))
rows = sorted(rows, key=lambda b:b[1], reverse=False)

仅在图像中获取列

kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT,(1,50))
morph2 = cv2.morphologyEx(thre,cv2.MORPH_CLOSE,kernel2)
contours,h = cv2.findContours(morph2, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
table = cv2.drawContours(table, contours, 0, (0,255,0), 3)

cols = [None]*len(contours)
for i, c in enumerate(contours):
    cols[i] = cv2.boundingRect(cv2.approxPolyDP(c, 3, True))
cols = sorted(cols, key=lambda b:b[0], reverse=False)

删除行和列并仅保留文本

_,thre2 = cv2.threshold(thre,0,255,cv2.THRESH_BINARY_INV)
no_table = cv2.bitwise_and(morph,thre2)
no_table = cv2.bitwise_and(morph2,no_table)

kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT,(10,2))
mask = cv2.morphologyEx(no_table,cv2.MORPH_CLOSE,kernel2)

在一个框中获取每个文本

contours,h = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours_poly = [None]*len(contours)
boundRect = [None]*len(contours)
for i, c in enumerate(contours):
    contours_poly[i] = cv2.approxPolyDP(c, 3, True)
    boundRect[i] = cv2.boundingRect(contours_poly[i])
#     cv2.rectangle(table, (int(boundRect[i][0]), int(boundRect[i][1])),
#                  (int(boundRect[i][0]+boundRect[i][2]), int(boundRect[i][1]+boundRect[i][3])), (0,0,255), 2)
# table = cv2.drawContours(table, contours, -1, (0,255,0), 3)

裁剪每个框并识别文本

获取每个文本的行和列以及它在图像中的位置

text_position = []
offest = 10
boundingBoxes = sorted(boundRect, key=lambda b:b[0], reverse=False)


for rect in boundingBoxes:
    if rect[2] > 30 and rect[3]>10:
        image = table[rect[1]-offest:rect[1]+rect[3]+offest,rect[0]-offest:rect[0]+rect[2]+offest]
        text = pytesseract.image_to_string(image)
        for i,row in enumerate(rows):
            if i < len(rows):
                if rect[1] >row[1] and rect[1] <rows[i+1][1]:
                    r = i 
                    break 
        for i,col in enumerate(cols):
            if i < len(cols):
                if rect[0] >col[0] and rect[0] <cols[i+1][0]:
                    c = i 
                    break
                    
        text_position.append('Text':text.split("\n")[0],"row":r,'col':c,"X":rect[0],"Y":rect[1])
        

在同一行和列中组合文本

indexs = []
for j,t in enumerate(text_position):
    list_re = []
    for i,tt in enumerate(text_position):
        if tt["row"] == t["row"] and tt["col"] == t["col"] :
            list_re.append(i)
    if len(list_re)>1:
        indexs.append(list_re)
        
indexs = list(set(tuple(i) for i in indexs))
text = ""
for indexs_ in indexs:
    text_repeated = [text_position[i] for i in indexs_]
    text_repeated = sorted(text_repeated, key=lambda b:b["Y"], reverse=False)
    for i in range(len(text_repeated)):
        text += text_repeated[i]["Text"]+" "
    new_dic = 'Text': text, 'row':text_repeated[0]["row"] , 'col': text_repeated[0]["col"], 'X': text_repeated[0]["X"], 'Y': text_repeated[-1]["Y"]
    for i in indexs_:
        text_position.pop(i)
    text_position.append(new_dic)

最终输出将是一个字典列表,每个字典都包含表格中每个单元格的文本、行和列,如下所示

['Text': 'Jane Doe', 'row': 3, 'col': 1, 'X': 67, 'Y': 167,
 'Text': 'John Smith', 'row': 2, 'col': 1, 'X': 67, 'Y': 86,
 'Text': 'Name', 'row': 1, 'col': 1, 'X': 68, 'Y': 59,
 'Text': '07 March, 2017', 'row': 3, 'col': 2, 'X': 301, 'Y': 167,
 'Text': '07 March, 2017', 'row': 2, 'col': 2, 'X': 301, 'Y': 86,
 'Text': ' ', 'row': 1, 'col': 2, 'X': 302, 'Y': 59,
 'Text': 'Los Angeles', 'row': 3, 'col': 3, 'X': 536, 'Y': 167,
 'Text': 'Detroit', 'row': 2, 'col': 3, 'X': 536, 'Y': 140,
 'Text': 'Locations', 'row': 1, 'col': 3, 'X': 536, 'Y': 58,
 'Text': 'Currently in', 'row': 1, 'col': 4, 'X': 769, 'Y': 58,
 'Text': 'Pacific Ocean', 'row': 2, 'col': 4, 'X': 770, 'Y': 85,
 'Text': 'Chicago Milwaukee Detroit ',
  'row': 2,
  'col': 3,
  'X': 535,
  'Y': 140

【讨论】:

以上是关于Tesseract OCR:解析表格单元格的主要内容,如果未能解决你的问题,请参考以下文章

解析对象数组并填充表格视图单元格 Swift

Pandas:使用合并的单元格和空白值解析 Excel 电子表格

iOS如何编辑(更新)日期我表格视图单元格解析Swift

如果解析失败,取消表格单元格编辑的规范方法

正则表达式解析谷歌表格中单元格中的单词

从Google电子表格单元格中解析Json