25 ноября 2014 в 16:18

И ещё раз про распознавание номеров

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



В любых задачах обработки изображений 90% успеха — хорошая база данных. Репрезентативная и большая. Весной мы обещали выложить полную базу изображений того, что нам придёт. Подписка блога заканчивается, поэтому время выполнить обещание (блог может продлят, а может и нет). Наш сервер работал 95% времени, начиная с первого поста. Всё что пришло теперь доступно + мы сделали отдельные базы по вырезанным номерам и нарезанным символам.

Под катом ссылки на базу + её анализ + немного кода + небольшой рассказ о том, что будет сделано дальше с нашим сервером/жизнью проекта.

Сама база


Базы по номерам у нас не размечены (нигде нет файла с правильной расшифровкой). Размечена только база по символам.
База необрезанных фотографий автомобилей (1.4 ГБ). Примерно 9300 кадров.
Размеры будут от пары сотен пикселей до десятка мегапикселей. Выглядят картинки так:



База вырезанных номеров + контрпримеров (260 МБ). Примерно 5000 номеров + 1200 контрпримеров.

Выглядят картинки так:



База нарезанных символов для российских номеров (60 МБ). Примерно 18 тысяч букв, цифр и контрпримеров.
*Маленькое дополнение. Папка «17» — пустая. В ней были буквы «O», но классификаторы не делали различия от неё и нуля, поэтому мы их объединили в папке «0».

Выглядят картинки так:



Пара слов про базу


База основана на том, что мы собирали сами + на том, что нам присылали + ручное прореживание от плохих кадров. Для буков дана нарезанная база отрицательных примеров (папка за номером 22). Без отрицательной выборки почти невозможна работа реального алгоритма, буквы будут определяться на любом шуме. В отрицательной выборке содержатся куски буков, без этого порезанная пополам восьмёрка может быть принята за тройку. В конце статьи будет пример такого использования такой базы. Отрицательная выборка дана и для вырезанных номеров, если найдутся желающие обучить каскад по ним.

Из минусов базы:

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

Зачем это нужно?


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

Интересна она и для тестирования алгоритмов машинного распознавания и классификации. Взять тот же MNIST. Синтетическая задача, но до сих пор многим интересна. А тут имеется возможность реального применения обученного алгоритма.

Дальнейшая жизнь проекта


Сервер с распознаванием номеров мы планирует поддерживать в рабочем состоянии + иногда мы апдейтим алгоритм. Приложение на телефоны, как мы и предполагали, особо не выстрелило. Оно доступно в PlayStore + есть версия под iPhone. Приложения в целом рабочие, но какой-то дальнейшей их поддержки мы делать не будем. Да и особо большой базы не собралось. Код для обоих приложений открыт. Если хотите, можете допиливать самостоятельно.
С другой стороны мы были приятно удивлены, что наш сервер зачастую стали использовать как некий эталон для проверки своих алгоритмов. Раза три нам загоняли на сервер огромные базы. Точно мы не знаем, кто и зачем. Но, приятно. Видно, что люди потратили время, чтобы сравнить свой алгоритм с нашим, пусть даже далёким от идеала.
Отсюда родилась идея: небольшой кусок базы мы оставили себе. Если вдруг кому будет интересно, можем прогнать алгоритм по нашей базе (или дать базу вам), на условиях нераспространения базы и публикации результатов. Результаты мы опубликуем тут и тут, при публикации ставим ссылку на ваш сайт/контакт.
Нераспространение такой тестовой базы нужно чтобы избежать подгонки результатов, как делают при наличии открытых баз (например MNIST).

Если блог нам продлят, то о результатах и методологии оценки будем отчитываться на Хабре.

Про каскад


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

Обучение распознавания букв


Не смотря на то, что всю логику и алгоритмы мы опубликовали в прошлых статьях, исходники куска, где происходит распознавание букв номера мы пока публиковать не хотим (если вдруг решим отключить сервер окончательно, то, конечно, опубликуем).

Но всё же, нам хотелось бы показать способ, как можно распознавать номера быстро и просто. Поэтому мы перебрали несколько вариантов простых алгоритмов позволяющих распознать буквы, которые легко обучить (при наличии большой базы) и обучили по базе, которую выложили выше. Наилучшие проценты и наиболее простая работа на наш взгляд у SVM в библиотеке Accord (ML-библиотека проекта AForge). В принципе, всё аналогично делается и в OpenCV, SVM есть и там.

Обучение:

using Accord.MachineLearning.VectorMachines;
using Accord.MachineLearning.VectorMachines.Learning;
using Accord.Statistics.Kernels; 

