Распознавание образов для программистов

ВНИМАНИЕ! БЛОГ ПЕРЕЕХАЛ ПО АДРЕСУ
RECOG.RU

28 Июнь 2010

Существующие на настоящий момент кодировщики Aztec code врут?

написано в рубрике: Распознавание 2D штрих-кодов — Кручинин Александр @ 1:38 ПП
К такому неоднозначному выводу я пришёл, анализируя ряд генерируемых кодов. Я конечно понимаю, что некоторые коды Aztec не используются, т.к. замещены стандартом Small Aztec Code, но почему такие ошибки понять не могу. Берём описанный ранее здесь http://blog.vidikon.com/?p=284 случай:

 

Рис. 1. 19на19 Aztec код с закодированным выражением «Test code» и расписанными 6 битными словами (не расписаны слова коррекции Рида-Соломона)

Рис. 1. 19на19 Aztec код с закодированным выражением «Test code» и расписанными 6 битными словами (не расписаны слова коррекции Рида-Соломона)

При декодировании оказывается, что там закодировано немного другое:

6 битное слово Код символа Значение
110101

000011

101011

010100

100001

100100

100000

001010

111101

0) – 21

1) – 28

2) – 6

3) – 20

4) – 21 5) – 1

6) – 4

7) – 16

8) – 10

9) – 18

‘T’

LL Mode

‘e’

’s’

‘t’  Пробел

‘с’

‘o’

‘i’

‘r’

 Кодируем тот же текст тем же кодировщиком, например (http://www.java4less.com/barcodes/AztecCode/php/demo.php):

Рис. 2. Код 27x27 с закодированным: Test code

Рис. 2. Код 27x27 с закодированным: Test code

При декодировании оказывается вот что:

 

8 битное слово Код символа Значение
11110101

10110000

01010010

00100001

00001001

10010100

11111101

0) – 21

1) – 28 2) – 6

3) – 20

4) – 21 5) – 1

6) – 4 7) – 16

8) – 5

9) – 6

‘T’

LL Mode   ‘e’

’s’

‘t’  Пробел

‘с’   ‘o’

‘d’

‘e’

 

Кодируется то правильно. Кто бы не мог указать на мою ошибку в рассуждениях? Или я всё-таки прав, и из-за молодости стандарта все используют неправильный кодировщик. Пусть хоть вместо малых кодов используют Small Aztec, но факт ошибки остаётся фактом.

23 Июнь 2010

30. Использование особенностей для распознавания образов

написано в рубрике: OpenCV — Кручинин Александр @ 8:32 ПП

Здесь http://blog.vidikon.com/?p=213 показано как использовать особенностей для нахождения объектов в изображении. Попробуем протестировать данный подход при работе с базами изображений для выяснения достоверности. Для тестирования можно использовать следующую базу образов http://staff.science.uva.nl/~aloi/ .

Поскольку OpenCV использует особенности для изображения в градациях серого, я скачал файл aloi_grey_red2_col.tar. Для тестирования была написана простенькая программа (Листинг 30.1)

Листинг 30.1

//Загружаем и обучаем

int i;

char buf[256];

CvSURFParams params = cvSURFParams(500, 1);

CvSeq *objectKeypoints[1000], *objectDescriptors [1000];

CvMemStorage* storage = cvCreateMemStorage(0);

for(i=0;i<500;i++)

{

sprintf(buf,”e:\\algor\\grey2\\%d\\%d_i110.png”,i+1,i+1);

IplImage* object=cvLoadImage(buf,CV_LOAD_IMAGE_GRAYSCALE);

IplImage* object_color = cvCreateImage(cvGetSize(object), 8, 3);

cvCvtColor( object, object_color, CV_GRAY2BGR );

objectKeypoints[i]=0;objectDescriptors[i]=0;

double tt = (double)cvGetTickCount();

cvExtractSURF( object, 0, &objectKeypoints[i], &objectDescriptors[i], storage, params );

printf(”%d Image Descriptors: %d\n”, i+1,objectDescriptors[i]->total);

tt = (double)cvGetTickCount() – tt;

printf( “Extraction time = %gms\n”, tt/(cvGetTickFrequency()*1000.));

cvReleaseImage(&object);

cvReleaseImage(&object_color);

}

LOG_FILE log;

log.BLog(”out1.log”);

//А теперь необходимо произвести сравнение

int j,k;

int all=0;

int all_p=0;

int pai_r[1000];

int maxpair;

int i1;

for(i=0;i<500;i++)

