opencv+paddle orc 识别图片提取表格信息
生活随笔
收集整理的這篇文章主要介紹了
opencv+paddle orc 识别图片提取表格信息
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
參考:
python-opencv表格識別
思路:
1.提取出橫線
2.提取出縱線
3.得到交叉點,刪除錯誤的交叉點,兩個交叉點距離小于10,取坐標值小的那個交叉點,得到表格行列
4.對每個單元格使用paddle ocr提取文字
在原文代碼基礎上修改了2點
1.pytesseract識別準確率不高,使用paddle ocr代替 pytesseract
2.識別出的表格交叉點有些并非真實交叉點,通過判斷該行像素點個數,丟掉錯誤橫縱坐標
待優化:
- 真實圖片的單元格并不在一條橫坐標或縱坐標下,使用框線交叉點坐標確定單元格,實際圖像不夠完整,待后續優化,獲取每個單元格坐標
添加優化代碼:
1.找到表格四個角點
2.做平移變換
因為已經平移變換,就刪除了一些代碼,完整優化代碼如下:
import cv2 import pandas as pd import numpy as npfrom paddleocr import PaddleOCRocr = PaddleOCR(use_angle_cls=True, lang="ch")class TableOCR(object):def __init__(self,result_file=''):self.img_file = ''if not result_file:self.result_file = 'result.xlsx'def get_sorted_rect(self, rect):'''獲取排序的四個坐標@param rect:@return:按照左上 右上 右下 左下排列返回'''mid_x = (max([x[1] for x in rect]) - min([x[1] for x in rect])) * 0.5 + min([x[1] for x in rect]) # 中間點坐標left_rect = [x for x in rect if x[1] < mid_x]left_rect.sort(key=lambda x: (x[0], x[1]))right_rect = [x for x in rect if x[1] > mid_x]right_rect.sort(key=lambda x: (x[0], x[1]))sorted_rect = left_rect[0], left_rect[1], right_rect[1], right_rect[0]return sorted_rectdef get_table(self, gray, min_table_area=0):'''從灰度圖獲取表格坐標,[[右下→左下→左上→右上],..]邊緣檢測+膨脹---》找最外圍輪廓點,根據面積篩選---》根據縱坐標排序---》計算輪廓的四個點,再次篩選@param gray:灰度圖 如果是二值圖會報錯@return:'''canny = cv2.Canny(gray, 200, 255) # 第一個閾值和第二個閾值kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))canny = cv2.dilate(canny, kernel)_, contours, HIERARCHY = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)if not min_table_area:min_table_area = gray.shape[0] * gray.shape[1] * 0.01 # 50000 # 最小的矩形面積閾值candidate_table = [cnt for cnt in contours if cv2.contourArea(cnt) > min_table_area] # 計算該輪廓的面積candidate_table = sorted(candidate_table, key=cv2.contourArea, reverse=True)area_list = [cv2.contourArea(cnt) for cnt in candidate_table]table = []for i in range(len(candidate_table)):# 遍歷所有輪廓# cnt是一個點集cnt = candidate_table[i]# 找到最小的矩形,該矩形可能有方向rect = cv2.minAreaRect(cnt)# box是四個點的坐標box = cv2.boxPoints(rect)box = np.int0(box)sorted_box = self.get_sorted_rect(box)result = [sorted_box[2], sorted_box[3], sorted_box[0], sorted_box[1]] # 右下 左下 左上 右上result = [x.tolist() for x in result]table.append(result)return tabledef perTran(self, image, rect):'''做透視變換image 圖像rect 四個頂點位置:左上 右上 右下 左下'''tl, tr, br, bl = rect # 左下 右下 左上 右上 || topleft topright 左上 右上 右下 左下# 計算寬度widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))maxWidth = max(int(widthA), int(widthB))# 計算高度heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))maxHeight = max(int(heightA), int(heightB))# 定義變換后新圖像的尺寸dst = np.array([[0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1],[0, maxHeight - 1]], dtype='float32')# 變換矩陣rect = np.array(rect, dtype=np.float32)dst = np.array(dst, dtype=np.float32)M = cv2.getPerspectiveTransform(rect, dst)# 透視變換warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))return warpeddef recognize_bgkx(self,binary):rows, cols = binary.shapescale = 30 # 值越小 橫線越少 40# 自適應獲取核值# 識別橫線:kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (cols // scale, 1))# 矩形eroded = cv2.erode(binary, kernel, iterations=1) # 腐蝕dilated_col = cv2.dilate(eroded, kernel, iterations=1) # 膨脹# cv2.imshow("excel_horizontal_line", dilated_col)# cv2.waitKey()# 識別豎線:scale = 20kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, rows // scale))eroded = cv2.erode(binary, kernel, iterations=1)dilated_row = cv2.dilate(eroded, kernel, iterations=1)# cv2.imshow("excel_vertical_line:", dilated_row)# cv2.waitKey()# 將識別出來的橫豎線合起來 對二進制數據進行“與”操作bitwise_and = cv2.bitwise_and(dilated_col, dilated_row)# cv2.imshow("excel_bitwise_and", bitwise_and)# cv2.waitKey()# 標識表格輪廓# merge = cv2.add(dilated_col, dilated_row) # 進行圖片的加和# cv2.imshow("entire_excel_contour:", merge)# cv2.waitKey()# 兩張圖片進行減法運算,去掉表格框線# merge2 = cv2.subtract(binary, merge)# cv2.imshow("binary_sub_excel_rect", merge2)# cv2.waitKey()# new_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))# erode_image = cv2.morphologyEx(merge2, cv2.MORPH_OPEN, new_kernel)# cv2.imshow('erode_image2', erode_image)# cv2.waitKey()# merge3 = cv2.add(erode_image, bitwise_and)# cv2.imshow('merge3', merge3)# cv2.waitKey()# 將焦點標識取出來ys, xs = np.where(bitwise_and > 0)# 橫縱坐標數組y_point_arr = []x_point_arr = []# 通過排序,排除掉相近的像素點,只取相近值的最后一點# 這個10就是兩個像素點的距離,不是固定的,根據不同的圖片會有調整,基本上為單元格表格的高度(y坐標跳變)和長度(x坐標跳變)i = 0sort_x_point = np.sort(list(set(xs)))for i in range(len(sort_x_point) - 1):if sort_x_point[i + 1] - sort_x_point[i] > 10:x_point_arr.append(sort_x_point[i])i = i + 1# 要將最后一個點加入x_point_arr.append(sort_x_point[i])i = 0sort_y_point = np.sort(list(set(ys)))# print(np.sort(ys))for i in range(len(sort_y_point) - 1):if (sort_y_point[i + 1] - sort_y_point[i] > 10):y_point_arr.append(sort_y_point[i])i = i + 1y_point_arr.append(sort_y_point[i])return x_point_arr, y_point_arrdef recognize_text_by_loop(self,gray,x_point_arr,y_point_arr):# y_point_arr = [x - 3 for x in y_point_arr]# 循環y坐標,x坐標分割表格data = [[] for i in range(len(y_point_arr))]for i in range(len(y_point_arr) - 1):# if i==0:# continuefor j in range(len(x_point_arr) - 1):# 在分割時,第一個參數為y坐標,第二個參數為x坐標cell = gray[y_point_arr[i]:y_point_arr[i + 1],x_point_arr[j]:x_point_arr[j + 1]]# cv2.imshow("sub_pic" + str(i) + str(j), cell)# cv2.waitKey()# cv2.destroyAllWindows()# img_path = "cell_image_" + str(i) + '_' + str(j) + ".png"# cv2.imwrite(img_path, cell)# 輸入待識別圖片路徑# 輸出結果保存路徑result = ocr.ocr(cell)text1 = ''.join([x[1][0] for x in result])print(text1)data[i].append(text1)print(data)df = pd.DataFrame(data[1:-1], columns=data[0])return dfdef ocr(self,img_file):self.img_file = img_fileimg = cv2.imread(img_file)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)rect = self.get_table(gray)sorted_rect = self.get_sorted_rect(rect[0])gray_z = self.perTran(gray, sorted_rect) #如果有多個table 可以循環cv2.imwrite('gray_z.jpg', gray_z)binary_z = cv2.adaptiveThreshold(~gray_z, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY, 15, -5)x_point_arr, y_point_arr = self.recognize_bgkx(binary_z)df = self.recognize_text_by_loop(gray_z,x_point_arr, y_point_arr)df.to_excel(self.result_file, index=False)tableOCR = TableOCR() tableOCR.ocr('table.png')效果:
結果:
總結
以上是生活随笔為你收集整理的opencv+paddle orc 识别图片提取表格信息的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 合生活门禁复制数据结构分析
- 下一篇: 百度手写识别