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

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

25 Май 2010

28. Слежение за точками

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

Пример организации слежения за точками представлен в файле lkdemo.c в разделе samples (OpenCV). Попробовав запустить пример, вы увидите изображение с предварительно подключённой вами камеры. Нажмите мышкой, к примеру, на вашем носу – появится точка, которая будет отслеживаться компьютером при вашем движении лицом. Программой используется алгоритм Lucas-Kanade. В листинге 28.1 приведён текст примера с комментариями на русском языке.

Листинг 28.1

ifdef _CH_

#pragma package <opencv>

#endif

 

#define CV_NO_BACKWARD_COMPATIBILITY

 

#ifndef _EiC

#include “cv.h”

#include “highgui.h”

#include <stdio.h>

#include <ctype.h>

#endif

 

IplImage *image = 0, *grey = 0, *prev_grey = 0, *pyramid = 0, *prev_pyramid = 0, *swap_temp;

 

int win_size = 10;

const int MAX_COUNT = 500;

CvPoint2D32f* points[2] = {0,0}, *swap_points;

char* status = 0;

int count = 0;

int need_to_init = 0;

int night_mode = 0;

int flags = 0;

int add_remove_pt = 0;

CvPoint pt;

 

//обработка событий с мыши

void on_mouse( int event, int x, int y, int flags, void* param )

{

    if( !image )

        return;

 

    if( image->origin )

        y = image->height – y;

 

    if( event == CV_EVENT_LBUTTONDOWN )

    {

            //Если нажали левую кнопку мыши, то добавляем отслеживаемую точку

        pt = cvPoint(x,y);

        add_remove_pt = 1;

    }

}

 

 

int main( int argc, char** argv )