{

sprintf(buf,”Begin %d-s image:”,i+1);

log.Log(buf);

for(j=0;j<11;j++)

{

k=120+j*10;

if (j>=8) k=210+(j-8)*20;

sprintf(buf,”e:\\algor\\grey2\\%d\\%d_i%d.png”,i+1,i+1,k);

log.Log(buf);

CvSeq *objectKeypoints1, *objectDescriptors1;

IplImage* object=cvLoadImage(buf,CV_LOAD_IMAGE_GRAYSCALE);

IplImage* object_color = cvCreateImage(cvGetSize(object), 8, 3);

cvCvtColor( object, object_color, CV_GRAY2BGR );

objectKeypoints1=0;objectDescriptors1=0;

double tt = (double)cvGetTickCount();

cvExtractSURF( object, 0, &objectKeypoints1, &objectDescriptors1, storage, params );

printf(”%d Image Descriptors: %d\n”, i+1,objectDescriptors[i]->total);

tt = (double)cvGetTickCount() – tt;

printf( “Extraction time = %gms\n”, tt/(cvGetTickFrequency()*1000.));

cvReleaseImage(&object);

cvReleaseImage(&object_color);

log.Log(”Find object”);

//здесь собственно предстоит найти объект

maxpair=0;

for(i1=0;i1<500;i1++)

{

vector<int> ptpairs;

findPairs( objectKeypoints[i1], objectDescriptors[i1], objectKeypoints1, objectDescriptors1, ptpairs );

int n = ptpairs.size()/2;

pai_r[i1]=n;

if (i1>0 && n>pai_r[maxpair]) maxpair=i1;

}

sprintf(buf,”Object #%d”,maxpair+1);

log.Log(buf);

all++;

//if (pai_r[maxpair]<4) maxpair=-1;

if (maxpair==i) all_p++;

}

}

sprintf(buf,”All #%d”,all);

log.Log(buf);

sprintf(buf,”All D #%d”,all_p);

log.Log(buf);

В программе используются функции из http://blog.vidikon.com/?p=213. Картинок различного типа на самом деле тестовых 1000, но у меня после 500-ой при обучении висло, поэтому без рассуждений было принято решение  сокращении в два раза изображений. При обучении формировалось 500 эталонных образов с особенностями. При тестировании распознавалось 500*11=5500 изображений. Т.к. сравнивалось с каждым эталоном, то общее время распознавания составило 2.7 часа в одном потоке. Не очень быстро. Результаты тестирования таковы: 3775 изображений из 5500 было распознано правильно. Это 69% достоверности. Есть достаточно много причин такой низкой достоверности, о которых мы возможно поговорим в другой раз.

18 Июнь 2010

29. Движение курсором мышки при помощи пальца или фломастера перед камерой с неоднородным фоном

написано в рубрике: OpenCV — Кручинин Александр @ 8:08 ДП

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

На рисунке 29.1 представлен фон, на котором будем следить за пальцем.

 

Рис. 29.1 Фон

Рис. 29.1 Фон

 

Использовав принципы выделения отличных от фона объектов, описанных здесь [http://blog.vidikon.com/?p=208], будем находить объект. На рисунке 29.2 представлен объект, который должен детектироваться.

 

Рис. 29.2. Фон вместе с объектом детектирования

Рис. 29.2. Фон вместе с объектом детектирования

Для того, чтобы детектировать кончик объекта, использовался следующий подход.

1. Всё изображение разделялось на квадраты одинаковых размеров.

2. Внутри каждого квадрата вычислялось, сколько отличных от фона точек.

3. Если больше половины квадрата оказывалось заполненным, то считалось, что в квадрате есть «инородный» объект или его часть.

4. Сверху вниз перебирались все квадраты и, когда находился квадрат, удовлетворяющий условиям, представленным на рисунке 29.3, то считалось, что это и есть кончик объекта. 

Рис. 29.3. Условия определения кончика объекта (красные квадраты – заполненные , жёлтые – пустые)

Рис. 29.3. Условия определения кончика объекта (красные квадраты – заполненные , жёлтые – пустые)

 

Результат работы алгоритма представлен на рисунке 29.4. На кончике выделен толстый красный квадрат

 

Рис. 29.4. Результат выделения кончика

Рис. 29.4. Результат выделения кончика

 

Текст программы почти полностью сходен с [http://blog.vidikon.com/?p=208], за исключением того, что алгоритм отсечения отличных от фона объектов немного был улучшен за счёт включения при обработке фона не одной точки, а соседних. В листинге 29.1 представлена часть программы, отвечающая за выделения квадратов и кончика, которая должна находиться в цикле выделения отличных от фона объектов.

 

Листинг 29.1

ptr = (uchar*) (image1->imageData);

 

            //Считаем матрицу отличных от фона пикселей

            for(i=1;i<image[0]->height-1;i++)

                  for(j=1;j<image[0]->width-1;j++)

                  {

                        k=0;

                        for(j1=0;j1<3;j1++)

                        {

                             dd=0;

                             for(k1=0;k1<9;k1++)                                 

                                    dd+=ptr[(j-1+k1%3)*3+(i-1+k1/3)*image[0]->widthStep+j1];

                             dd/=9;

                             k+=abs(m[(i*image1->width+j)*3+j1]-dd);

                        }

                            

                        if (k<=porog)

                             mat[j+i*image1->width]=1;

                        else mat[j+i*image1->width]=0;

                  }

 

            //Обнуляем матрицу квадратов

            for(i=0;i<image[0]->height/step;i++)

                  for(j=0;j<image[0]->width/step;j++)

                        mat1[j+width*i]=0;

            //считаем заполненность квадратов

            for(i=1;i<image[0]->height-1;i++)

                  for(j=1;j<image[0]->width-1;j++)

                  {

                        if (mat[j+i*image1->width])

                        {

                             for(j1=0;j1<3;j1++){

                                   if (j1==0) ptr[j*3+i*image1->widthStep+j1]=255;

                                   else ptr[j*3+i*image1->widthStep+j1]=0;

                             }                           

                        }

                        else

                        {

                             k11=j/step;

                             k12=i/step;                       

                             mat1[k11+k12*width]++;                        

                        }

                  }

 

            //Рисуем квадраты

            for(i=0;i<image[0]->height/step;i++)

                  for(j=0;j<image[0]->width/step;j++)

                        if (mat1[j+width*i]>=step*step/2)

                        {

                             cv1.x=j*step;cv1.y=i*step;

                             cv2.x=(j+1)*step;cv2.y=(i+1)*step;

                             cvRectangle(image1,cv1,cv2,color,1,CV_AA,0);

                        }

            //Определяем зону кончика вверх

            k11=0;

            for(i=0;i<image[0]->height/step;i++){

                  for(j=0;j<image[0]->width/step;j++)

                        if (mat1[j+width*i]>=step*step/2 && mat1[j+width*(i+1)]>=step*step/2 && mat1[j+width*(i+2)]>=step*step/2 &&

                             mat1[j+width*(i-1)]<step*step/2 && mat1[j-1+width*(i-1)]<step*step/2 && mat1[j+1+width*(i-1)]<step*step/2 &&

                             mat1[j-2+width*(i)]<step*step/2 && mat1[j+2+width*(i-1)]<step*step/2)

                        {

                             //Нашли кончик

                             cv1.x=j*step;cv1.y=i*step;

                             cv2.x=(j+1)*step;cv2.y=(i+1)*step;

                             cvRectangle(image1,cv1,cv2,color,5,CV_AA,0);

                             k11=1;           

                             x=j*step+step/2;

                             y=i*step+step/2;

                             break;

                        }

                        if (k11==1) break;

            }

        cvShowImage( “Motion”, image1);

 

Далее можно постоянно вызывать функции котроля кончика или использовать технологию трекинга [http://blog.vidikon.com/?p=277] для слежения за точками. К сожалению данный подход хоть и прост, но не обеспечивает достаточное качество слежения для кончика объекта, чтобы его без модернизации можно было прямо сейчас взять и использовать на практике для движения курсором мыши.

7 Июнь 2010

Проблемы с декодированием Aztec кода

написано в рубрике: Распознавание 2D штрих-кодов — Кручинин Александр @ 5:24 ПП

Занимаясь распознаванием, ну и соответственно декодированием данного вида двухмерных кодов, я столкнулся с такой проблемой. С неправильным кодированием кодов Aztec всеми кодировщиками, это наблюдается и в Zint Barcode Studio 2.3.1, и вBarcode Studio 9.4, и даже в онлайн кодировщиках. Это касается кодирования полных Aztec кодов малых размеров. Например такого, как на рисунке 1.

Рис. 1. 19на19 Aztec код с закодированным выражением «Test code» и расписанными 6 битными словами (не расписаны слова коррекции Рида-Соломона)

Рис. 1. 19на19 Aztec код с закодированным выражением «Test code» и расписанными 6 битными словами (не расписаны слова коррекции Рида-Соломона)

При декодировании оказывается, что там закодировано немного другое:

6 битное слово Код символа Значение
110101

000011

101011

010100

100001

100100

100000

001010

111101

0) – 21

1) – 28

2) – 6

3) – 20

4) – 21 5) – 1

6) – 4

7) – 16

8) – 10

9) – 18

‘T’

LL Mode

‘e’

’s’

‘t’  Пробел

‘с’

‘o’

‘i’

‘r’

Все кодировщики используют один и тот же движок? Понятно, что вместо кодов такого размера используют Small Aztec Code, но всё же кодирование должно быть правильным.

При этом большие коды с 8битными словами декодируются нормально, например:

Рис. 2. Декодирование нормальное «int main() { AztecCode azt; return 0;} 0123456789 efghijklmnoprstuvwxyz»

Рис. 2. Декодирование нормальное «int main() { AztecCode azt; return 0;} 0123456789 efghijklmnoprstuvwxyz»

Рис. 3. Декодирование нормальное:

Рис. 3. Декодирование нормальное:

Нет ли у кого соображений по этому поводу?

Работает на WordPress