Как найти таблицу, как структуру в изображении - PullRequest
0 голосов
/ 13 июня 2018

У меня разные типы файлов счетов, я хочу найти таблицу в каждом файле счетов.В этой таблице положение не является постоянным.Итак, я иду на обработку изображений.Сначала я попытался преобразовать свой счет в изображение, затем я нашел контур, основанный на границах таблицы. Наконец, я могу определить положение таблицы.Для задачи, которую я использовал ниже код.

with Image(page) as page_image:
    page_image.alpha_channel = False #eliminates transperancy
    img_buffer=np.asarray(bytearray(page_image.make_blob()), dtype=np.uint8)
    img = cv2.imdecode(img_buffer, cv2.IMREAD_UNCHANGED)

    ret, thresh = cv2.threshold(img, 127, 255, 0)
    im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    margin=[]
    for contour in contours:
        # get rectangle bounding contour
        [x, y, w, h] = cv2.boundingRect(contour)
        # Don't plot small false positives that aren't text
        if (w >thresh1 and h> thresh2):
                margin.append([x, y, x + w, y + h])
    #data cleanup on margin to extract required position values.

В этом коде thresh1, thresh2 я буду обновлять на основе файла.

Таким образом, используя этот код, я могу успешно читатьпозиции таблиц в изображениях, используя эту позицию, я буду работать над моим PDF-файлом счета.Например,

Образец 1:

enter image description here

Образец 2:

enter image description here

Образец 3: enter image description here

Выход:

Образец 1:

enter image description here

Образец 2:

enter image description here

Образец 3:

enter image description here

Но теперь у меня есть новый формат, у которого нет границ, но это таблица.Как это решить?Потому что вся моя работа зависит только от границ таблиц.Но сейчас у меня нет границ таблицы.Как мне этого добиться?Я понятия не имею, чтобы выйти из этой проблемы.У меня вопрос, есть ли способ найти позицию, основанную на структуре таблицы?

Например, мой ввод проблемы выглядит следующим образом:

enter image description here

Я хотел бы найти его позицию, как показано ниже: enter image description here

Как я могу решить эту проблему?Это действительно заметно, чтобы дать мне идею решить проблему.

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 09 августа 2018

Вайбхав прав.Вы можете поэкспериментировать с различными морфологическими преобразованиями, чтобы извлечь или сгруппировать пиксели в разные формы, линии и т. Д. Например, подход может быть следующим:

  1. Начните с расширения, чтобы преобразовать текст всплошные пятна.
  2. Затем примените функцию findContours в качестве следующего шага, чтобы найти ограничивающие текстовые поля.
  3. После получения ограничивающих текстовых полей можно применить некоторый эвристический алгоритм для кластеризации текстовых полей вгрупп по их координатам.Таким образом, вы можете найти группы текстовых областей, выровненных по строкам и столбцам.
  4. Затем вы можете применить сортировку по координатам x и y и / или некоторый анализ к группам, чтобы попытаться найти, могут ли сгруппированные текстовые полясформировать таблицу.

Я написал небольшой пример, иллюстрирующий эту идею.Я надеюсь, что код не требует пояснений.Я также разместил там несколько комментариев.

import os
import cv2
import imutils

# This only works if there's only one table on a page
# Important parameters:
#  - morph_size
#  - min_text_height_limit
#  - max_text_height_limit
#  - cell_threshold
#  - min_columns


