Как мы распознаем фото документов пользователей. Часть I
Привет, Хабр! Я Илья, 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.
3 категории распознавания текста
Первой программой, распознающей кириллицу, был «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 boxesText score heatmapLink score heatmap
Здесь:
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
)
Char detection boxes
Более подробный код запуска есть на GitHub. Также с этой библиотекой удобно менять разные пороговые значения. Например, для распознавания более мелких или крупных шрифтов.
Далее боксы распознаных символов можно подать в модель классификации, которая обучается с дополнительной аугментацией с учетом поворотов и прочих шумов.
Но подробнее о моделях CRAFT, CRNN и их использовании я расскажу в следующей части статьи. Если у вас есть вопросы или комментарии — пишите, с удовольствием отвечу.