如何 圖片中的表格數據 圖片截取

我們分以下幾步進行識別:

  1. 識別表格中的橫線,即分割記錄(每一行)的橫線;
  2. 識別表格中的豎線,即每個列的分割線;
  3. 找到數據所在的單元格;
  4. 利用pyteressact識別單元格的文字。

識別表格中的橫線

  識別橫線之前,我們先創建一個圖片表格識別類(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爲灰度模式的圖片。
  接下來,我們識別圖片中的分割兩條記錄的橫線。注意到,相鄰兩條記錄之間的顏色是不一致的,因此,我們利用圖片灰度化後,每一行像素的平均值的差的絕對值來作爲相鄰兩條記錄的分割線,這樣就能檢測出分割兩條記錄的橫線了。具體的識別橫線的函數的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

首先對圖片進行二值化處理,再進行兩次中值濾波,這樣是爲了使相鄰兩條記錄之間的像素區別儘可能大。然後對該圖片中的每一行的像素進行檢測, 以相鄰兩行的平均像素差大於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]]

識別豎直直線後的圖片如下:(圖片中的紅色線段)

 

可以看到,圖片六條豎直的線段都已經完整標記出來了。

識別圖片中的單元格

  在識別圖片中的單元格之前,我們先來識別每個單元格所在的頂點,也就是上述識別後的橫線與豎線的交點。完整的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

頂點檢測後的圖片如下:(圖片中的藍色點即爲每個單元格的頂點)

 

由此可見,我們識別出來的單元格的頂點是正確的。接着,我們把這些單元格取出來,代碼如下:(接以上代碼)

 


import cv2
import numpy as np
 

def HorizontalLineDetect(gray,image):

    # 圖像二值化
    ret, thresh1 = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY)
    # 進行兩次中值濾波
    blur = cv2.medianBlur(thresh1, 3)  # 模板大小3*3
    blur = cv2.medianBlur(blur, 3)  # 模板大小3*3

    h, w = gray.shape

    # 橫向直線列表
    horizontal_lines = []
    for i in range(h - 1):
        # 找到兩條記錄的分隔線段,以相鄰兩行的平均像素差大於120爲標準
        if abs(np.mean(blur[i, :]) - np.mean(blur[i + 1, :])) > 50:
            # 在圖像上繪製線段
            horizontal_lines.append([0, i, w, i])
            cv2.line(image, (0, i), (w, i), (0, 255, 0), 2)

    horizontal_lines = horizontal_lines[1:]
    # print(horizontal_lines)
    return horizontal_lines


def VerticalLineDetect(gray,image):
        # Canny邊緣檢測
        edges = cv2.Canny(gray, 30, 240)

        # Hough直線檢測
        #minLineLength = 500
        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( image, (x1, y1), (x2, y2), (0, 0, 255), 2)

        return vertical_lines


def VertexDetect(gray,image):
    vertical_lines =  VerticalLineDetect(gray,image)
    horizontal_lines =  HorizontalLineDetect(gray,image)

    # 頂點列表
    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(image, point, 1, (255, 0, 0), 2)

    return vertex
    
def CellDetect(gray,image ):
    vertical_lines = VerticalLineDetect(gray,image)
    horizontal_lines = HorizontalLineDetect(gray,image)

    # 頂點列表
    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    
    
path1="C:\\Users\\admin\\Desktop\\ocr\\temp\\"
filename=path1+"dianli.jpg"
    
image = cv2.imread(filename, 1)
        # 把圖片轉換爲灰度模式
gray = cv2.cvtColor( image, cv2.COLOR_BGR2GRAY)


vertex=VertexDetect(gray,image)


rects=CellDetect(gray,image)


cv2.imwrite(path1+"out\\image.jpg", image)
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章