Разработка системы распознавания текста на кассовых чеках

IT-копирайтер
Время чтения: 12 минут
Задача распознавания текста в различных условиях была и остаётся актуальной. Автоматизировать распознавание документов, кредитных карт, распознать и перевести на другой язык вывеску на билборде – всё это могло бы сэкономить время на сбор и обработку нужных данных. С развитием свёрточных нейронных сетей и методов их обучения качество распознавания текста неуклонно растет.
Мы в очередной раз убедились в эффективности использования свёрточных нейронных сетей в процессе работы над проектом по распознаванию кассовых чеков. Объектом исследования выступили кассовые чеки из ряда российских торговых точек, с текстом на кириллице и латинице. При этом разработанная система может быть легко адаптирована под распознавание кассовых чеков из других стран, с текстом на других языках. Рассмотрим проект в деталях, чтобы показать принцип действия полученного решения.
Обзор проекта
Задача распознавания чеков состоит из нескольких этапов:
1. Препроцесссинг
• Поиск чека на изображении
• Бинаризация
2. Выделение текста
3. Распознавание
4. Извлечение необходимой семантической составляющей чека
Реализация
1. Препроцессинг
Задача препроцессинга состоит в следующем: повернуть изображение так, чтобы строки чека располагались максимально горизонтально, найти чек на изображении и бинаризовать его.
1.1. Поворот изображения и поиск чека на нём
Задачу поиска чека мы выполняли при помощи следующих методов:
- Адаптивная бинаризация с высоким порогом
- Свёрточная нейронная сеть
- Классификатор с признаками Хаара
Поиск чека при помощи адаптивной бинаризации
с высоким порогом
Рис. 1. Изначальный вид чека
На данном этапе задача состояла в том, чтобы найти на изображении область, которая содержит весь чек и минимальное количество фона.
Для упрощения задачи поиска картинка сначала поворачивается так, чтобы строки располагались максимально близко к горизонтальному положению (Рис. 2). Алгоритм поворота необходим, чтобы максимизировать дисперсию суммы яркости по строкам. Максимум достигается, когда строки расположены горизонтально.
Рис. 2. Поворот чека
Для поиска чека мы использовали функцию adaptive_threshold из библиотеки scikit-image. Это адаптивная бинаризация с большим порогом, которая оставляет пиксели белыми в областях с высоким градиентом, а более однородные области становятся черными. Таким образом, при достаточно однородном фоне остаётся небольшое количество белых пикселей, для которых мы ищем описанный прямоугольник. В результате полученный прямоугольник (Рис. 3) включает в себя область с чеком и минимальное количество фона.
Рис. 3. Найденная область с чеком
Поиск чека свёрточной нейронной сетью
Мы решили искать ключевые точки чека при помощи свёрточной нейронной сети, как мы делали это ранее в проекте по локализации объектов на изображении. В качестве ключевых точек были выбраны углы чека. Такой метод оказался неплохим, но по качеству он проигрывал методу адаптивной бинаризации с высоким порогом.
Свёрточная нейронная сеть показала не самый лучший результат, потому что она обучилась предсказывать координаты углов только относительно найденного текста. Вместе с тем, расположение текста относительно углов отличается от чека к чеку, поэтому точность полученной модели свёрточной нейронной сети не самая высокая.
Представляем вам результаты работы сети:
Рис. 4. Примеры работы свёрточной нейронной сети на поиск углов чека
Поиск чека каскадным классификатором с признаками Хаара
В качестве альтернативы мы решили попробовать классификатор с признаками Хаара. Потратив на обучение около недели, регулируя параметры обнаружения чека, мы так и не получили достойного результата. Свёрточная нейронная сеть показала более высокое качество работы.
Примеры работы каскадного классификатора с признаками Хаара:
Рис. 5. Положительные результаты работы каскадного классификатора с признаками Хаара
Рис. 6. Ложноотрицательное и ложное срабатывания классификатора с признаками Хаара
1.2. Бинаризация
Для бинаризации используется тот же adaptive_threshold, окно достаточно большое, чтобы оно содержало и текст, и фон (Рис. 7).
Рис. 7. Бинаризация чека
2. Выделение текста
2.1. Выделение текста методом связных компонент
Первый этап выделения текста – это поиск связных компонент. Мы реализовали его с помощью функции findContours из OpenCV. Большинство связных компонент в самом деле являются символами, но некоторые – остатки шума после бинаризации. Мы отсеяли их при помощи фильтров по максимальной/минимальной площади. Для составных символов применили алгоритм объединения связных компонент (:, Й, =). После этого символы объединяются в слова при помощи поиска ближайших соседей. Принцип поиска ближайших соседей: для каждого символа ищутся несколько ближайших соседей, затем из них выбирается наиболее подходящий кандидат на присоединение справа и слева. Алгоритм повторяется, пока не останется символов, не принадлежащих словам (Рис. 8).
Рис. 8. Поиск связных компонент и формирование слов (слова выделены одним цветом)
Далее формируются строки из полученных слов. Для этого используется гипотеза, что в одной строке слова расположены на одной высоте (y-координата).
Рис. 9. Формирование строк (строки выделены одним цветом)
Недостаток этого алгоритма состоит в том, что он не способен корректно распознавать слова со слипшимися или разорванными буквами.
2.2. Выделение текста при помощи сетки
Мы заметили, что почти во всех чеках текст моноширинный. Это значит, что можно нарисовать на чеке сетку так, чтобы линии сетки проходили между символами:
Рис. 10. Пример сетки
Алгоритм автоматического поиска сетки чека упрощает дальнейшее распознавание чека: нейронная сеть применяется к каждой ячейке сетки, распознается каждый символ, нет проблем со слипшимися символами и разорванными символами, точно определяется количество пробелов, которые идут друг за другом в строке.
Для поиска такой сетки мы попробовали следующий алгоритм. Сначала находятся связные компоненты на бинаризованном изображении:
Рис. 11. Пример поиска связных компонент
Затем берем нижние левые углы этих зеленых прямоугольников и получаем набор точек заданных двумя координатами. Для определения искажений мы решили использовать следующую двумерную периодическую функцию:
График этой формулы выглядит следующим образом:
Рис. 12. График функции в формуле
Идея метода выделения сетки чека заключается в поиске таких нелинейных геометрических искажений координат точек, чтобы точки попали на пики графика. То есть задача сводится к задаче поиска максимума суммы значений этой функции. При этом ищется оптимальное искажение.
Геометрическое искажение было параметрезированно с помощью функции RectBivariateSpline из модуля scipy в python. Оптимизация осуществлялась с помощью функции minimize из модуля scipy.
Пример правильно найденной сетки:
Рис. 13. Пример правильно найденной сетки
Рис. 14. Пример неправильно найденной сетки
Мы отказались от данного метода, потому что он имеет ряд существенных недостатков – работает нестабильно и медленно.
3. Распознавание текста
3.1. Распознавание текста, найденного методом связных компонент
Распознавание текста производится при помощи сверточной нейросети, обученной на шрифтах, вырезанных с чеков. На выходе из сети мы имеем вероятности для каждой буквы и берем несколько первых вариантов, которые в сумме дают близкую к 1 вероятность (99%). Далее рассматриваем все возможные варианты составления слов из полученных букв и проверяем их по словарю. Это позволяет улучшить точность распознавания, исключая ошибки среди похожих символов (З и Э).
К сожалению, данный метод работает стабильно только в том случае, когда буквы не разрываются и не слипаются между собой.
3.2. Распознавание слова целиком
Распознавать слово целиком необходимо в сложных случаях, когда буквы разрываются и слипаются между собой. Эту задачу мы решали двумя способами:
- при помощи рекуррентной нейронной сети вида LSTM;
- при помощи равномерной сегментации.
LSTM
Для сложных случаев мы решили использовать нейронную сеть вида LSTM, чтобы распознавать все слово целиком, опираясь на опыт исследований в статьях “Reading Scene Text in Deep Convolutional Sequences” и “Can we build language-independent OCRusing LSTM networks?”. Для этой цели мы взяли библиотеку OCRopus.
Использовав моноширинные шрифты, мы подготовили искусственную выборку для обучения (рис. 15).
Рис. 15. Примеры искусственной выборки
Обучив сеть, мы протестировали её на валидационной выборке. Результаты тестирования показали, что сеть обучилась хорошо. Тогда мы проверили её на реальных чеках. Ниже представлены результаты:
Обученная нейронная сеть неплохо работала на простых примерах, которые мы и так успешно распознаем другим способом. Со сложными примерами сеть не справлялась.
Мы решили добавить различные искажения в обучающую выборку, для того чтобы приблизить её к словам, получаемым с чеков (Рис. 16).
Рис. 16. Примеры искусственной выборки
Для того чтобы сеть не переобучилась, мы останавливали тренировку сети, готовили новый датасет и обучали сеть дальше уже с новым датасетом. В результате обучения мы получили следующее:
Полученная нейронная сеть распознавала сложные слова лучше, но стала хуже распознавать простые слова. Такая модель нас не удовлетворила, поскольку она не отличалась стабильностью.
Мы предполагаем, что при одном шрифте и с малыми искажениями такая сеть работала бы намного лучше.
Равномерная сегментация
У нас возникла идея разбить слово на символы равномерно, поскольку шрифт на чеках моноширинный. Для этого необходимо знать ширину символа в слове. Для каждого чека оценивается мода ширины символа. Если распределение ширин символов бимодальное (Рис. 17), то выбираются две моды и для каждой строки определяется своя ширина.
Рис. 17. Пример бимодального распределения ширин символов в чеке
После того как мы получаем примерную ширину символа в данной строке, мы делим длину слова на ширину символа и получаем примерное количество букв. Затем делим длину слова на полученное примерное количество букв плюс-минус один:
Рис. 18. Процесс поиска оптимальной сегментации
И выбираем наилучший вариант разбиения:
Рис. 19. Оптимальная сегментация
Точность такой сегментации весьма высока.
Рис. 20. Пример корректной работы алгоритма
Но иногда мы наблюдали, что алгоритм работает не совсем корректно:
Рис. 21. Пример некорректной работы алгоритма
После сегментации каждый фрагмент отправляется в свёрточную нейронную сеть и распознаётся.
4. Извлечение необходимой семантической составляющей чека
Поиск покупок в чеке совершается при помощи регулярных выражений. Для всех чеков есть одна общая особенность: цена покупок записана в формате XX.XX, где Х – цифра. Таким образом можно извлечь строки с покупками. ИНН ищется по 10 цифрам и проверяется по контрольной сумме. Имя держателя карты ищется в формате NAME/SURNAME.
Рис. 22. Результаты извлечения необходимой семантической составляющей чека
Заключение
Задача распознавания кассовых чеков оказалась не столь простой, как на первый взгляд. В процессе поиска решения мы столкнулись с большим количеством подзадач, каждая из которых полностью или частично связана с другими. Зачастую такие сложные алгоритмы, как рекуррентная нейронная сеть вида lstm, воспринимаются как универсальный инструмент. Но на деле подобные методы требуют много времени на освоение и не всегда оказываются полезными.
Работа над проектом продолжается. Мы занимаемся улучшением качества на каждом этапе распознавания и оптимизацией отдельных алгоритмов. На данный момент система с высокой точностью распознаёт чеки хорошего качества – без слипшихся и разорванных букв. Чеки со слипшимися и разорванными буквами распознаются несколько хуже.
Если вам интересна тема использования современных технологий, можете ознакомится с нашими статьями о распозновании речи с помощью нейросети и распозновании изображений с локализацией определенных объектов.
Комментарии