{

    CvCapture* capture = 0;

 

      //Получаем видеопоток с камеры или видеофайла, в зависимости от входных параметров

    if( argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0])))

        capture = cvCaptureFromCAM( argc == 2 ? argv[1][0] – ‘0′ : 0 );

    else if( argc == 2 )

        capture = cvCaptureFromAVI( argv[1] );

 

    if( !capture )

    {

        fprintf(stderr,”Could not initialize capturing…\n”);

        return -1;

    }

   

      //Печатается приветствие и краткий хелп по программе

    printf (”Welcome to lkdemo, using OpenCV version %s (%d.%d.%d)\n”,

          CV_VERSION,

          CV_MAJOR_VERSION, CV_MINOR_VERSION, CV_SUBMINOR_VERSION);

 

    printf( “Hot keys: \n”

            “\tESC – quit the program\n”

            “\tr – auto-initialize tracking\n”

            “\tc – delete all the points\n”

            “\tn – switch the \”night\” mode on/off\n”

            “To add/remove a feature point click it\n” );

 

    cvNamedWindow( “LkDemo”, 0 );

      //Устанавливаем функцию обработчик мыши, сама функция находится выше

    cvSetMouseCallback( “LkDemo”, on_mouse, 0 );

 

      //Главный рабочий цикл программы – работает бесконечно, пока вы принудительно его не закроете, например нажав ESCAPE

    for(;;)

    {

        IplImage* frame = 0;

        int i, k, c;

 

            //Получаем фрейм из видеопотока

        frame = cvQueryFrame( capture );

        if( !frame )

            break;

 

        if( !image )

        {

            /*Если изображение image не создано, то надо распределить все буферы. Изображение не создано первоначально,

                  поэтому это всего навсего инициализация.

                  */

            image = cvCreateImage( cvGetSize(frame), 8, 3 );

            image->origin = frame->origin;

            grey = cvCreateImage( cvGetSize(frame), 8, 1 );

            prev_grey = cvCreateImage( cvGetSize(frame), 8, 1 );

            pyramid = cvCreateImage( cvGetSize(frame), 8, 1 );

            prev_pyramid = cvCreateImage( cvGetSize(frame), 8, 1 );

                  //Выделяем массивы под точки, судя по MAX_COUNT максимально у нас может быть 500 точек

            points[0] = (CvPoint2D32f*)cvAlloc(MAX_COUNT*sizeof(points[0][0]));

            points[1] = (CvPoint2D32f*)cvAlloc(MAX_COUNT*sizeof(points[0][0]));

            status = (char*)cvAlloc(MAX_COUNT);

            flags = 0;

        }

 

            //копирование и перевод в чёрно-белое изображение

        cvCopy( frame, image, 0 );

        cvCvtColor( image, grey, CV_BGR2GRAY );

 

            //Это для эффекта, когда вы нажимаете n изображение становится тёмным и вы видите только перемещающиеся

            //назначенные вами точки

        if( night_mode )

            cvZero( image );

 

        if( need_to_init )

        {

            //Это нужно для автоматической инициализации наблюдения – если вам лень тыкать мышкой, то можно

                  //запустить автоматическое сканирование изображения на факт выделения подходящих точек

            IplImage* eig = cvCreateImage( cvGetSize(grey), 32, 1 );

            IplImage* temp = cvCreateImage( cvGetSize(grey), 32, 1 );

            double quality = 0.01;

            double min_distance = 10;

 

            count = MAX_COUNT;

                  //определяет наиболее “сильные” углы изображения

            cvGoodFeaturesToTrack( grey, eig, temp, points[1], &count,

                                   quality, min_distance, 0, 3, 0, 0.04 );

                  //уточняет местоположение углов

            cvFindCornerSubPix( grey, points[1], count,

                cvSize(win_size,win_size), cvSize(-1,-1),

                cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));

                  //удаляет ненужные изображения

            cvReleaseImage( &eig );

            cvReleaseImage( &temp );

 

            add_remove_pt = 0;

        }

        else if( count > 0 )

        {

                  //Если есть хотя бы одна точка на экране, то за ней надо следить

 

                  //Вычисляем оптический поток на факт поиска особенностей

                  //points[0] – предыдущие точки points[1] – текущие точки

            cvCalcOpticalFlowPyrLK( prev_grey, grey, prev_pyramid, pyramid,

                points[0], points[1], count, cvSize(win_size,win_size), 3, status, 0,

                cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03), flags );

            flags |= CV_LKFLOW_PYR_A_READY;

                  //перебираем все точки

            for( i = k = 0; i < count; i++ )

            {

                        //Если вы добавили новую точку нажатием мыши и она слишком близка к предыдущей – просто удаляем её

                if( add_remove_pt )

                {

                    double dx = pt.x – points[1][i].x;

                    double dy = pt.y – points[1][i].y;

 

                    if( dx*dx + dy*dy <= 25 )

                    {

                        add_remove_pt = 0;

                        continue;

                    }

                }

 

                if( !status[i] )

                    continue;

 

                points[1][k++] = points[1][i];

                        //Отображаем точку на экране

                cvCircle( image, cvPointFrom32f(points[1][i]), 3, CV_RGB(0,255,0), -1, 8,0);

            }

            count = k;

        }

 

        if( add_remove_pt && count < MAX_COUNT )

        {

                  //Если точка прошла процедуру проверки (см. выше), то добавляем её в буфер

            points[1][count++] = cvPointTo32f(pt);

                  //уточняет местоположение углов для всех точек

            cvFindCornerSubPix( grey, points[1] + count – 1, 1,

                cvSize(win_size,win_size), cvSize(-1,-1),

                cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));

            add_remove_pt = 0;

        }

 

        CV_SWAP( prev_grey, grey, swap_temp );

        CV_SWAP( prev_pyramid, pyramid, swap_temp );

            //Меняем местами точки – новые становятся старыми на следующем шаге цикла

        CV_SWAP( points[0], points[1], swap_points );

        need_to_init = 0;

        cvShowImage( “LkDemo”, image );

 

        c = cvWaitKey(10);

        if( (char)c == 27 )

            break;

        switch( (char) c )

        {

        case ‘r’:

            need_to_init = 1;

            break;

        case ‘c’:

            count = 0;

            break;

        case ‘n’:

            night_mode ^= 1;

            break;

        default:

            ;

        }

    }

 

    cvReleaseCapture( &capture );

    cvDestroyWindow(”LkDemo”);

 

    return 0;

}

 

