Привет, Хабр! Я Илья, Data Scientist в inDriver. В работе нам часто приходится распознавать документы водителей или пассажиров для их верификации в приложении. Наша команда выработала свой подход к идентификации текста и фото, которым я хотел бы поделиться.
В первой части статьи кратко расскажу о том, как мы распознаем фото документов и текст на них. Во второй более предметно поговорю о моделях CRAFT, CRNN и их использовании. Приятного чтения!

Минутка истории
Задача оптического распознавания символов (OCR — optical character recognition) — старая проблема, восходящая к 1970-м годам, когда была разработана первая технология OCR c омни-шрифтами (omni-font). Сложность этой задачи обусловлена естественными особенностями текстов:
-
В некоторых алфавитах найти и распознать буквы очень сложно (например, в арабском, китайском, особенно в курсиве).
-
Существует много разных шрифтов и стилей, некоторые символы слишком похожи на другие (например, буквы I и l, цифра 0 и буква O).
-
Рукописный текст бывает всех форм и размеров.
Все методы распознавания текста с глубоким обучением можно условно разделить на 3 большие категории:
-
Character-based. Эти методы сначала пытаются найти и распознать определенные местоположения отдельных символов, а затем сгруппировать в слова.
-
Word-based. Методы решают распознавание текста как проблему классификации слов, где классы — общие слова на определенном языке.
-
Sequence-to-sequence. Методы рассматривают OCR как проблему маркировки последовательностей. Одни из самых ранних работ по этому типу методов была написана китайскими авторами. Статья является оригинальной работой по описанию модели CRNN. Также в ней дается подробный обзор конкретной архитектуры GRU-CNN с вычислительной точки зрения. Различные модификации моделей CRNN работают лучше, чем другие, на многих эталонных наборах данных OCR.

Первой программой, распознающей кириллицу, был «AutoR» российской компании «ОКРУС». Программа начала распространяться в 1992 году и работала под управлением операционной системы DOS. Алгоритм «AutoR» был разработан и испытан еще в конце 1960-х годов биофизиками и выпускниками МФТИ Г. М. Зенкиным и А. П. Петровым (1, 2).
Распознавание фото документов
Перейду к тому, как все устроено в inDriver. В ряде случаев нам необходимо автоматически распознавать фото документов пользователей для их верификации в приложении. При распозновании мы регулярно стакливались с несколькими проблемами:
-
Часто необходимо распознать не весь документ, а какую-то область или поле.
-
Фотографии имеют низкое разрешение из-за старой модели телефона, или документ расположен далеко от камеры.
-
Фотографии сделаны под углом по оси Z.
-
Область документа для распознавания может быть испорчена или потерта, так что бывает трудно разобрать символы.
-
Используются разные шрифты и разные типы документов в пределах страны или города.
-
Фотография может быть перевернута на 90, 180 или 270 градусов.
Распознавание документов в inDriver включает в себя 2 основных этапа:
-
Segmentation. Сегментация области документа, которая должна быть распознана.
-
Recognition. Распознавание текста или Detection (детекция) и распознавание.
Расскажу подробнее о сегментации. Задачи компьютерного зрения разделяют на несколько видов:
-
Classification. Классификация изображения по типу объекта, которое оно содержит.
-
Object Detection. Обнаружение всех объектов указанных классов и определение охватывающей рамки для каждого из них (bounding box).
-
Semantic Segmentation. Выделение разных классов объектов на изображении, включая среду.
-
Instance Segmentation. Разделение объектов одного класса на разные объекты.
-
Panoptic Segmentation. Объединение задач семантической и инстанс-сегментации. Также в задаче паноптической сегментации каждому пикселю изображения должна быть присвоена ровно одна метка.