//Набор входных изображений, развёрнутых в одномерные массивы
double[][] inputs;
//Набор ответов чем являются входные изображения
int[] outputs;
//"Размазанность" гауссианы при обучении. Чем ниже значение, тем больше "обобщения" делает SVM
double sigma = 12;
//Количество классов при обучении. В номерах 10 цифр, 12 букв, + 1 класс с отрицательной выборкой
int classCount=23;
MulticlassSupportVectorLearning teacher = null;
//Параметры распознающей машины: длина массива на каждую фотографию, параметр ядра обучения, количество классов
//sigma - единственный настраиваемый параметр обучения. Я ставил где-то 10-20, изменялась точность незначительно.
machine = new MulticlassSupportVectorMachine(width*height, new Gaussian(sigma), classCount);
//Инициализация обучения
teacher = new MulticlassSupportVectorLearning(machine, inputs, outputs);
teacher.Algorithm = (svm, classInputs, classOutputs, i, j) => new SequentialMinimalOptimization(svm, classInputs, classOutputs) { CacheSize = 0 };
teacher.Run();
machine.Save("MachineForSymbol");

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

MulticlassSupportVectorMachine machine = MulticlassSupportVectorMachine.Load("MachineForSymbol");
int output = machine.Compute(input);

Важно, чтобы входные данные были бинаризованы. Это значительно повышает точность работы. Приведу пример загрузки:

using AForge.Imaging;
using AForge.Imaging.Filters;
private static List<double> test(string str)
        {
            Bitmap bmp = new Bitmap(str);
//При обучении нужно, чтобы все изображения были единого размера. База которую мы привели позволяет обучатся на размере 34*60. Тут мы её немножко ужимаем, для скорости работы.
            ResizeNearestNeighbor filter = new ResizeNearestNeighbor(17, 30);
            int count = 0;
            BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
            ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            List<double> res = new List<double>();
            int width = bitmapData.Width;
            int height = bitmapData.Height;
            int stride = bitmapData.Stride;
            int offset = stride - width*3;
            unsafe
            {
                byte* ptr = (byte*)bitmapData.Scan0.ToPointer();
                double summ = 0;
                for (int y = 0; y < bitmapData.Height; y++)
                {
                    for (int x = 0; x < bitmapData.Width; x++, ptr+=3)
                    {
//Можно загрузить ЧБ изображения, но тут предполагается, что работаем с цветными изображениями
                        res.Add((ptr[0] + ptr[1] + ptr[2]) / (3*255.0));
                        summ += (ptr[0] + ptr[1] + ptr[2]);
                    }
                    ptr += offset;
                }
                summ = summ / (3*255.0* bitmapData.Height * bitmapData.Width);
//Бинаризуем по среднему цвету изображения
                for (int i = 0; i < res.Count; i++)
                {
                    if (res[i]<summ)
                        res[i] = 0;
                    else
                        res[i] = 1;
                }
            }

            bmp.UnlockBits(bitmapData);
            return res;
        }

Пример обученного SVM'a. Процент правильно распознанных символов по независимой от обучающей выборке — 96%.

P.S.


Из историй связанных с номерами. Сейчас в Казахстане происходит (или происходил) какой-то массовый тендер на установку систем распознавания номеров. Но компетентных фирм, связанных с системными решениями IT там не то очень мало, не то нет совсем. Где-то раз-два в месяц с нами связывается очередной менеджер оттуда и предлагает поставить им систему немедленно, уже обученную под номера страны. При этом путая слова «сервер» и «камера»…
Автор: @ZlodeiBaal
Recognitor
рейтинг 36,02

