Распознавание номеров: от А до 9. Часть 3

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


    (часть фотографий, присланных в течение недели)


    Хочется рассказать еще и о том, как мы — программисты, ворочающие нос от интернет технологий и Linux, — решали проблему с сервером.
    Все мысли по поводу настоящего шумного компьютера под ухом, протягивание кабеля на кухню и переговоров с провайдером про реальный IP, были отброшены, как не соответствующие новым реалиям (со всех сторон только и говорят про облачные сервисы и прочие новинки). Но еще хотелось удобства, привычного Windows, dotNET, да и вообще возможности по-живому отлаживаться на сервере. Посему было решено: виртуальный сервер с Windows Server и удаленный рабочий стол.
    Хочу передать огромное спасибо терпеливым и вежливым парням в техподдержке! Так что справились.


    Да-да, вот так все просто выглядит. Это принтскрин с удаленного доступа к виртуальному серверу (да не сочтите это рекламой Windows Server 2012 R2).

    Затем надо было написать http ответчик. Хотелось как можно проще и не связываться с IIS, нужно было уложиться в пару дней на разработку. Но оказалось очень просто скачать пример SimpleHttpServer и в функцию:
    public override void handlePOSTRequest(HttpProcessor p, StreamReader inputData) {
            Console.WriteLine("POST request: {0}", p.http_url);
            string data = inputData.ReadToEnd();
            
            p.outputStream.WriteLine("<html><body><h1>test server</h1>");
            p.outputStream.WriteLine("<a href=/test>return</a><p>");
            p.outputStream.WriteLine("postbody: <pre>{0}</pre>", data);
        }

    вписывать нужную обработку. Надеюсь, мы не нарушили никакой лицензии.
    А тем специалистам Web безопасности, у которых сейчас на спине зашевелились волосы от такой реализации… огромный привет и приглашение сделать нам все по умному!

    Доступ к серверу


    Сервер распознавания работает, как очень простой http сайт. Пользователь отправляет на страницу post-сообщение в формате http, в котором содержится лишь один параметр — изображение. В ответ получает результат распознавания.
    Для запроса из БД, если в этом есть необходимость, нужно отправить 2 строки: автомобильный номер в текстовом виде и уникальный ID.
    В Android программе было 3 запроса, их код выглядит следующим образом:

    1) отправка предварительно выделенного номера серверу:
    HttpClient httpclient = new DefaultHttpClient();
    final HttpParams httpParameters = httpclient.getParams();
    HttpConnectionParams.setConnectionTimeout(httpParameters, 10 * 1000);
    HttpConnectionParams.setSoTimeout        (httpParameters, 10 * 1000);
    //Создаём Http запрос и прилагаем к нему файл изображения
    HttpPost httppost = new HttpPost("http://212.116.121.70/:10000/result");
    InputStreamEntity reqEntity;
    httppost.setEntity(new FileEntity(new File(FileName), "application/octet-stream"));
    //Получаем ответ от сервера
        try {
    		HttpResponse response = httpclient.execute(httppost);
    		HttpEntity responseEntity = response.getEntity();
    		ans = EntityUtils.toString(responseEntity);
    		String[] strs=ans.split("\r\n");
    		if(strs.length>2)
    		{
    	         	ans=strs[0]; //Получаемый от сервера распознанный номер
            	        timesWas=Integer.parseInt(strs[1]); //Сколько раз он встречался в базе
                    	ID=strs[2]; //Унакальный ID текущей операции
    		}
    	} catch (ClientProtocolException e) {
    			e.printStackTrace();
    			ans = "NOT CONNECT";
    	} catch (IOException e) {
    	        e.printStackTrace();
    		ans = "NOT CONNECT";
    	}
    


    2) отправка запроса по номеру:
    HttpClient httpclient = new DefaultHttpClient();
    final HttpParams httpParameters = httpclient.getParams();
    HttpConnectionParams.setConnectionTimeout(httpParameters, 10 * 1000);
    HttpConnectionParams.setSoTimeout        (httpParameters, 10 * 1000);      
    HttpPost httppost = new HttpPost("http://212.116.121.70:10000/checkplate");
    InputStreamEntity reqEntity; 
     try {
    	 httppost.setEntity(new StringEntity( editText1.getText().toString()+"\r\n"+ID));    
    	 HttpResponse resp = httpclient.execute(httppost);
    	 HttpEntity ent = resp.getEntity();
    	String ans = EntityUtils.toString(ent);
    	timesWas=Integer.parseInt(ans);
    	textView.setText("Уже обозвали раз: "+Integer.toString(timesWas));
            
    	} catch (ClientProtocolException e) {
    		e.printStackTrace();
    	} catch (IOException e) {
    		e.printStackTrace();
    	}catch(Exception e)
    	{
    		e.printStackTrace();
    	}
    


    3) «ругань» на номер:
    HttpClient httpclient = new DefaultHttpClient();
    final HttpParams httpParameters = httpclient.getParams();
    HttpConnectionParams.setConnectionTimeout(httpParameters, 10 * 1000);
    HttpConnectionParams.setSoTimeout        (httpParameters, 10 * 1000);
    HttpPost httppost = new HttpPost("http://212.116.121.70:10000/swear");
    InputStreamEntity reqEntity;   
             try {
            	 httppost.setEntity(new StringEntity( editText1.getText().toString()));    
            	 HttpResponse resp = httpclient.execute(httppost);
            	 HttpEntity ent = resp.getEntity();
            	String ans = EntityUtils.toString(ent);
    		textView.setText("Обозван");
                } catch (ClientProtocolException e) {
    		e.printStackTrace();			
                } catch (IOException e) {
    		e.printStackTrace();	
    	    }catch(Exception e)
    			{
    				e.printStackTrace();
    			}
    


    По-моему, комментировать тут особенно нечего. HttpPost файла и HttpPost двух текстовых строк.

    Не забывайте, что в условиях использования мобильного интернета, приходится отправлять область с предварительно обнаруженным номером с помощью каскадного детектора Хаара.
    Пример кода выделения Хааром с помощью OpenCV на Android Java:

    //Детектирование каскадом Хаара номера
    if (mJavaDetector != null)
    mJavaDetector.detectMultiScale(temp, faces, 1.1, 10, 5, 
    	                		 new Size(70, 21), new Size(500,150));
    //Если нашлось	
    Rect[] facesArray = faces.toArray();
    for (int i = 0; i < facesArray.length; i++)
    {
     	DetectedNum = new Mat();
    	IsNumDetected=true;
    
    	//Новая рамка с чуть большими границами
    
    	int dW=facesArra[i].width/5; // расширяем рамку по X на 20%
    	int dH=facesArray[i].height*3/10; //по Y на 30%
    
      	int left = Math.max(facesArray[i].x-dW/2,0);
           	int top = Math.max(facesArray[i].y-dH/2,0);
    	int right = facesArray[i].x+facesArray[i].width+dW/2; if(right>temp.width())right=temp.width()-1;
            int bottom = facesArray[i].y+facesArray[i].height+dH/2; if(bottom>temp.height())bottom=temp.height()-1;
    
    	//Отправка на сервер данного куска
    	DetectedNum = temp.submat(BiggerRect).clone();  
    }
    


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

    И по просьбе трудящихся добавили http заход на функцию поиска и распознавания номера в целом кадре: 212.116.121.70:10000/uploadimage
    В ответ получите список найденных номеров и некий критерий качества распознавания по каждому (больше — лучше):
    x000xx99 90%
    a111aa197 75%
    строки разделены "\r\n"
    Найдено 2 номера, первый более качественный (90%), второй менее (75%).

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

    На других платформах код должен получаться не намного сложнее.

    Несколько слов о трех днях полета сервера распознавания номеров


    Программу на Android для ругани на автомобили Recognitor мы выложили 13 мая. У меня чувства смешанные: от гордости от того, что оно работает, до сжигающего стыда за случающиеся ошибки в алгоритме распознавания, когда прямо на глазах приходит чистый четкий номер, но пользователю возвращается абракадабра.

    Количество отправленных на сервер изображений: 1700
    Из них оказалось номерами РФ: 1370
    Количество распознанных: 830
    (с точностью до 10ти указано)

    Вот тут стоит отдельно пояснить «из них оказалось номерами РФ». Мы не учли, что хабр хорошо читают на территории СНГ и нигде не указали, что номера должны быть РФ. Естественно, сюда же относятся и ошибки не идеально обученного каскадного детектора, который часто ошибался в непривычной ситуации съемки с монитора. И было несколько десятков зеркально отраженных номеров, т. е. пользователь не выбрал в меню “Flip”. Также ну очень сильно размазанные (не читаемые глазами) я тоже отнес сюда.
    В промежуточном итоге результат не фантастический, мы сделали выводы, уже выпустили 2 обновления Android программы, поправив косяки и дав пользователю новую волшебную функцию выделения области номера пальцем. Изменили алгоритмы на сервере. О том, что интересного мы поменяли в самих алгоритмах, в моей следующей статье (воспользовались парой альтернативных методов из предыдущей моей статьи).
    Но, не смотря на не идеальную работу, пользователям приложение пришлось по душе! Оценки в GooglePlay радовали.

    И да, конечно, поощрим бесспорных победителей:
    P494KE_197 — обозван 226 раз (конечно, это ZlodeiBaal)
    X777XX_77 – обозван 21 раз (в топе запроса яндекса на запрос «номера»)
    Даже поймали A362MP_97, А231МР_97 и А869МР_97 (возможно, тоже из интернета).

    Удачи

    Вообще, алгоритм обучался на очень грязных зимних номерах (и парадоксально не всегда устойчиво работает на чистых), поэтому тут то его преимущества и стоит поискать. И да, действительно, часто размытые и весьма грязные номера удавалось распознать:




    Ссылки:
    Часть 1
    Часть 2
    Обновленные исходники Android-проекта

    Update:
    1) Оказывается, обученный каскад нами на российские автомобильные номера был замержин в основную версию OpenCV
    2) при предварительном выделении номера ожидали картинки довольно больших размеров в uploadimage, сейчас поправил, все приводится к одному масштабу. Должно заработать и на мелких картинках из интернета.
    Recognitor 36,15
    Компания
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Похожие публикации
    Комментарии 28
    • +1
      Отличный материал! Спасибо за статью!
      Про алгоритмы бы еще, как в предыдущих постах.
      • +2
        Скорее всего следующий Васин пост будет про алгоритмы:)
      • +1
        Ну можно же вместо HTTP сервера на коленке использовать WCF-сервис c WebHttpBinding (и пускать всё в self hosted режиме). Раз уж решили .NET использовать.
        • +20
          Что-то вдруг вспомнилось

          image
          • +2
            Следующим по традиции идет комикс про маленького Дроппи Тейбл с XKCD
            • 0
              Авторы описанного инструмента про этот баг помнят, таблицу после аплоада не дропнуло. Выдало
              O701AH 45 67% T066MM 02 62%
              • +1
                67 и 62% — это почти наверняка не номера, но хаар нашел две похожих на номер нарезки, пришлось пытаться определить знаки. ну и видно, что мы ждем номер только в формате буква-цифра-цифра-цифра-буква-цифра-цифра.
            • 0
              А почему бы не выложить алгоритм работы распознавания? Или это не Ваш алгоритм? Зачем городить огород с сервером распознавания или в дальнейшем это будет коммерческий проект?
              • 0
                Был интересный нюанс насчёт преобразования Хафа, но в первом посте на него (пока?) не ответили… Насчёт сервера: судя по всему, не опенсорс :(
                • +1
                  В следующей статье опишу алгоритмы которые использовались. Открывать полностью/частично/не открывать — решаем прямо сейчас. Открыть все — казалось бы хорошо для других. Но все-равно без отладки и поддержки это бессмысленно с коммерческой точки зрения. Прикладник лучше свои подходы реализует, чем будет адаптировать чужое.

                  Для всех некоммерческих, кто к нам обращается, мы отдаем доступ свободный. Адаптировать на мобильное устройство не выйдет — там другие алгоритмы нужны. Но и с таким вопросом никто не обращался пока.

                  А главное, что беспокоит в такой ситуации: эти исходники всплывут в десятках курсовых (если не дипломных) работ. А это очень печально на самом деле, когда студент просто тащит что-то готовое, не напрягаясь. Причем совершенно не надуманная история — делали обзор по биометрии на сайте когда-то, то его посещаемость в течении двух лет активировалась до сотен в день только перед сессией.

                  а что с хафом не ответили? Фактически хаф используется для поиска нижней и боковых границ, верхняя часто размыта и вообще является частью багажника, т.к. снимают номер сверху.
                  • +2
                    п.с. не правильно утверждать, что посещаемость сайта свидетельствует про то, что его активно ctrl-c-ctrl-v, но в большинстве случаев открытые такие «точечные» алгоритмы распознавания изображений всплывают в студенческих работах. Но был бы рад ошибаться.
                    • 0
                      Для всех некоммерческих, кто к нам обращается, мы отдаем доступ свободный. Адаптировать на мобильное устройство не выйдет — там другие алгоритмы нужны. Но и с таким вопросом никто не обращался пока.


                      Я к вам обращался еще 6 мая, писал сообщение с идеей разработки свободной и бесплатной кроссплатформенно библиотеки распознавания (вообще это даже не идея, я пишу такую библиотеку, пока дело продвигается туго, в плане как раз алгоритмов распознавания номеров). Вы сказали, что исходники алгоритма закрыты и заказчик не планирует их открыть. Сейчас пишите, что для некоммерческих даете доступ, нестыковка получается.
                      • +1
                        прошу прощения, не точно сформулировал. «Даем доступ к серверу», ну т.е. пользуйтесь пожалуйста, мы учимся лучше работать, вам сервис распознавания. Эта статья как раз про доступ к серверу.

                        «Открывать полностью/частично/не открывать — решаем прямо сейчас». Вы понимаете, что если я Вам писал в личном сообщении, что существует заказчик именно первых исходников, то под этой формулировкой «решаем прямо сейчас» может скрываться ряд мероприятий (даже если все писать с нуля, то согласовывать в каком-то виде с заказчиком надо), которые не всегда хочется публично на весь хабр обсуждать.
                        • +1
                          кстати, а чем OpenCV не такая кроссплатформенная библиотека распознавания? Они на многих платформах присутствуют. Качество распознавания не всегда лучшее? Ну так это очевидно проблема всех библиотек общего назначения. Все всегда зависит от условий съемки и предмета съемки. И когда встает задача прикладную систему сделать под конкретные весьма задачи, то библиотеку распознавания приходится откладывать и смотреть, а чего тут собственно специфичного.
                          • 0
                            OpenCV предоставляет готовые алгоритмя по обработке изображения, но не дает готовых алгоритмов распознавания дорожных знаков, номеров автомобилей, лиц, оптич. распознавание текста методом контурного анализа и т.д.
                            Библиотека которую я пишу представляет набор процедур и функций, на вход которым дается изображение + набор параметров, а на выходе готовый результат. Конечно в своей библиотеки я использую массу сторонних, включая opencv.
                            Разработчику не нужно придумавать сам алгоритм, он уже есть в библиотеке. Вот в этом и отличие.
                            Например сейчас есть готовый набор простых процедур и функций для OCR, оптич. распознавания текста с использованием Tesseract-OCR, то есть на входе указываю карнитку (jpg, gif, tiff, png) + язык распознавания, на выходе получаем готовый распознанный текст, если нужны доп. продвинутые функции, например массив из позиций всех слов в тексте, или строк или отдельных букв, то пожалуйста, есть и такие фунции. Так же я написал VCL компонент для Delphi и Lazarus для использования функций OCR моей библиотеки.
                        • 0
                          Здравствуйте. Мне бы очень хотелось узнать (отнюдь не для курсовой) об алгоритме, основанного на поиске градиента в пространтстве Хафа (или о вашем понимании, что он из себя представляет). Для себя. Можно ли это считать «некоммерческой» целью, не знаю, но думаю, да. В комментарии я привел ссылки о результатх поиска, но имхо это все не то. Поэтому хотелось бы, чтобы вы это рассказали, если можете.
                      • +3
                        Огород с сервером нужно городить:

                        логика подсказывает, что еще довольно долго искуственный мозг в кармане за 100у.е. никому не носить, а значит архитектура с сервером потенциально выигрышна для любых алгоритмов, которые не работают full-time. Наше приложение скачали >1000 раз и позапускали его, мучают сервер. Но он даже и на 10% времени не занят. Просто вопрос эффективности.

                        По-этому коммерческое направление тут может быть — это КПП с простенькими IP камерами, вся аналитика на спец. сервере.

                        И еще нам было очень важно собрать принимаемые номера в одном месте на сервере, у нас. Т.к. нам же и оптимизировать развивать алгоритм. Просто сделать какое-то распознавание и не улучшаться его… зачем? А как еще наладить логистику получения номеров у нас на сервер, кроме как сделать этот сервер обязательным для распознавания? Попросить всех отправлять нам номера? ))) ну порядка на 2 у нас бы меньше приходило.
                        • 0
                          Обновите, пожалуйста, в будущем каскад, полученный на новых, собранных от пользователей фотографиях.
                          • +1
                            Делаем большую работу над ошибками. К сожалению, не так быстро и просто все получается. Конечно, мы все обучим и выложим!
                      • 0
                        Это объяснялось подробно в прошлой статье, а вам, помниться, Вася ещё и отвечал на эту тему. Проект — частично наш. Полностью коды опубликовать мы пока права не имеем. Но сделать всё чтобы любой октрытый проект пользовался «облаком» распознавания — можем. И, поверьте, это нужно многим. После моей прошлой статьи к нам обратилось несколько владельцев сайтов, позволяющих отправлять заявления в ГИБДД и.т.д. которые хотят к себе это подключить.
                        Почему? Потому что у нас есть неиспользуемая библиотека и мы можем это сделать. Как мне кажется если можно принести пользу- её нужно принести. Даже если открыто в OpenSource не всё.
                      • 0
                        На SGSIII с CyanogenMod приложение play.google.com/store/apps/details?id=org.opencv.samples.tutorial334565 ничего не показывает, черный экран, потом вылетает с ошибкой.
                        • 0
                          Часто вижу в автомобилях два устройства, которые бы неплохо могли совместно помочь водителям: регистратор (читай — камера), и мультимедийная голова, не родная для автомобиля — из тех, что и интернет «умеют», и навигаторы ставь какие хочешь. Такие девайсы обычно имеют либо WinCE, либо Android в качестве ОСи, и на них можно запустить свой софт.

                          Так вот, случись камере в процессе движения видеть дорогу, и передавать картинку в «голову» с нужным софтом (картинка — это автомобили на полосах, сами полосы и знаки вокруг), то, при должном распознавании, можно и запоминать номера тех авто, что ехали рядом в потоке (вплоть до того, что на случай аварии держать список возможных свидетелей), и принимать решение, какие знаки и разметка сейчас действует и водителю подсказывать режим движения (более актуально не в пробке, а на полупустой дороге), и даже упреждать слишком сильное приближение к впереди находящему автомобилю.

                          Мечты-мечты, точнее, идеи-идеи, но было бы здорово, если бы такое реализовали. В некоторых машинах распознавание знаков и полос уже есть (Opel Mokka с системой «Фронтальная камера с системой распознавания дорожных знаков» — там камера прямо глазом заметна, нефиговая такая штука приклеена к лобовому стеклу), но, подозреваю, точность распознавания в российских условиях там… не 100%-я, наверняка )

                          А вот распознавать ли знаки на сервере, или локально — вопрос интересный. Ведь знаки меняются редко, центральная база всех знаков, проверенных людьми, была бы в тему, наверное? Зато эта база лимитов скорости и прочих указаний водителям была бы куда актуальнее тех, что идут с картами в навигаторах.
                          • +1
                            какие знаки и разметка сейчас действует и водителю подсказывать режим движения

                            RoadAr умеет.
                            Теперь надо JOIN-ить команды разработчиков.
                            • 0
                              Супер, спасибо за наводку! Кажется, единственный аргумент повестить Android в роли регистратора :)

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

                              Так что было бы супер иметь аппаратное решение, а лучше подружить бы мелкую камеру, «голову» (иные «головы» могут и регистраторами работать, если в них камеру воткнуть), и толковый софт — милое бы дело было!

                              Альтернатива, так сказать, Opel Eye и иже с ним (хотя ни одного более подобного решения от производителя авто не знаю).
                            • 0
                              Да, по поводу распознавания из видео на лету нас уже активно спрашивали:)
                              В принципе это очень перспективное применение. Но его нужно поддерживать какой-нибудь крупной фирме или большому коллективу энтузиастов с огромной пачкой серверов. Этим направлением очень интересуются люди, связанные с направлением автоматически управляемых автомобилей. Тем очень полезна была бы функция «знать кто перед тобой едет».

                              Распознавание знаков в чём то проще, в чём то сложнее. Знаки достаточно универсальны и обычно грязью не замазаны. Но там нужно достаточно мощную логику строить. Кстати, мне перспективнее кажется, когда распознаётся набор знаков тем же Яндыксом, а потом Яндыкс.Навигатор говорит знаки, которые расположены в текущей области. Но там, конечно, требуется ручная работа для поддержания знаковой системы.
                            • 0
                              del
                              • 0
                                Сервер больше не работает?

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

                                Самое читаемое