{"id":14277,"url":"\/distributions\/14277\/click?bit=1&hash=17ce698c744183890278e5e72fb5473eaa8dd0a28fac1d357bd91d8537b18c22","title":"\u041e\u0446\u0438\u0444\u0440\u043e\u0432\u0430\u0442\u044c \u043b\u0438\u0442\u0440\u044b \u0431\u0435\u043d\u0437\u0438\u043d\u0430 \u0438\u043b\u0438 \u0437\u043e\u043b\u043e\u0442\u044b\u0435 \u0443\u043a\u0440\u0430\u0448\u0435\u043d\u0438\u044f","buttonText":"\u041a\u0430\u043a?","imageUuid":"771ad34a-9f50-5b0b-bc84-204d36a20025"}

Повышение качества распознавания сканов документов с таблицами с помощью вычисления координат ячеек

Библиотека распознавания текста tesseract является открытым, безопасным и полезным инструментом в аудите. К сожалению, она пасует при распознавании массивных таблиц на скан образах документов. Рассмотрим как обойти это ограничение.

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

Актуальным решением проблемы обработки таблиц на данный момент является распознавание каждой ячейки по отдельности. Встроенный в tesseract метод image_to_data требует настройки для каждого шаблона документа, поэтому проще использовать внешние решения. Популярным решением является нейросеть An Efficient and Accurate Scene Text Detector (далее — EAST), которая у нас не прижилась из-за сложности развертки инфраструктуры. К тому же, функционал EAST избыточен для поиска машинно-распознаваемых полей в сканах документов – нейросеть решает проблемы зашумленности, нарушений перспективы, размытости изображений, бликов, текста на неплоских объектах, цветного и плохо освещенного текста – в общем того, чего в сканированных изображениях нет. В результате было принято решение вернуться к менее популярному, но более традиционному методу поиска ячеек таблиц при помощи простой и надежной эвристики в opencv.

Opencv –библиотека алгоритмов компьютерного зрения, обработки изображений и численных алгоритмов общего назначения с открытым кодом. В числе прочего, в библиотеке реализована функция поиска контуров объектов на картинке.

Описываемая часть задачи заключалась в извлечении информации из табличной части скан-образов документов и переводе ее в привычный для работы формат, такой как строки или даже таблицы, с целью использования в различных аудиторских проверках. Реализовано это было на языке python, привычном в ML-сообществе, благо интерфейсы opencv и tesseract присутствуют в нем в полной мере.

Первым делом документы нужно было извлечь из pdf. Для этого использовалась библиотека PyMuPDF(импортируется как fitz) с преобразованием в понятный opencv формат при помощи массивов numpy:

pages = [] doc = fitz.open(file) for n in range(doc.pageCount): page = doc.loadPage(n) pix = page.getPixmap() image = np.frombuffer(pix.samples, dtype=np.uint8).reshape(pix.h, pix.w, pix.n) image = np.ascontiguousarray(image[..., [2, 1, 0]]) pages.append(image)

Способ применения к полученному списку страниц метода распознавания это скорее вопрос инфраструктурный, поскольку самым ресурсоемким во всем процессе в любом случае останется само распознавание текста. Покажем выполняемые методом действия на примере скана страницы журнала «Моделист-конструктор» № 8’2007, в дальнейшем называя его «image»:

Для наглядности прогоним его через tesseract при помощи его python обертки:

recognized_text = pytesseract.image_to_string(image,lang="rus") print(recognized_text.replace("\n"," "))