Комментарии (21)

  • +1
    о! как я этого ждал! :)
    Молодцы! теперь можно будет объективно сравнивать распознавалки.
    • +5
      Ну, это если владельцы распознавалок захотят их сравнивать;)
      К тому же зачастую существуют весьма специфические решения, которые работают в строго определённых условиях, а с произвольными кадрами могут не справляться. Зато при наличии условий: подсветка, положение камеры, ракурс, и.т.д. справляются лучше всех. Такие алгоритмы сложно тестировать по базе, они настроены на оборудование…
      • 0
        скажу за себя — мне будет интересно :)
  • +3
    Пособники большого брата)
  • +1
    А нельзя попросить в алгоритм внести номер-другой, которые бы гарантированно не распознавались? ))
    • 0
      А почему вы уверены, что если кто-то и будет использовать наш алгоритм, то это будет плюсом?:)
      В какой-то момент с нашим алгоритмом экспериментировал товарищ, который автоматический КПП к себе в деревню делал;)
      • 0
        Потому что вы — молодцы, и еще потому, что хороший алгоритм можно хоть куда приделать, но он не перестанет быть отличным.

        КПП кто-то делал. Хоть не тюрягу оснащал, и то хорошо. Он буренкам номера не привесил, кстати, чтобы они с пастбища могли пройти в деревню? ))
  • 0
    А на рыб распознование будет работать?
  • –3
    Самое сложное — это нахождение автомобильных номеров в потоке, где в кадре находятся сразу несколько номеров.
    А распознавание — это уже ерунда, тривиальная задача
    • +4
      :)))) Вооот, а нам кажется наоборот, что выделить номер в потоке — ерунда. А как раз распознать, когда он снят черти как, с перспективой, «весь забрызган грязью, абсолютно весь», да еще буквами подтертыми — вот это приятно.
      • –4
        Так говорит только человек, никогда не сталкивавшийся ранее с поиском номеров на изображении.
        Какие у вас алгоритмы нахождения номеров в кадре?
        Днём, ночью, в дождь, снег, запачканные номера… Вы вот сейчас серьёзно говорите, что это легко? Не верю.
        Ссылки в студию, как говорится.
        • +4
          Вы хоть по ссылкам в тексте статьи походили?.. Там же всё есть… Вплоть до примеров и исходников.
          Вот наш блог на хабре, там 7 статей на тему — habrahabr.ru/company/recognitor/
          Вот рабочая версия на Андроиде — play.google.com/store/apps/details?id=org.opencv.samples.tutorial334565
          Вот её исходники — github.com/ZlodeiBaal/Recognitor
          Ещё есть версия для ios и исходники для неё.
          В кратце — всё построено вокруг каскада Хаара — habrahabr.ru/company/recognitor/blog/223441/
          В версиях под мобильники каскад и применяется. Так же наш каскад с распознаванием номеров замержен в основную версию OpenCV, они проверили, что он работает…
          Подход не 100%, конечно. Но в плохих условиях съёмки с дрожащих рук мыльной камерой даёт 90% выделения всех номеров — eye-recognition.ru/platerec.html (из тех кадров, где номер есть в приличном для дальнейшей работе качестве).
          При хороших условиях, или если переобучить по более широкой базе 95% не проблемма.

          Так говорит только человек, никогда не сталкивавшийся ранее с поиском номеров на изображении.

          Может расскажете, где вы сталкивались?
        • 0
          Error_403_Forbidden, мой комментарий был с долей шутки, если честно. Значил он примерно следующее: «так говорит только человек, никогда ранее не сталкивавшийся с РАСПОЗНАВАНИЕМ номера на изображении. Какие у вас алгоритмы РАСПОЗНАВАНИЯ номеров в кадре? Днем, ночью, в дождь, снег, запачканные номера… Вы вот сейчас серьезно говорите, что распознавать номер — это легко? Не верю. ссылки в студию, как говорится». Ну а если серьезно, то никакие задачи в сложной постановке не просты.
    • 0
      Эх, если бы всё так просто…
      Но номер найти тривиально. Наш способ за счёт каскада Хаара один из самых стабильных, из тех, которые не используют специальную подсветку, кстати.
      • 0
        все-таки найти номер не всегда просто. в 90% случаях справляется простой детектор на каскаде Хаара. Но что делать тому десятому несчастному, который остановился перед шлагбаумом, а его номер не читается?.. )) Кроме того, тут еще ключевое слово «в потоке». Мы делали по одиночному кадру. В потоке вообще другие алгоритмы и другие к ним требования. кажется, что по видео надежнее — есть много кадров и движущаяся машина (а значит и попроще достичь тех же 90%).
        • 0
          да, я пробовал ваш каскад на видео с регистратора — я бы сказал, что он работает терпимо: на каких-то кадрах теряет номер впереди идущей машины, какие-то (вполне различимые номера) игнорирует практически полностью, довольно много ложных срабатываний на зданиях и вообще горизонтальных/вертикальных текстурах типа забора.
          Понимаю, что он создавался не для этих целей, так что для такого использования. я бы сказал, очень хороший результат!
          Ждем интересных обновлений! :)
          • +1
            Да, где-то в январе я попробую переобучить каскад, должен лучше срабатывать начать по таким ситуациям, я надеюсь. Ложняки за заборах почти точно уйдут. Мы в базу отрицательных примеров их уже порядочно набрали.
            • 0
              что ж, ждем-с! :)
  • 0
    Как то вы странно базу выложили, как по ней производить тестирование точности детектирования и распознавания?
    По идее надо было выкладывать первый архив(«база необрезанных фотографий автомобилей») и дополнительно к ней метаинформацию в виде координат описанных прямоугольников вокруг номеров для задачи детектирования и текстового описания что на номере изображено для задачи распознавания.
    • 0
      Для этого нужно 10000 картинок прописать номера и верефицировать. У нас на это сил и времени не было. Если хотите, можете сделать, перевыложим архив.
      У нас есть небольшая своя база, для которой есть написанные ручками номера. Мы упомянали её в тексте. Если кто захочет, мы можем по ней проверить любой алгоритм. Параметров выделения рамок в той базе нет, они на наш взгляд не нужны. Некоторые алгоритмы могут работать без явного выделения, со множественными гипотезами.

      База, которую мы выложили достаточна чтобы люди, которые будут этим заниматься проверили работоспособность алгоритма в разных условиях, чтобы им не пришлось бегать по улице с фотоаппаратом.
    • 0
      Да, наш каскад, кстати, отработал не на всех, а где-то на 80% картинок.
      В принципе каскад подключается в 2 строчки OpenCV, можете проверить.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка