Распознавание автомобильных номеров

Распознавание автомобильных номеров
Сейчас, пока идет разработка и тестирование SDK для распознавания автомобильных номеров iANPR, решил поделиться с некоторыми предварительными результатами. Первоначальная версия SDK рассчитана на стандартные российские номера. Библиотека работает на базе OpenCV. Тестируется версия под Windows, но потом возможна поддержка и других операционных систем.

Интерфейс далеко не окончательный, но пока h-файл имеет следующий вид:

#pragma once

#include "opencv2/core/core_c.h"
#include "opencv2/imgproc/imgproc_c.h"

#define ANPR_DETECTMODE1                0x01
#define ANPR_DETECTMODE2                0x02
#define ANPR_DETECTMODE3                0x04

#define ANPR_RUSSIAN_BASE               0x00

struct ANPR_OPTIONS
{
        int min_plate_size; // минимальная площадь номера
        int max_plate_size; // максимальная площадь номера
        int Detect_Mode; // Режимы детектирования
        bool Border_Number; // Ограничение на распознавание касающихся краев номеров
        int max_text_size; // Максимальный размер символов номера
        int type_number; // тип номера
        int param; // дополнительный параметр для типа номера, если ANPR_RUSSIAN_BASE, то при param = 1 предполагается, что 1-ка в регионе
        int type_rect_detect; // пока не используется
        int type_text_recognition; // пока не используется
        int sort_cand_number; // пока не используется
};

int
#ifdef WIN32
__stdcall 
#endif 
anprPlate( IplImage* Image8, ANPR_OPTIONS Options, int* AllNumber, CvRect* Rects, char** Texts );


Как видно из файла, интерфейс библиотеки представлен в виде единственной функции. На вход передается изображение 8-битное 1-канальное в градациях серого, заполненная структура ANPR_OPTIONS с опциями. На выходе будут: количество распознанных номеров AllNumber, их местоположения Rects, распознанный номер *Texts. Сам тестовый пример представлен ниже:

#include "opencv2/highgui/highgui_c.h"
#include "..\..\src\iANPR.h"
#include <stdio.h>

int main(int argc, char** argv)
{       
        CvCapture* capture = cvCaptureFromFile(argv[1]); 
        char buffer[256];
        sprintf( buffer, "%s.avi", argv[1] );
        CvVideoWriter* cvVideoWriter = 0;               
        IplImage* object = 0;
        IplImage* image = cvCreateImage( cvSize( 960, 540 ), 8, 3 );
        int i = 0;
        char mem[100][20];
        int all_mem = 0;
        for(;;)
    {           
                IplImage* frame = 0;    
                frame = cvQueryFrame(capture );
                if( !frame )
                        break;
                if (!object)
                {
                        object = cvCreateImage(cvGetSize(frame), 8, 1);                 
                        cvZero( object );
                        object->origin = frame->origin;                         
                }
                cvCvtColor( frame, object, CV_BGR2GRAY );
                int all = 100;
                CvRect Rects[100];                              
                char** res = new char*[all];
                for(int j=0;j<all;j++) res[j] = new char[20];
                ANPR_OPTIONS a;
                a.Border_Number = 0;
                a.Detect_Mode = ANPR_DETECTMODE2 | ANPR_DETECTMODE3;
                a.min_plate_size = 500;
                a.max_plate_size = 25000;
                a.max_text_size = 20;
                a.sort_cand_number = 0;
                a.type_number = ANPR_RUSSIAN_BASE;
                a.type_rect_detect = 0;
                a.type_text_recognition = 0;
                a.param = 1;
                DWORD tick1 = GetTickCount();
                int i1 = anprPlate( object,  a, &all, Rects, res ); 
                DWORD tick2 = GetTickCount();
                printf( "Num:%d; time:%5.3f; cand:%d\n", i, (float) ( tick2 - tick1) / 1000, all );
                if ( i1 == 0 )
                {                                               
                        for(int j = 0; j < all; j++ )                           
                        {                               
                                if ( strlen( res[j] ) >= 8 )
                                {
                                        int k =0;
                                        for( int j1 = 0; j1 < all_mem; j1++ )
                                                if ( strcmp( res[j], mem[j1]) == 0 ) k =1;
                                        if ( k == 0 ) continue;
                                        cvRectangle( frame,cvPoint( Rects[j].x, Rects[j].y),cvPoint(Rects[j].x+Rects[j].width,
                                                Rects[j].y+Rects[j].height), CV_RGB(255,255,0), 2);
                                        if ( strlen( res[j] ) == 9 ) res[j][6] = '1';
                                        CvFont font;
                                        float aa=0.001f*frame->width;
                                        cvInitFont( &font, CV_FONT_HERSHEY_SIMPLEX, aa,
                                                                        aa,0,1, 8 ); 
                                        CvPoint pp2,pp1;
                                        pp2.x=Rects[j].x;
                                        pp2.y=Rects[j].y;
                                        pp1.x=Rects[j].x+1;
                                        pp1.y=Rects[j].y+1;
                                        cvPutText( frame, res[j], pp1, &font, CV_RGB(0,0,0) );
                                        cvPutText( frame, res[j], pp2, &font, CV_RGB(0,255,0) );
                                }                       
                        }
                        // Копирование в память
                        for(int j = 0; j < all; j++ )                           
                        {                               
                                if ( strlen( res[j] ) >= 8 )
                                {
                                        strcpy( mem[j], res[j] );
                                }
                        }
                        all_mem = all;
                }                               
                for(int j=0;j<100;j++) delete [] res[j];
                delete [] res;
                i++;
                cvResize( frame, image );
                cvShowImage( "frame", image);
                if (!cvVideoWriter)
                {
                                cvVideoWriter=cvCreateVideoWriter( buffer, CV_FOURCC('U', '2', '6', '3'), 30,
                                                cvGetSize( frame) );
                }       
                cvWriteFrame( cvVideoWriter, frame );

                int c = cvWaitKey( 20 );
                if ( c== 27 ) break;
        }

        cvReleaseCapture( &capture );
        cvReleaseVideoWriter( &cvVideoWriter );

        return 0;
}


