用OpenCV和OCR识别图片中的表格数据
作者:?jclian,喜歡算法,熱愛分享,希望能結交更多志同道合的朋友,一起在學習Python的道路上走得更遠!
??在很多時候,我們的數(shù)據(jù)來源形式是多種多樣的,有時候數(shù)據(jù)(或表格)也會呈現(xiàn)在圖片中。那么,我們?nèi)绾蝸慝@取圖片中的有用數(shù)據(jù)呢?當一張圖片中含有表格數(shù)據(jù)的時候,我們可以用OpenCV識別表格中的直線,然后再用OCR技術識別其中的文字。
??本文僅作為如何識別圖片中的表格的一個例子,希望能給讀者一些啟示。筆者用到的工具如下:
opencv
pyteressact
numpy
我們用opencv來識別表格中的直線,用pyteressact來識別單元格文字,用numpy做數(shù)值處理。我們要識別的示例圖片(AI.png)如下:
示例圖片 AI.png我們分以下幾步進行識別:
識別表格中的橫線,即分割記錄(每一行)的橫線;
識別表格中的豎線,即每個列的分割線;
找到數(shù)據(jù)所在的單元格;
利用pyteressact識別單元格的文字。
識別表格中的橫線
??識別橫線之前,我們先創(chuàng)建一個圖片表格識別類(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為灰度模式的圖片。
??接下來,我們識別圖片中的分割兩條記錄的橫線。注意到,相鄰兩條記錄之間的顏色是不一致的,因此,我們利用圖片灰度化后,每一行像素的平均值的差的絕對值來作為相鄰兩條記錄的分割線,這樣就能檢測出分割兩條記錄的橫線了。具體的識別橫線的函數(shù)的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
首先對圖片進行二值化處理,再進行兩次中值濾波,這樣是為了使相鄰兩條記錄之間的像素區(qū)別盡可能大。然后對該圖片中的每一行的像素進行檢測, 以相鄰兩行的平均像素差大于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]]
識別豎直直線后的圖片如下:(圖片中的紅色線段)
識別豎線后的圖片可以看到,圖片六條豎直的線段都已經(jīng)完整標記出來了。
識別圖片中的單元格
??在識別圖片中的單元格之前,我們先來識別每個單元格所在的頂點,也就是上述識別后的橫線與豎線的交點。完整的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
頂點檢測后的圖片如下:(圖片中的藍色點即為每個單元格的頂點)
頂點檢測后的圖片由此可見,我們識別出來的單元格的頂點是正確的。接著,我們把這些單元格取出來,代碼如下:(接以上代碼)
#?尋找單元格區(qū)域????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?=?'?`~!@#$%^&*()-_=+[]{}|\;:‘’,。《》/?ˇ'
????????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'
????????????#?識別數(shù)字(每行第一列)
????????????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-->肌訊-->互聯(lián)網(wǎng)綜合服務
3-->標為-->人一智能自動化業(yè)務、智能屹片
4-->阿里巴巴-->互聯(lián)網(wǎng)綜合服務
5-->平安集口-->人T智能金融研發(fā)平仄
6-->華大基因-->精準檢測、醫(yī)療數(shù)據(jù)運營服務
d-->搜狗-->綜合人T智能解決方案平臺
8-->一科大訊飛-->智能語音技術
9-->一中利創(chuàng)湯-->智能終端平臺技術
10-->珍山集團-->SaaS級智能營銷云平臺
i-->商湯科技-->人工智能視覺深度學習平臺
12-->神州泰岳-->綜合類軟件產(chǎn)品及服務
13-->寒武紅科技-->深度學對專用的智能盂片
14-->漢王科技-->文字識別技術與智能交工
15-->全志刑技-->智能芯片設計
16-->face曠視科技-->人T智能產(chǎn)品和行業(yè)解夷方案
17-->創(chuàng)略科技-->智能客戶數(shù)據(jù)平臺
18-->海云數(shù)據(jù)-->企業(yè)級大數(shù)據(jù)整體運營與分析服務
19-->影渭科技-->視覺技術、智能影像生產(chǎn)企業(yè)
20-->智蹈智能-->智能機器人技術提供和平臺運蕭
下面,我們來統(tǒng)計一下識別的準確率。在原來的表格中,一共是20個數(shù)字加上280個漢字(包括標點)加上10個英語字母(包括標點),對于識別的結果,其中數(shù)字類識別正確17個,漢字正確256個,英語字母8個,因此,總的識別的準確率為90.6% ,識別的結果還是可以的。
總結
??本文僅作為如何識別圖片中的表格的一個例子,希望能給讀者一些啟示。對于不同的圖片表格,需要具體問題具體分析。
??雖然筆者盡可能把整個過程寫得簡單明了,但其中的探索過程卻是很復雜的。而且值得注意的是,在本文中的檢測橫線的方法僅適用于相鄰兩條記錄有顏色差別的表格圖片。
??完整的Python代碼如下,希望能給大家一些思考。
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
????#?尋找單元格區(qū)域
????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?=?'?`~!@#$%^&*()-_=+[]{}|\;:‘’,。《》/?ˇ'
????????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'
????????????#?識別數(shù)字(每行第一列)
????????????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程序加密
用Python開發(fā)計時器程序
硬核 | 用Python給女朋友送一顆彩蛋
用Pandas庫實現(xiàn)MySQL數(shù)據(jù)庫的讀寫
推薦Python中文社區(qū)旗下的幾個服務類公眾號
▼ 長按掃碼上方二維碼或點擊下方閱讀原文
免費成為社區(qū)注冊會員,會員可以享受更多權益
總結
以上是生活随笔為你收集整理的用OpenCV和OCR识别图片中的表格数据的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DM3730 x-loader 分析 一
- 下一篇: DM3730学习日记-开发环境