В первой итерации мы использовали instance segmentation модель на TensorFlow2 архитектуры MASK RCNN (TensorFlow Hub). С помощью Fine-Tuning дообучили модель на наших данных. Она позволила получать bounding box и mask размеченной области документов.
Но вскоре мы отказались от ее использования. Проблема — много зависимостей от TensorFlow Object Detection API. Нужно было сериализовывать данные в собственный формат TFRecords.
Было решено перейти на PyTorch-фреймворк detectron2. На тестах метрики у моделей из detectron2 были выше, чем у модели на TensorFlow2 (даже без тестов можно сравнить метрики на TFHub и model zoo detectron2). Для detecron2 использовали предобученные веса модели СOCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x (в зависимости от ресурсов и времени можно выбрать готовые веса).
Датасет создавался с помощью ручной разметки через приложение labelme, а потом конвертировался в формат COCO датасета, где был написан свой, немного адаптированный скрипт. Была попытка использовать Label Studio, но софт показался не совсем дружелюбным и больше ориентированным на команду разметки.
Так как в документах обычно одно уникальное поле и оно не дублируется, можно было использовать модели из semantic segmentation (маски) или object detection (bounding box). Но мы применили instance segmentation, чтобы получить маски и боксы.
Следующий шаг после определения нужных полей для распознавания — само распознавание текста.
Распознавание текста
В первой итерации использовались несколько моделей распознавания:
-
EasyOCR. Для детекции CRAFT + CRNN для распознавания.
-
keras-ocr. CRAFT + CRNN.
-
MMOCR. Модели распознавания SAR, SATRN, RobustScanner, SegOCR, а также разные модели детекции текста в связке с моделями распознавания.
В основе библиотеки EasyOCR (PyTorch), как и в keras-ocr лежат 2 модели: детекции текста CRAFT(Character Region Awareness for Text Detection) и распознавания текста CRNN. Фреймворк MMOCR предлагает намного больше моделей.
Tesseract изначально не рассматривался, так как он хорошо работает с фотографиями высокого разрешения (где могут помочь морфологические операции), что нам не подходит. Также нам нужна высокая скорость обработки на GPU. Хотя Tesseract можно адаптировать под себя, как, например, в статье с исправлением шрифтов, но этот метод не универсален.
Используемые нами модели выдавали в ответ координаты боксов, а также распознанный текст и его score. Из всех моделей выбирался вариант с наибольшим score. Часто фото документов были сделаны под углом, для выравнивания использовался алгоритм Perspective Transformation по 4 координатам бокса. Использовалась и предобработка маски с помощью opencv. Например:
-
Выравнивание гистограммы cv2.equalizeHist.
-
Расширение изображения dilation:
kernel = np.ones((5,5), np.uint8). i_im = cv2.dilate(i_im, kernel, iterations=1).
-
Нормализация:
i_im = cv2.normalize(i_im, i_im, 0, 255, norm_type=cv2.NORM_MINMAX)
-
Размытие:
i_im = cv2.GaussianBlur(i_im, (7, 7), 0)
-
Увеличение резкости:
kernel = np.array([[-1,-1,-1], [-1, 9,-1], [-1,-1,-1]]) i_im = cv2.filter2D(i_im, -1, kernel)
-
Разные варианты порогового преобразования:
im = cv2.threshold(im, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
Для улучшения качества изображения мы пробовали увеличивать разрешение с помощью интерполяции и моделей Super-Resolution. Для правильного определения ориентации маски или фото применяли модель классификации на 4 классах с поворотом на 0, 90, 180, 270 градусов.
Также были попытки обучить на синтетически сгенерированных данных модель CRNN из EasyOCR. Но здесь мы столкнулись с проблемой подбора шрифтов — не удавалось найти один шрифт, чтобы все символы были похожи на символы из оригинальной выборки. Поэтому при генерации синтетических данных мы использовали несколько шрифтов, которые подбирали с помощью онлайн-сервисов (например, WhatTheFont! « MyFonts).
Еще столкнулись с изначально некорректной разметкой при сегментации, выделяли не только нужные данные, но и поле/ячейку, часто с названием поля и рамками. Приходилось на препроцессинге выделять только нужную область, а также использовать маски с пустыми рамками для генерации синтетического датасета.
Так как готовые модели были обучены на синтетических датасетах, которые были сгенерированы без большого разнообразия шрифтов и с недостаточной аугментацией, на реальных фото часто путались цифры с буквами (например 6 и G, 7 и T, 2 и Z).
При работе с документами нам часто приходится распознавать определенные последовательности чисел или символов. Поэтому мы решили попробовать character-based метод. Для выделения области распознавания и разделения на символы мы сначала использовали средства opencv. Но не всегда удавалось отделить символы друг от друга, потому что часто документы были измяты или потерты, и исходное фото было в низком разрешении.
В итоге для разделения символов мы начали использовать модель CRAFT. Для Python есть удобная реализация в виде библиотеки craft-text-detector. Библиотека позволяет регулировать параметры выделения текста и получать боксы каждой области текста или символа в отдельности.
Для примера распознаем текст с помощью библиотеки craft-text-detector на кадрах из первого части «Человека-паука» 2002 года:




Здесь:
-
Text detection boxes — боксы с текстами (которые получаются с помощью работы моделей CRAFT + LinkRefiner).
-
Text score heatmap — вывод модели CRAFT (по каждому символу).
-
Link score heatmap — вывод модели LinkRefiner (модели связи символов).
Чтобы получить char-boxes, а не text-boxes, мы можем выставить значение параметра link_threshold=999999 (сделать большим). Тогда не будет учитываться работа модели связи символов LinkRefiner. Например:
prediction_result = get_prediction( image=image, craft_net=craft_net, refine_net=refine_net, text_threshold=0.7, link_threshold=999999, low_text=0.4, cuda=True, long_size=1280 )

Более подробный код запуска есть на GitHub. Также с этой библиотекой удобно менять разные пороговые значения. Например, для распознавания более мелких или крупных шрифтов.
Далее боксы распознаных символов можно подать в модель классификации, которая обучается с дополнительной аугментацией с учетом поворотов и прочих шумов.
Но подробнее о моделях CRAFT, CRNN и их использовании я расскажу в следующей части статьи. Если у вас есть вопросы или комментарии — пишите, с удовольствием отвечу.
ссылка на оригинал статьи https://habr.com/ru/company/indriver/blog/594357/
Добавить комментарий