在很多時(shí)候,我們的數(shù)據(jù)來源形式是多種多樣的,有時(shí)候數(shù)據(jù)(或表格)也會(huì)呈現(xiàn)在圖片中。那么,我們?nèi)绾蝸慝@取圖片中的有用數(shù)據(jù)呢?當(dāng)一張圖片中含有表格數(shù)據(jù)的時(shí)候,我們可以用OpenCV識(shí)別表格中的直線,然后再用OCR技術(shù)識(shí)別其中的文字。
本文僅作為如何識(shí)別圖片中的表格的一個(gè)例子,希望能給讀者一些啟示。筆者用到的工具如下:
我們用opencv來識(shí)別表格中的直線,用pyteressact來識(shí)別單元格文字,用numpy做數(shù)值處理。我們要識(shí)別的示例圖片(AI.png)如下:
示例圖片 AI.png
我們分以下幾步進(jìn)行識(shí)別:
識(shí)別表格中的橫線
識(shí)別橫線之前,我們先創(chuàng)建一個(gè)圖片表格識(shí)別類(ImageTableOCR),如下:
# -*- coding: utf-8 -*-import cv2import pytesseractimport numpy as npclass ImageTableOCR(object): # 初始化 def __init__(self, ImagePath): # 讀取圖片 self.image = cv2.imread(ImagePath, 1) # 把圖片轉(zhuǎn)換為灰度模式 self.gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
其中self.image為RGB模塊的圖片,self.gray為灰度模式的圖片。
接下來,我們識(shí)別圖片中的分割兩條記錄的橫線。注意到,相鄰兩條記錄之間的顏色是不一致的,因此,我們利用圖片灰度化后,每一行像素的平均值的差的絕對(duì)值來作為相鄰兩條記錄的分割線,這樣就能檢測(cè)出分割兩條記錄的橫線了。具體的識(shí)別橫線的函數(shù)的Python代碼如下:(接以上代碼)
# 橫向直線檢測(cè) def HorizontalLineDetect(self): # 圖像二值化 ret, thresh1 = cv2.threshold(self.gray, 240, 255, cv2.THRESH_BINARY) # 進(jìn)行兩次中值濾波 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為標(biāo)準(zhǔn) 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
首先對(duì)圖片進(jìn)行二值化處理,再進(jìn)行兩次中值濾波,這樣是為了使相鄰兩條記錄之間的像素區(qū)別盡可能大。然后對(duì)該圖片中的每一行的像素進(jìn)行檢測(cè), 以相鄰兩行的平均像素差大于120為標(biāo)準(zhǔn), 識(shí)別出分割兩條記錄的橫線。識(shí)別后的橫線如下:(圖片中的綠色線段)
識(shí)別橫線后的圖片
識(shí)別表格中的豎線
在這一步中,我們利用opencv中的Hough直線檢測(cè)方法來檢測(cè)圖片中的豎線。完整的Python代碼如下:(接以上代碼)
# 縱向直線檢測(cè) def VerticalLineDetect(self): # Canny邊緣檢測(cè) edges = cv2.Canny(self.gray, 30, 240) # Hough直線檢測(cè) 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
首先我們對(duì)灰度圖片進(jìn)行Canny邊緣檢測(cè),在此基礎(chǔ)上再利用Hough直線檢測(cè)方法識(shí)別圖片中的直線,要求識(shí)別的最大間距為30,線段長(zhǎng)度最小為500,并且為豎直直線(x1 == x2),當(dāng)然,也有一些人為的因素,那就是筆者自己添加了兩條豎直直線([[13, 937, 13, 102]],[[756, 937, 756, 102]])。運(yùn)行上述方法,輸出的結(jié)果如下:
[[13, 937, 13, 102]]
[[75, 937, 75, 102]]
[[77, 937, 77, 102]]
[[270, 937, 270, 104]]
[[272, 937, 272, 102]]
[[756, 937, 756, 102]]
識(shí)別豎直直線后的圖片如下:(圖片中的紅色線段)
識(shí)別豎線后的圖片
可以看到,圖片六條豎直的線段都已經(jīng)完整標(biāo)記出來了。
識(shí)別圖片中的單元格
在識(shí)別圖片中的單元格之前,我們先來識(shí)別每個(gè)單元格所在的頂點(diǎn),也就是上述識(shí)別后的橫線與豎線的交點(diǎn)。完整的Python代碼如下:(接以上代碼)
# 頂點(diǎn)檢測(cè) def VertexDetect(self): vertical_lines = self.VerticalLineDetect() horizontal_lines = self.HorizontalLineDetect() # 頂點(diǎn)列表 vertex = [] for v_line in vertical_lines: for h_line in horizontal_lines: vertex.append((v_line[0], h_line[1])) #print(vertex) # 繪制頂點(diǎn) for point in vertex: cv2.circle(self.image, point, 1, (255, 0, 0), 2) return vertex
頂點(diǎn)檢測(cè)后的圖片如下:(圖片中的藍(lán)色點(diǎn)即為每個(gè)單元格的頂點(diǎn))
頂點(diǎn)檢測(cè)后的圖片
由此可見,我們識(shí)別出來的單元格的頂點(diǎn)是正確的。接著,我們把這些單元格取出來,代碼如下:(接以上代碼)
# 尋找單元格區(qū)域 def CellDetect(self): vertical_lines = self.VerticalLineDetect() horizontal_lines = self.HorizontalLineDetect() # 頂點(diǎn)列表 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
以第一個(gè)單元格為例,其圖像如下:
第一個(gè)單元格的圖片
識(shí)別單元格的文字
在識(shí)別出圖片中表格的單元格后,我們可以對(duì)該單元格圖片進(jìn)行文字識(shí)別,我們使用的OCR工具為Teressact, 其Python的接口為pyteressact 。具體的Python代碼如下:(接以上代碼)
# 識(shí)別單元格中的文字 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í)別數(shù)字(每行第一列) text1 = pytesseract.image_to_string(DetectImage1, config="--psm 10") print(text1, end='-->') # 識(shí)別漢字(每行第二列) 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='-->') # 識(shí)別漢字(每行第三列) 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)
識(shí)別后的結(jié)果如下:
I-->度一-->開放的人一智能服務(wù)平臺(tái)
2-->肌訊-->互聯(lián)網(wǎng)綜合服務(wù)
3-->標(biāo)為-->人一智能自動(dòng)化業(yè)務(wù)、智能屹片
4-->阿里巴巴-->互聯(lián)網(wǎng)綜合服務(wù)
5-->平安集口-->人T智能金融研發(fā)平仄
6-->華大基因-->精準(zhǔn)檢測(cè)、醫(yī)療數(shù)據(jù)運(yùn)營(yíng)服務(wù)
d-->搜狗-->綜合人T智能解決方案平臺(tái)
8-->一科大訊飛-->智能語音技術(shù)
9-->一中利創(chuàng)湯-->智能終端平臺(tái)技術(shù)
10-->珍山集團(tuán)-->SaaS級(jí)智能營(yíng)銷云平臺(tái)
i-->商湯科技-->人工智能視覺深度學(xué)習(xí)平臺(tái)
12-->神州泰岳-->綜合類軟件產(chǎn)品及服務(wù)
13-->寒武紅科技-->深度學(xué)對(duì)專用的智能盂片
14-->漢王科技-->文字識(shí)別技術(shù)與智能交工
15-->全志刑技-->智能芯片設(shè)計(jì)
16-->face曠視科技-->人T智能產(chǎn)品和行業(yè)解夷方案
17-->創(chuàng)略科技-->智能客戶數(shù)據(jù)平臺(tái)
18-->海云數(shù)據(jù)-->企業(yè)級(jí)大數(shù)據(jù)整體運(yùn)營(yíng)與分析服務(wù)
19-->影渭科技-->視覺技術(shù)、智能影像生產(chǎn)企業(yè)
20-->智蹈智能-->智能機(jī)器人技術(shù)提供和平臺(tái)運(yùn)蕭
下面,我們來統(tǒng)計(jì)一下識(shí)別的準(zhǔn)確率。在原來的表格中,一共是20個(gè)數(shù)字加上280個(gè)漢字(包括標(biāo)點(diǎn))加上10個(gè)英語字母(包括標(biāo)點(diǎn)),對(duì)于識(shí)別的結(jié)果,其中數(shù)字類識(shí)別正確17個(gè),漢字正確256個(gè),英語字母8個(gè),因此,總的識(shí)別的準(zhǔn)確率為90.6% ,識(shí)別的結(jié)果還是可以的。
總結(jié)
本文僅作為如何識(shí)別圖片中的表格的一個(gè)例子,希望能給讀者一些啟示。對(duì)于不同的圖片表格,需要具體問題具體分析。
雖然筆者盡可能把整個(gè)過程寫得簡(jiǎn)單明了,但其中的探索過程卻是很復(fù)雜的。而且值得注意的是,在本文中的檢測(cè)橫線的方法僅適用于相鄰兩條記錄有顏色差別的表格圖片。
完整的Python代碼如下,希望能給大家一些思考。
# -*- coding: utf-8 -*-import cv2import pytesseractimport numpy as npclass ImageTableOCR(object): # 初始化 def __init__(self, ImagePath): # 讀取圖片 self.image = cv2.imread(ImagePath, 1) # 把圖片轉(zhuǎn)換為灰度模式 self.gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY) # 橫向直線檢測(cè) def HorizontalLineDetect(self): # 圖像二值化 ret, thresh1 = cv2.threshold(self.gray, 240, 255, cv2.THRESH_BINARY) # 進(jìn)行兩次中值濾波 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為標(biāo)準(zhǔn) 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 # 縱向直線檢測(cè) def VerticalLineDetect(self): # Canny邊緣檢測(cè) edges = cv2.Canny(self.gray, 30, 240) # Hough直線檢測(cè) 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 # 頂點(diǎn)檢測(cè) def VertexDetect(self): vertical_lines = self.VerticalLineDetect() horizontal_lines = self.HorizontalLineDetect() # 頂點(diǎn)列表 vertex = [] for v_line in vertical_lines: for h_line in horizontal_lines: vertex.append((v_line[0], h_line[1])) #print(vertex) # 繪制頂點(diǎn) 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() # 頂點(diǎn)列表 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 # 識(shí)別單元格中的文字 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í)別數(shù)字(每行第一列) text1 = pytesseract.image_to_string(DetectImage1, config="--psm 10") print(text1, end='-->') # 識(shí)別漢字(每行第二列) 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='-->') # 識(shí)別漢字(每行第三列) 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()
聯(lián)系客服