Пример работает с видеофайлами, которые были сформированы регистратором с Full HD качеством. С помощью cvCvtColor получается нужный формат изображения. Затем выделяется память для хранения областей CvRect нахождения номера и информации о распознанном номере. В примере на 100 элементов, но на практике было бы достаточно и 10 номеров в одном кадре.
Затем заполняется структура ANPR_OPTIONS.

ANPR_OPTIONS a;
a.Border_Number = 0;
a.Detect_Mode = ANPR_DETECTMODE2 | ANPR_DETECTMODE3;
a.min_plate_size = 500;
a.max_plate_size = 25000;
a.max_text_size = 20;
a.sort_cand_number = 0;
a.type_number = ANPR_RUSSIAN_BASE;
a.type_rect_detect = 0;
a.type_text_recognition = 0;
a.param = 1;


Border_Number = 0; говорит о том, что будут фиксироваться все номера, даже те, которые с краю.
В a.Detect_Mode выбирается два типа детектирования номеров (можно включать и все 3). Пока типов детектирования 3, но они могут поменяться.
a.min_plate_size – минимальная площадь номера.
a.max_plate_size – максимальная площадь номера.
a.max_text_size – размер буфера для каждого номера.
a.type_number – тип распознаваемых номеров, пока он один — ANPR_RUSSIAN_BASE.

Если распознавание происходит успешно (if ( i1 == 0 )), то номера выводятся на изображение чуть выше места распознавания. Ну а далее – действия и функции обычные для программ на основе OpenCV. Ниже представлен небольшой ролик в Full HD качестве, иллюстрирующий работу SDK.

Оригинал статьи здесь
  • +1
  • avatar
  • Поделиться

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

+1
Как я понимаю, в моём фотоаппарате работает OpenCV. Обнаруживает лица, улыбки и моргание.
Я дурил программу с помощью коленки, которая распознавалась как лицо.

Идея гиперплоскости наверно хорошая, но мне кажется, сначала мозг, как конкурент OpenCV, распознаёт улицу (асфальт?), затем распознаёт автомобили, затем места, где вешают номера: перед и зад авто, а уже после этих распознаваний будет меньший процент ошибок для номеров.
0
Программа довольно интересная, но пойдет ли — неизвестно. Нужно учесть множество технических нюансов — что, если к примеру, номер побиты или заляпаны грязью, распознает ли их программа? Работать, конечно, над ней еще немало, знать бы что не напрасно, а то, как обычно, на самом интересном месте сворачивают финансирование и адью.
0
Распознавать номер вообще не обязательно! Достаточно фотку номера делать, а если надо будет — человек сам посторается распознать.
А вот знаки бы различать — вот это было бы классно!!!
А то иногда не успеваешь или отвлекаешься. Вот очень надо бы!
Или хорошо бы разработать для каждого знака, перекрестка или вообще события всякие радиомаячки и программой выводить на дисплей.
Давайте разработаем!
0
Номер нужно распознавать, для того что бы вычислить расстояние до впереди идущего автомобиля
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.