#ifdef _EiC

main(1,”lkdemo.c”);

#endif

 

Итак, запускаем пример и ставим на ручке точку (Рис. 28.1)

Рис. 28.1

Рис. 28.1

Следим за точкой, перемещая ручку (Рис. 28.2).

Рис. 28.2

Рис. 28.2

 

Для пущего эффекта, нажимаем ‘n’ и смотрим за перемещением точки на чёрном экране (Рис. 28.3).

 

Рис. 28.3

Рис. 28.3

 

Подробнее о параметрах функций для слежения поговорим в другой раз.

18 Май 2010

Как на изображении найти пунктирную линию

написано в рубрике: Распознавание образов — Кручинин Александр @ 4:22 ПП

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

1 этап. Синхронизирующая пунктирная линия обычно не бывает толщиной в один пиксель, поэтому надо найти точку, через которую проходит эта линия. Это можно сделать разными способами: а) перебрать большинство точек изображения, вычисляя для каждой линию; б) перебрать только пиксели выделенных контуров; в) используя какой-либо другой способ (например, когда вы распознайте 2D штрих-код, то вы уже примерно представляете, где находится эта пунктирная линия и необходимо только уточнить её направление).

2 этап. Задаём диапазон углов, в которых может находиться пунктирная линия. Если вам категорически ничего не известно, то задавайте 360 градусов, т.е. 2*PI. Задаём количество шагов для определения линий в диапазоне углов, естественно оно должно быть умеренным – т.к. тут в силу вступает компромисс – качество и производительность. Задаём длину линии – она не может быть у нас бесконечной. Вы должны задать размер, который обязательно меньше реальной длины линии.

3 этап. Согласно диапазону углов и шагов в цикле перебираем все возможные линии с центром линии в задаваемой на этапе 1 точке. Для каждой линии вызываем функцию, которую можно назвать OnBlackWhite(). Эта функция по формуле линии определяет перепады яркости, считая по сколько пикселей приходится на каждый перепад. Полученные количество и величину перепадов сохраняем, после чего находятся математическое ожидания и дисперсия перепадов.

4 этап. Из всех значение дисперсии находится минимальное, которое и будет соответствовать линии в наибольшей степени соответствующей пунктиру (Рис. 1). На графике минимум достигается на шаге 21, который и соответствует пунктиру.

Рис. 1. График зависимости дисперсии перепадов от угла

Рис. 1. График зависимости дисперсии перепадов от угла

15 Май 2010

27. Стереозрение с использованием особенностей

написано в рубрике: OpenCV, Стереозрение — Кручинин Александр @ 6:42 ПП

В http://blog.vidikon.com/?p=213 показано, как, находя особенности объектов, можно их распознавать. Естественно возникает мысль, а что если выделить на обоих изображениях особенности и сравнить их между собой. По расхождению в пикселях найти расстояния до этих особенностей, которые и использовать, к примеру, для ориентации робота в пространстве.

Проверка гипотезы осуществлялась при помощи модели http://blog.vidikon.com/?p=191, в которой использовались те же два скриншота, что и здесь http://blog.vidikon.com/?p=236.  В результате было найдено 104 пары особенностей (Рис. 27.1 и 27.2)

Рис. 27.1. Особенности левого изображения

Рис. 27.1. Особенности левого изображения

Рис. 27.2. Особенности правого изображения

Рис. 27.2. Особенности правого изображения

Соответственно для 104 особенностей нашлось столько же трёхмерных точек, по которым была построена модель (Рис. 27.3).

Рис. 27.3. Трёхмерная модель особенностей

