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

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

26 Январь 2010

12. Управляем презентацией взмахом руки

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

12. Управляем презентацией взмахом руки

 

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

 

Листинг 12.1. Функция детектирования взмаха руки

void FoundMotion(IplImage* img1,IplImage* img2)

{

//Установка начальных параметров

      short VKCode = ‘ ‘;

      CvSize img_sz = cvGetSize( img1 );

      int win_size = 10;

 

      IplImage* eig_image = cvCreateImage( img_sz, IPL_DEPTH_32F, 1 );

      IplImage* tmp_image = cvCreateImage( img_sz, IPL_DEPTH_32F, 1 );

     

      int corner_count = MAX_CORNERS;

 

      CvPoint2D32f* cornersA = new CvPoint2D32f[ MAX_CORNERS ];

 

      cvGoodFeaturesToTrack(img1,eig_image,tmp_image,cornersA,&corner_count,

                                     0.01,5.0,0,3,0,0.04);

      cvFindCornerSubPix(img1,cornersA,corner_count,cvSize(win_size,win_size),

                                cvSize(-1,-1),cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));

 

      //Создаются дополнительные массивы для cvCalcOpticalFlowPyrLK

      char features_found[ MAX_CORNERS ];

      float feature_errors[ MAX_CORNERS ];

      CvSize pyr_sz = cvSize( img1->width+8, img2->height/3 );

      IplImage* pyrA = cvCreateImage( pyr_sz, IPL_DEPTH_32F, 1 );

      IplImage* pyrB = cvCreateImage( pyr_sz, IPL_DEPTH_32F, 1 );

      CvPoint2D32f* cornersB = new CvPoint2D32f[ MAX_CORNERS ];

 

      cvCalcOpticalFlowPyrLK(img1,img2,pyrA,pyrB,cornersA,cornersB,corner_count,cvSize( win_size,win_size ),5,features_found,feature_errors,cvTermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, .3 ),0);

 

      int all=0;

      int up=0;

      int down=0;

      F_LINE f;

//Совпадения с точками нашли, можно их обрабатвать

      for( int i=0; i<corner_count; i++ ) {

            if( features_found[i]==0|| feature_errors[i]>550 ) {

                                                continue;

            }

            CvPoint p0 = cvPoint(cvRound( cornersA[i].x ),cvRound( cornersA[i].y ));

            CvPoint p1 = cvPoint(cvRound( cornersB[i].x ),cvRound( cornersB[i].y ));

            //Удаляем точки с небольшим смещением

            if (sqrt((double)pow((double)p0.x-p1.x,2)+(double)pow((double)p0.y-p1.y,2))<10) continue;

            //Делаем формулу линии

            all++;

            f=MakeLine(p0.x,p0.y,p1.x,p1.y);

            //Только вертикальные линии

            if (f.b==2 && absf(f.b1)<0.2)

            {

                  if (p1.y<p0.y) down++;

                  else up++;

                  cvLine( img2, p0, p1, CV_RGB(255,0,0),2 );

            }

      }

      cvShowImage( “M”, img2);

 

      cvReleaseImage( &eig_image );

      cvReleaseImage( &tmp_image  );

      cvReleaseImage( &pyrA  );

      cvReleaseImage( &pyrB );

      delete cornersA;

      delete cornersB;

 

      if (motion_1>0) motion_1–;

      if (motion_1==10) motion_1=0;

 

      if (all>20)

      if ((double)up/all>0.4 && (double)down/all<0.1)

      {

            //объект двигается вверх

            if (motion_1<10) motion_1=10;

      }

 

      if (all>20)

      if ((double)down/all>0.4 && (double)up/all<0.1)

      {

            //объект идёт вниз

            if (motion_1>0 && motion_1<8) {

                  printf(”vzmax222222222\n”);

                  //Состоялся взмах

                  motion_1=15;

                  VKCode=VK_RIGHT;

                  keybd_event(VKCode, 0, 0, 0);

            }

 

      }

 

      printf(”%d;%d;%d\n”,all,up,down);

}

 

Листинг описан поверхностно – без подробного описания назначения функций. Часть кода взята из известной книги «Learning OpenCV» [1].

Функция получает на входе два изображения – текущий и предыдущий кадр с камеры. Ранее должно быть определено:

const int MAX_CORNERS = 500;

Первоначально выделяется память под хранение углов cornersA. Затем с помощью функции cvGoodFeaturesToTrack() находятся углы с наибольшим собственным значением в изображении. С помощью функции cvFindCornerSubPix() определяется точное местоположение углов или радиальных седловых точек.

После создания дополнительных массивов вызывается функция cvCalcOpticalFlowPyrLK(), которая осуществляет алгоритм слежения за точками Lucas-Kanade tracker. Подробнее об это алгоритме вы можете почитать в публикации [2] или в описании OpenCV.

В результате работы функции находятся совпадения с точками на втором изображении. Затем идёт обработка полученных результатов. В функции используются дополнительные переменные, структура и функция (Листинг 12.2).

 

Листинг 12.2. Дополнительные переменные, структуры и функции

//Переменная для отслеживания комбинации движения

int motion_1=0;

 

struct F_LINE

{

      int k1;

      int b;

      double b1,b2;

};

 

 

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

{

      F_LINE f;

      f.k1=3;

            //вычисляем координаыт линии от i3 к i4

      f.b=1;

      if (absf(x1-x2)<absf(y1-y2)) f.b=2;

      if (f.b==1)

      {

            //y=f(x);

            f.b1=(double)(y2-y1)/(x2-x1);

            f.b2=(double)y1-(double)x1*f.b1;

      }

      if (f.b==2)

      {

            //x=f(y);

            f.b1=(double)(x2-x1)/(y2-y1);

            f.b2=(double)x1-(double)y1*f.b1;     }

      return f;

}

 

В начале обработки удаляются точки, чьё смещение было минимальным – поскольку надо «ловить» смещение движущегося объекта. После вычисления формулы линии мы оставляем только вертикальные (или почти вертикальные) линии, т.к. будем отслеживать взмах вверх-вниз.

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

Если взмах состоялся, то эмулируется нажатие кнопки вправо с помощью функции keybd_event(). Пример слежения за точками представлен на рисунке 12.1.

Рис. 12.1 Наблюдение за движением руки
Рис. 12.1 Наблюдение за движением руки

Литература:

1. Bradsky G., Kaehler A. Learning OpenCV – O’Reilly, 2008. – 555 p.

2. http://cgm.computergraphics.ru/content/view/54

2 комментариев

  1. здравствуйте.Попробовал написать вышеописанное приложение,но очень тормозит-не поможете ?

    Комментарий от Стас — 6 Март 2011 @ 12:11 ДП

  2. Да оно булет тормозить. Можно попробовать уменьшить изображение до 320 на 240. Или использовать слежение за точками:
    http://blog.vidikon.com/?p=277

    Комментарий от Кручинин Александр — 6 Март 2011 @ 10:53 ДП

RSS лента комментариев к этой записи.

Извините, комментирование на данный момент закрыто.

Работает на WordPress