‘офи [ом [ м | пятидверный Высота, мм 1766 Бы Угол въезда/съезда, град. 25,4/22,9 25,4/22,9 620 — 1750 620 — 1750 Снаряженная масса, кг 2125 2180 Бензиновый с распределенным Дизельный впрыском топлива с турбонаддувом 7. Объем багажника; л. — Двигатель Рабочий объем, см? 2996 4799 ни ош Привод Полный с многодисковой муфтой в приводе р передних колес Нередняя подвеска независимая пружинная двухрычажная Задняя подвеска независимая | многорычажная | пневматическая’

К сожалению, с легкочитаемой человеком таблицы оказалось распознано не всё. Причина в том, что tesseract пытается распознать части таблицы как символы. Этого можно избежать, обрезая страницу по контурам ячеек и распознавая при помощи tesseract отдельные ее фрагменты, что и является освещаемой в этой статье темой.

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

Первым делом преобразуем изображение в черно-белое по пороговому значению яркости (1), чтобы полутона не мешали алгоритму размытия. После этого размоем изображение так, чтобы буквы склеились между собой и образовали совокупный контур (2). Методом cv2.findContours обнаружим все контуры на обработанном изображении (3). Получим координаты и размеры прямоугольников вокруг каждого контура, и отфильтруем контуры похожие на ячейки таблицы (4). Для наглядности промежуточные результаты приведены ниже.

# remove color info gray_image= image[:,:,0] # (1) thresholding image ret,thresh_value = cv2.threshold(gray_image,180,255,cv2.THRESH_BINARY_INV) # (2) dilating image to glue letter with e/a kernel = np.ones((2,2),np.uint8) dilated_value = cv2.dilate(thresh_value,kernel,iterations = 1) # (3) looking for countours contours, hierarchy = cv2.findContours(dilated_value,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # (4) extracting coordinates and filtering them empirically coordinates = [] for contour in contours: x,y,w,h = cv2.boundingRect(contour) if h> 50 and w>50 and h*w<350000: coordinates.append((x,y,w,h))

Результатом станет список координат и размеров ячеек таблицы вида:

[(922, 1096, 254, 52),

(682, 1093, 238, 53),

.. .

(499, 19, 191, 53),

(33, 16, 465, 54)]

Теперь можно нарезать страницу по этим координатам и распознать при помощи tesseract.

Для сохранения структуры таблицы в результате используем соображение о том, что каждая новая строка таблицы ниже предыдущей. После этого останется лишь применить приведенный метод ко всему списку страниц.

def sort2(val): #helper for sorting by y return val[1] recognized_table = row = [] prev_y = 0 coordinates.sort() #sort by x coordinates.sort(key = sort2) # sort by y for coord in coordinates: x,y,w,h = coord if y>prev_y+5: #new row if y is changed recognized_table.append(row) row = [] crop_img = image[y:y+h, x:x+w] recognized_string = pytesseract.image_to_string(crop_img, lang="rus") row.append(recognized_string.replace("\n"," ")) prev_y = y recognized_table
[[...], ['| Модификация', '3,051', '4,81', '30а'], ['| Тип кузова', 'пятидверный', 'универсал'], ['Число мест .', '50)', '50)', '`. 50'], ['', '4854', '4854', '4854'], ['Ширина, мм', '_—_ 1933', '1933', '__` 1933'], ['', '_ 1766', '1766.', '1766'], ['аза, мм', '2933', '2933', '2933'], ['Колея, мм', '', '1644', '1644'], ['Клиренс, мм .', '22.', '', '= 212'], ['Уго л въезда/ съезда, гад. | ГВ ъ е зд. ал /съе: З, д. а ‚Г р: ад', '25,4/22,9', '', '25,4/22,9'], ['| Объем багажника; л -', '620 — — -1750', '620 — 1750', '620 —1750.'], ['', '', '2245 =', '2180'], ['Полная масса, кг', '2680 —', '', '2740'], ['Двигатель', 'И | Бензиновый с распределенным впрыском топлива', '| Дизельный с турбонаддуво'], ['Число и расположение цилиндров', '— бвряд', '. 8 У-образно', '6в ряд'], ['Рабочий объем', '2996', '4799', '2993'], ['Максимальная мощность, л.с. ^', '272', '_355', '235'], ['’Привод', 'Полный с многодисковой муфтой в приводе передних колес'], ['Нередняя подвеска | о аа', 'независимая', 'пружинная', 'двухрычажная']]

Видно, что распознано намного больше чисел, исправлена часть ошибок, а главное — сохранена структура таблицы, что оказывается еще более важно, если таблица имеет неравномерный размер ячеек, пропуски значений или вообще является диаграммой. Таблицу в таком виде можно сохранить в csv и открыть в excel для дальнейшей работы:

Это не единственный, но весьма мощный способ улучшения качества распознавания скан-образов, который хорошо сочетается с прочими методами препроцессинга изображений, выходящими за рамки этой статьи.

На наглядном примере мы продемонстрировали один из зарекомендовавших себя методов решения проблемы распознавания таблиц, облегчающий автоматическую проверку сканов документов. Надеемся, что предложенные нами пути решения помогут Вам при распознавании скан-образов документов, содержащих табличные данные.

0
Комментарии
-3 комментариев
Раскрывать всегда