Рис. 27.3. Трёхмерная модель особенностей

На рисунках 27.4 и 27.5 показаны изображения модели с различных сторон.

Рис. 27.4.

Рис. 27.4.

Рис. 27.5

Рис. 27.5

Можно заметить, что по особенностям строится пол и потолок тестируемой модели, т.к. на стенах особенностей не было найдено.

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

13 Май 2010

Описание библиотеки geometry 1.00

написано в рубрике: Другие библиотеки — Кручинин Александр @ 5:14 ПП

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

Структуры:

 

F_LINE – описывает линию, k1 – резерв, назначение остальных параметров

При b=1 y=b1*x+b2, при b=2 x=b1*y+b2

C_POINT – описание целой точки

C_POINTd – описание точки в формате double

FOUR_FIG_P – описание четырёхугольника с помощью целых точек

FOUR_FIG_Pd – описание четырёхугольника с помощью точек в формате double

FOUR_FIG_L – описание четырёхугольник при помощи линий

 

Функции:

 

F_LINE MakeLine(double x1,double y1,double x2,double y2);

F_LINE MakeLine(C_POINT p1,C_POINT p2);

F_LINE MakeLine(C_POINTd p1,C_POINTd p2);

По заданным двум точкам функции строят линию.

 

C_POINTd FindPointContinue(C_POINTd p1,C_POINTd p2,F_LINE line,double size);

Иногда, когда известны две точки и линия между ними, необходимо построить продолжение этой лини на определённую длину, что и делает данная функция. Функция возвращает продолжение линии line построенное ранее между точками p1 и p2 и возвращает координаты точки p, которая является продолжением линии p1 и p2 от точки p1.

 

int Intersection(F_LINE f1,F_LINE f2,double &x,double &y);

Функция вычисляет точку пересечения двух линий и возвращает координаты этих точек в x и y. Пре неудаче функция возвращает 1, при удача – 0.

 

double LengthLine(double x1,double y1,double x2,double y2);

Функция определяет расстояние между двумя точками.

 

float MakePolarR(float x,float y);

float MakePolarF(float x,float y);

Эти функции переводят декартовы координаты в полярные.

 

bool DirectPoint(C_POINT*p,int all,unsigned char flags);

Эта функция, ссылаясь на массив точек, определяет можно ли их по порядку обойти по часовой стрелке на плоскости изображения (DIRECT_HOUR_ARROW) или против часовой (DIRECT_HOUR_ARROW).

 

C_POINTd PointID(C_POINT p);

C_POINT PointDI(C_POINTd p);

Перевод точек из одного формата в другой

 

double RToLine(F_LINE f,double x,double y);

double RToLine(F_LINE f,C_POINTd p);

Высчитывают расстояние от точки до прямой.

 

C_POINTd Average(C_POINTd p1,C_POINTd p2);

Для двух точек ищется средняя между ними.

 

F_LINE MakeParallelLine(F_LINE line,C_POINTd point);

Функция строит параллельную линию, которая проходит в точке point.

 

double FoundInclination(F_LINE line,F_LINE line1);

Функция возвращает угол в радианах между двумя линиями.

 

F_LINE TurnLine(F_LINE f,double alfa,C_POINTd point);

Функция поворачивает линию f на угол alfa и строит её в точке point. Возвращаемое значение записывается в возвращаемый параметр.

11 Май 2010

Библиотека geometry – Геометрические операции

написано в рубрике: Другие библиотеки — Кручинин Александр @ 12:01 ПП

Очень часто при распознавании изображений возникает необходимость в геометрических преобразованиях – получения точки пересечения двух линий, построения параллельной линии, построение продолжения линии, нахождения угла между линиями и т.д.  Библиотека geometry – это просто сборник небольших полезных функций. Лицензия библиотеки – BSD.

geometry 1.00

http://vidikon.com/download/geometry1.00.zip

Старые записи »

Работает на WordPress