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:解析表格单元格的主要内容,如果未能解决你的问题,请参考以下文章