def pre_process_image(img, save_in_file, morph_size=(8, 8)):

    # get rid of the color
    pre = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Otsu threshold
    pre = cv2.threshold(pre, 250, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    # dilate the text to make it solid spot
    cpy = pre.copy()
    struct = cv2.getStructuringElement(cv2.MORPH_RECT, morph_size)
    cpy = cv2.dilate(~cpy, struct, anchor=(-1, -1), iterations=1)
    pre = ~cpy

    if save_in_file is not None:
        cv2.imwrite(save_in_file, pre)
    return pre


def find_text_boxes(pre, min_text_height_limit=6, max_text_height_limit=40):
    # Looking for the text spots contours
    # OpenCV 3
    # img, contours, hierarchy = cv2.findContours(pre, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    # OpenCV 4
    contours, hierarchy = cv2.findContours(pre, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    # Getting the texts bounding boxes based on the text size assumptions
    boxes = []
    for contour in contours:
        box = cv2.boundingRect(contour)
        h = box[3]

        if min_text_height_limit < h < max_text_height_limit:
            boxes.append(box)

    return boxes


def find_table_in_boxes(boxes, cell_threshold=10, min_columns=2):
    rows = {}
    cols = {}

    # Clustering the bounding boxes by their positions
    for box in boxes:
        (x, y, w, h) = box
        col_key = x // cell_threshold
        row_key = y // cell_threshold
        cols[row_key] = [box] if col_key not in cols else cols[col_key] + [box]
        rows[row_key] = [box] if row_key not in rows else rows[row_key] + [box]

    # Filtering out the clusters having less than 2 cols
    table_cells = list(filter(lambda r: len(r) >= min_columns, rows.values()))
    # Sorting the row cells by x coord
    table_cells = [list(sorted(tb)) for tb in table_cells]
    # Sorting rows by the y coord
    table_cells = list(sorted(table_cells, key=lambda r: r[0][1]))

    return table_cells


def build_lines(table_cells):
    if table_cells is None or len(table_cells) <= 0:
        return [], []

    max_last_col_width_row = max(table_cells, key=lambda b: b[-1][2])
    max_x = max_last_col_width_row[-1][0] + max_last_col_width_row[-1][2]

    max_last_row_height_box = max(table_cells[-1], key=lambda b: b[3])
    max_y = max_last_row_height_box[1] + max_last_row_height_box[3]

    hor_lines = []
    ver_lines = []

    for box in table_cells:
        x = box[0][0]
        y = box[0][1]
        hor_lines.append((x, y, max_x, y))

    for box in table_cells[0]:
        x = box[0]
        y = box[1]
        ver_lines.append((x, y, x, max_y))

    (x, y, w, h) = table_cells[0][-1]
    ver_lines.append((max_x, y, max_x, max_y))
    (x, y, w, h) = table_cells[0][0]
    hor_lines.append((x, max_y, max_x, max_y))

    return hor_lines, ver_lines


if __name__ == "__main__":
    in_file = os.path.join("data", "page.jpg")
    pre_file = os.path.join("data", "pre.png")
    out_file = os.path.join("data", "out.png")

    img = cv2.imread(os.path.join(in_file))

    pre_processed = pre_process_image(img, pre_file)
    text_boxes = find_text_boxes(pre_processed)
    cells = find_table_in_boxes(text_boxes)
    hor_lines, ver_lines = build_lines(cells)

    # Visualize the result
    vis = img.copy()

    # for box in text_boxes:
    #     (x, y, w, h) = box
    #     cv2.rectangle(vis, (x, y), (x + w - 2, y + h - 2), (0, 255, 0), 1)

    for line in hor_lines:
        [x1, y1, x2, y2] = line
        cv2.line(vis, (x1, y1), (x2, y2), (0, 0, 255), 1)

    for line in ver_lines:
        [x1, y1, x2, y2] = line
        cv2.line(vis, (x1, y1), (x2, y2), (0, 0, 255), 1)

    cv2.imwrite(out_file, vis)

У меня есть следующий вывод:

Sample table extraction

Конечно, чтобы сделать алгоритм более устойчивым иПрименимо к множеству различных входных изображений, оно должно быть соответствующим образом скорректировано.

Обновление: Обновлен код с учетом изменений API OpenCV для findContours.Если у вас установлена ​​более старая версия OpenCV - используйте соответствующий вызов. Похожие посты .

0 голосов
/ 13 июня 2018

Вы можете попробовать применить некоторые морфологические преобразования (например, Dilation, Erosion или Gaussian Blur) в качестве шага предварительной обработки перед функцией findContours

Например,

blur = cv2.GaussianBlur(g, (3, 3), 0)
ret, thresh1 = cv2.threshold(blur, 150, 255, cv2.THRESH_BINARY)
bitwise = cv2.bitwise_not(thresh1)
erosion = cv2.erode(bitwise, np.ones((1, 1) ,np.uint8), iterations=5)
dilation = cv2.dilate(erosion, np.ones((3, 3) ,np.uint8), iterations=5)

Последний аргумент, итерации показывают степень расширения / эрозии, которая будет иметь место (в вашем случае, в тексте).Небольшое значение приведет к появлению небольших независимых контуров даже в алфавите, а большие значения объединят множество соседних элементов.Вам нужно найти идеальное значение, чтобы получить только этот блок вашего изображения.

Обратите внимание, что в качестве порогового параметра я принял значение 150, поскольку я работал над извлечением текста из изображений с различным фоном иэто получилось лучше.Вы можете продолжить работу с принятым значением, поскольку это черно-белое изображение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...