Хабрахабр

Распознавание номеров. Как мы получили 97% точности для Украинских номеров. Часть 2

Nomeroff Net numberplate detection OCR example
Продолжаем рассказ о том как распознавать номерные знаки для тех кто умеет писать приложение «hello world» на python-е! В этой части научимся тренировать модели, которые ищут регион заданного объекта, а также узнаем как написать простенькую RNN-сеть, которая будет справляться с чтением номера лучше чем некоторые коммерческие аналоги.
В этой части я расскажу как тренировать Nomeroff Net под Ваши данные, как получить высокое качество распознавания, как настроить поддержку GPU и ускорить все на порядок…
Конечно же, находить можно не только номер, а любой другой объект, потребность в поиске которого у вас возникла. Например можно, по аналогии, поискать кредитную карту и считать ее реквизиты. В общем, нахождение маски, в которую вписан объект на изображении называют задачей «Instance Segmentation» (об этом я уже писал в первой части).

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

Итак, алгоритм подготовки данных следующий:

  • Берем изображения размером не менее 300x300, сбрасываем все в одну папку
  • Загружаем инструмент для нанесения разметки VGG Image Annotator (VIA), можно размечать прямо онлайн, на выходе будет директория с фото и сформированный вами json-файл с разметкой. Таких папок нужно две, в одной под названием train поместите основную часть примеров, во вторую val приблизительно 20-30% от числа примеров первой паки (Конечно же, в этих папках не должно быть одинаковых фотографии). Можете посмотреть пример размеченных данных для проекта Nomeroff Net. По количеству — чем больше тем лучше. Некоторые специалисты рекомендуют 5 000 примеров, мы обленились, набрав чуть более 1 000 так как результат нас вполне устроил.

    VGG Image Annotator (VIA) editing example

  • Чтоб приступить к обучению вам нужно загрузить с Github сам проект Nomeroff Net, установить Mask RCNN со всеми зависимостями и можно попробовать запустить скрипт тренировки train/mrcnn.ipynb на наших данных
  • Сразу предупреждаю, это работает не быстро. Если у Вас нет GPU это может занять дни. Чтоб существенно ускорить процесс обучения желательно установить tensorflow с поддержкой GPU.
  • Если тренировка на нашем датасета у вас прошла успешно, теперь можно смело переключатся на свой.
  • Обратите внимание — мы не обучаем все «с нуля», мы дотренировываем модель, обученную на данных COCO dataset, которую Mask RCNN закачает при первом запуске

  • Вы можете дотренировывать не coco, а нашу модель mask_rcnn_numberplate_0700.h5, при этом укажите путь к этой модели в конфигурационном параметре WEIGHTS (по умолчанию «WEIGHTS»: «coco»)
  • Из параметров, которые можно протюнить это: EPOCH, STEPS_PER_EPOCH
  • Результат после каждой эпохи будет сбрасываться в папку ./logs/numberplate<дата запуска>/

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

Качество распознавания европейских номеров немного хуже, так как там номера с разным дизайном и увеличенным количеством встречающихся символов. В нашем проекте мы реализовали поддержку распознавания номеров Украины, РФ и Европейские номера в целом. Возможно, со временем, будут отдельные модули распознавания для «eu-ee», «eu-pl», «eu-nl»,…

Эта задача оказалась достаточно нетривиальной, мне даже пришлось вспомнить школьную математику и написать специализированную реализацию алгоритма кластеризации k-means :). Перед классификацией номерного знака его нужно «вырезать» из изображения и нормализировать, другими словами по максимуму убрать все искажения и получить аккуратный прямоугольник, который будет подвергаться дальнейшему анализу. Модуль, который это все процессит называется RectDetector, вот как выглядят нормализованные номера, которые далее будем классифицировать и распознавать.

Пример нормализации номерных знаков

С помощью этой админки вы можете разметить надпись на номерном знаке и класс к которому его относить. Чтоб как-то автоматизировать процесс создания датасета для классификации номеров мы разработали небольшую админку на nodejs.

В нашем случае по типу номера и по тому зарисован/закрашен ли он на фото. Классификаторов может быть несколько.

Админка для разметки номерных знаков Nomeroff Net

В качестве примера скачайте наш датасет autoriaNumberplateOptionsDataset-2019-03-06.zip, чтоб посмотреть как там все устроено.
После того как разметили датасет, делим его на тренировочную, валидационную и тестовую выборки.

Так как выборка уже размечена (отмодерирована), то вам нужно в рандомных json-файлах поменять «isModerated»:1 на «isModerated»:0 и после этого запустить админку.

Тренируем классификатор:

Скрипт тренировки train/options.ipynb поможет получить Вам свой вариант модели. На нашем примере видно что для классификации регионов/типов номерных знаков мы получили точность 98.8%, для классификации «закрашен ли номер?» 99,4% на нашем датасете. Согласитесь, неплохо получилось.
Ну вот мы нашли область с номером и нормализировали ее в прямоугольник, который содержит надпись с номером. Как нам прочитать текст? Проще всего прогнать его через FineReader или Tesseract. Качество будет «не очень», но при хорошем разрешении области с номером сможете получить точность на уровне 80%. На самом деле это неплохая точность, но если я Вам скажу что можете получить 97% и при этом потратите значительно меньше компьютерных ресурсов? Звучит неплохо — попробуем. Для этих целей подойдет немного необычная архитектура, в которой используются как сверточные так и рекуррентные слои. Архитектура этой сети выглядит приблизительно так:

image

Реализация взята с сайта https://supervise.ly/, мы ее немного модифицировали для тренировки на реальных фото (на сайте supervisely подан вариант для синтетической выборки)

Мы разметили около ~15 000 Украинских, ~6500 Европейских и ~5000 РФ. Теперь начинается самая увлекательная часть, разметить хотя бы 5 000, номеров :). Вы даже не представляете сколько раз я засыпал на стуле у компа модерируя по нескольку часов в день очередную порцию номеров. Это была самая сложна часть разработки. Но настоящий герой разметки dimabendera — он разметил 2/3 всего контента, (поставьте ему плюс если понимаете как скучно было делать всю эту работу 🙂 )

Можно попробовать этот процесс как-то автоматизировать, например, предварительно распознав каждое изображение Tesseract-ом, а потом уже поправить ошибки с помощью нашей админки.

Одни и те же данные вы сможете загрузить и туда и туда, кроме зарисованных номеров, конечно. Обратите внимание: для разметки классификатора и OCR на номере используется одна и та же админка.

Если вы разметите хотя бы 5000 номеров и сможете обучить свою OCR — смело оформляйте себе премию у начальства, уверен, это испытание не для слабаков!

Приступаем к тренировке

Скрипт train/ocr-ru.ipynb тренирует модель для номеров РФ, там же примеры для Украины и Европы.

Обратите внимание, в настройках тренировки там только одна эпоха (один проход).

Я вам рекомендую пробовать хотя бы 5 раз, при этом контролировать точность на тестовых данных. Особенностью тренировки такого датасета будет очень разный результат при каждой попытке, перед каждой тренировкой данные перемешиваются в случайном порядке, иногда более удачно для тренировки иногда «не очень». При разных попытках запуска у нас точность могла «прыгать» от 87% до 97%.

Несколько рекомендаций:

  • Не нужно инициализировать все по-новой просто перезапускаем строку model = ocrTextDetector.train(mode=MODE) пока не получим ожидаемый результат
  • Одна из причин плохой точности недостаточное количество данных. Если не устраивает — размечаем еще и еще, в какой-то момент качество расти перестает, для каждого датасета по-разному, можно ориентироваться на цифру 10 000 размеченных примеров
  • Тренировка будет проходить быстрее, если у вас установлен драйвер CuDNN от NVIDIA, поменяйте значение MODE = «gpu» в сценарии обучения и вместо слоя GRU подключится CuDNNGRU, что приведет к трехкратному ускорению.

Если вы счастливый обладатель GPU от NVIDIA, то вы можете в разы все ускорить: и тренировку моделей и инференс (режим распознавания) номеров. Проблема заключается в том чтоб корректно все установить и скомпилировать.

Мы на своих серверах ML используем Fedora Linux (так сложилось исторически).

Приблизительная последовательность действий для тех кто использует эту OS следующая:

  • Ставим драйвер GPU для вашей версии OS, вот тут для Fedora
  • Подключаем репозиторий NVIDIA и ставим оттуда набор пакетов CUDA, вот тут для CentOS/Fedora
  • Ставим bazel, и собираем tensorflow из исходников по этой доке
  • Так же желательно установить старую версию компилятора gcc, который называется cuda-gcc, у меня все нормально собиралось на cuda-gcc 6.4. При настройке сборки указывайте путь именно к cuda-gcc

Если собрать tensorflow с поддержкой gpu не получится, можно запустить все через docker, при этом кроме docker-а нужно доустановить пакет nvidia-docker2. Внутри docker-контейнера можно запустить jupyter notebook, и далее запускать все там.

jupyter notebook --ip=0.0.0.0 --port=8888 --allow-root

Полезные ссылки

Также хочу поблагодарить хабраюзеров 2expres, glassofkvass за предоставленные фото с номерами и dimabendera за то что написал большую часть кода и разметил большую часть данных проекта Nomeroff Net.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть