Jump to content
Compvision.ru
Sign in to follow this  
Cfr

Трекинг объектов.

Recommended Posts

Переработанный пример find_obj. Изначально создавался для проверки скорости и качества SURF-трекинга. Потом хотел добавить переход на LKT для скорости и сделать простенький AR, но так и не доделал. Если кого заинтересует -- нужно передать маску с закрашенным объектом в GoodFeaturesToTrack и трекить LKPyr (фичи SURF не очень хороши для LK трекинга).

Не факт, что в винде соберётся :( Если так -- интересно будет диагностировать проблему.

surftracking.tar.gz

Share this post


Link to post
Share on other sites

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

Лицо захватывается в видео потоке и очерчивается рамкой, мне передаются координаты этой рамки,

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

Актуально ли в этом случае использовать фильтр Калмана например?

И как мне идентифицировать это лицо?

Получается в каждом кадре видео мне надо находить снова это лицо по каким-то параметрам.

А по каким?

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

Share this post


Link to post
Share on other sites

Давай попробуем разобраться что есть что.

1. Нам подаётся видеопоток, в котором содержатся или не содержатся интересующие нас объекты - лица.

2. Лица надо как-то обнаружить: каскады Хаара, поиск регионов с текстурой, похожей на кожу и т.п. Поиск надо производить на каждом кадре?

3. Если поиск производится на каждом кадре, то надо определить какое лицо на кадре t соответствует лицу на кадре t+1. Есть варианты: по координатам, по размеру, найти точки с особенностями и сравнивать их и т.д. Соответствие между лицами на соседних кадрах найдено? Это и есть сопровождение.

4. Допустим, нам надо найти первое попавшееся лицо и сопровождать только его в видеопотоке. Тут можно воспользоваться алгоритмом Лукаса-Канаде. Находим алгоритмом из пункта 2 лицо, находим на нём характерные точки (например, как в примере lkdemo.exe). Сопровождаем точки с помощью Лукаса-Канаде; после их пропадания считаем, что лицо исчезло из поля зрения. Снова переходим к пункту 2.

5. Фильтр Кальмана в компьютерном зрении применяется для сглаживания траектории движения объекта (лица), а также для предсказания его положения на следующем кадре. Тут необходимо отметить, что фильтр Кальмана предназначен для линейных моделей движения. Для нелинейного же применяется particle filter (как вариант particle filter + mean shift).

Share this post


Link to post
Share on other sites
Давай попробуем разобраться что есть что.

1. Нам подаётся видеопоток, в котором содержатся или не содержатся интересующие нас объекты - лица.

2. Лица надо как-то обнаружить: каскады Хаара, поиск регионов с текстурой, похожей на кожу и т.п. Поиск надо производить на каждом кадре?

3. Если поиск производится на каждом кадре, то надо определить какое лицо на кадре t соответствует лицу на кадре t+1. Есть варианты: по координатам, по размеру, найти точки с особенностями и сравнивать их и т.д. Соответствие между лицами на соседних кадрах найдено? Это и есть сопровождение.

4. Допустим, нам надо найти первое попавшееся лицо и сопровождать только его в видеопотоке. Тут можно воспользоваться алгоритмом Лукаса-Канаде. Находим алгоритмом из пункта 2 лицо, находим на нём характерные точки (например, как в примере lkdemo.exe). Сопровождаем точки с помощью Лукаса-Канаде; после их пропадания считаем, что лицо исчезло из поля зрения. Снова переходим к пункту 2.

5. Фильтр Кальмана в компьютерном зрении применяется для сглаживания траектории движения объекта (лица), а также для предсказания его положения на следующем кадре. Тут необходимо отметить, что фильтр Кальмана предназначен для линейных моделей движения. Для нелинейного же применяется particle filter (как вариант particle filter + mean shift).

Спасибо за столь содержательный ответ.

Я сам посидел сейчас подумал над всем этим....

1. Нам действительно подается видеопоток.

2. Лицо обнаруживается с помощью каскадов Хаара и очерчивается рамкой.

3. Далее мне выдаются координаты рамки с объектом (лицом)

4. Тут я посмотрел алгоритм Лукаса-Канаде и решил воспользоваться им.

Пока не знаю, как переделать пример в openCV под мой случай.

Но мне надо как раз то, что Вы описали в 4 пункте.

Share this post


Link to post
Share on other sites

Как в примере lkdemo.exe:

1. На кадре найдено лицо:

а) заполняем массив точек лица каким-либо способом: равномерно по площади лица, случайным образом, cvGoodFeaturesToTrack;

б) если использовались первые 2 способа, то для массива точек вызываем cvFindCornerSubPix.

2. Для следующего кадра: вызываем cvCalcOpticalFlowPyrLK, добавляем к флагам CV_LKFLOW_PYR_A_READY.

3. Если полученное число точек равно нулю - цель потеряна, выход, переходим к пункту 1.

4. Удаляем из массива не найденные на очередном кадре точки на основании массива status из cvCalcOpticalFlowPyrLK.

5. Если какая-то точка слишком далеко "оторвалась" от объекта (например, на расстояние, превышающее размер лица), то её тоже удаляем.

6. Если точек не осталось или осталось слишком мало (например 1-2 точки на лицо), то считаем цель потерянной, выход, переходим к пункту 1.

7. Вычисляем прямоугольник, описывающий оставшиеся точки, выводим его.

8. Можно вывести на кадре траекторию движения лица: центры прямоугольников на каждом кадре; при желании траекторию можно обрабатывать фильтром Кальмана.

9. Переход к пункту 2.

Реализация практически всех пунктов есть в примере lkdemo.exe

Если качество видео достаточно высокое, то сопровождение должно работать корректно.

Share this post


Link to post
Share on other sites
Как в примере lkdemo.exe:

1. На кадре найдено лицо:

а) заполняем массив точек лица каким-либо способом: равномерно по площади лица, случайным образом, cvGoodFeaturesToTrack;

б) если использовались первые 2 способа, то для массива точек вызываем cvFindCornerSubPix.

2. Для следующего кадра: вызываем cvCalcOpticalFlowPyrLK, добавляем к флагам CV_LKFLOW_PYR_A_READY.

3. Если полученное число точек равно нулю - цель потеряна, выход, переходим к пункту 1.

4. Удаляем из массива не найденные на очередном кадре точки на основании массива status из cvCalcOpticalFlowPyrLK.

5. Если какая-то точка слишком далеко "оторвалась" от объекта (например, на расстояние, превышающее размер лица), то её тоже удаляем.

6. Если точек не осталось или осталось слишком мало (например 1-2 точки на лицо), то считаем цель потерянной, выход, переходим к пункту 1.

7. Вычисляем прямоугольник, описывающий оставшиеся точки, выводим его.

8. Можно вывести на кадре траекторию движения лица: центры прямоугольников на каждом кадре; при желании траекторию можно обрабатывать фильтром Кальмана.

9. Переход к пункту 2.

Реализация практически всех пунктов есть в примере lkdemo.exe

Если качество видео достаточно высокое, то сопровождение должно работать корректно.

Спасибо.

Звучит красиво :)

Попытаюсь написать все это, если знаний хватит )

Share this post


Link to post
Share on other sites

Nuzhny, проконсультируйте пожалуйста.

Есть алгоритм захвата лица.

Мне передают координаты прямоугольника, в который это лицо захватили, пометил красным (вроде так, как я понял, код писал не я)

// viola-jonsa.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "highgui.h"
#include "cv.h"
#include "cxcore.h"

#include "time.h"
#include "stdlib.h"
#define _USE_MATH_DEFINES
#include "math.h"

#include <string>
#include <sstream>

//=============Gloabal variables================

char* cascade_name =
// "C:\\_work\\OpenCV_SVN\\data\\haarcascades\\haarcascade_frontalface_default.xml"
"C:\\_work\\OpenCV_SVN\\data\\haarcascades\\haarcascade_frontalface_alt.xml"
// "C:\\_work\\OpenCV_SVN\\data\\haarcascades\\haarcascade_frontalface_alt2.xml"
// "C:\\_work\\OpenCV_SVN\\data\\haarcascades\\haarcascade_profileface.xml"
// "C:\\_work\\OpenCV_SVN\\data\\haarcascades\\haarcascade_mcs_nose.xml"
// "C:\\_work\\OpenCV_SVN\\data\\haarcascades\\haarcascade_eye.xml"
// "C:\\_work\\facedbs\\3\\train\\cascade.xml"
;

int angle_switch_value = 2;
int angleInt = 2;
double angle = 0;
IplImage *image;
IplImage *copy;

CvFont font;

int count1 = 100;
int count2 = 0;
int count3 = 0;

//==========additional functions============

void switch_callback_a(int position) {
angleInt = position;
}

void pr( const CvMat* mat ) {
float s = 0.0f;
for(int row=0; row<mat->rows; row++ ) {
const float* ptr = (const float*)(mat->data.ptr + row * mat->step);
for( int col=0; col<mat->cols; col++ ) {
s = *ptr++;
printf("%f ", s);
}
printf("\n");
}
}

void rotate(IplImage* src, IplImage* dst, double angle)
{
// Set up variables
CvMat* rot_mat = cvCreateMat(2,3,CV_32FC1);

// Compute rotation matrix
CvPoint2D32f center = cvPoint2D32f( src->width/2, src->height/2 );
cv2DRotationMatrix( center, angle, 1.0, rot_mat );

// Do the transformation
cvWarpAffine( src, dst, rot_mat );

// Release variables
cvReleaseMat( &rot_mat );
}

void map(const CvPoint src, CvPoint* dst, CvPoint center, double angle)
{
angle *= M_PI / 180;
double a = abs(center.x - src.x);
double b = abs(center.y - src.y);
double c = sqrt(pow(a, 2) + pow(b, 2));

double alpha = acos(b / c);

if ((src.x < center.x) && (src.y < center.y)) {
alpha -= angle;
// a = c * sin(alpha);
// b = c * cos(alpha);
}
else if ((src.x > center.x) && (src.y < center.y)) {
alpha = 6.28 - alpha;
alpha -= angle;
// a = c * sin(alpha);
// b = c * cos(alpha);
}
else if ((src.x < center.x) && (src.y > center.y)) {
alpha = 3.14 - alpha;
alpha -= angle;
// a = c * sin(alpha);
// b = c * cos(alpha);
}
else {
alpha = 3.14 + alpha;
alpha -= angle;
}
a = c * sin(alpha);
b = c * cos(alpha);
dst->x = center.x - (int)a;
dst->y = center.y - (int)b;
}

void color_filter(IplImage* img)
{
double t = (double)clock();
for( int y=0; y<img->height; y++ ) {
uchar* ptr = (uchar*)(img->imageData + y * img->widthStep);
for( int x=0; x<img->width; x++ ) {
uchar b = ptr[3*x];
uchar g = ptr[3*x+1];
uchar r = ptr[3*x+2];
if (r > 95 && g > 40 && b > 20 && (r - ((g < ? g : ) > 15 && abs(r - g) > 15
&& r > g && r > {
ptr[3*x] = 0;
ptr[3*x+1] = 0;
ptr[3*x+2] = 0;
} else {
ptr[3*x] = 255;
ptr[3*x+1] = 255;
ptr[3*x+2] = 255;
}
}
}
t = (double)clock() - t;
printf("time %f\n", t / CLOCKS_PER_SEC);
}

CvSeq* filter_double(CvSeq* objects, CvMemStorage* storage)
{
CvRect* obj1, *obj2;
int s = 0;
CvSeq* result = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvRect), storage);;
int doub = 0;
for (int i = 0; i < (objects ? objects->total : 0); i++) {
for (int j = i + 1; j < (objects ? objects->total : 0); j++) {
obj1 = (CvRect*)cvGetSeqElem(objects, i);
obj2 = (CvRect*)cvGetSeqElem(objects, j);
if ((obj1->x - (obj2->x + obj2->width) > s) || (obj2->x - (obj1->x + obj1->width) > s) ||
(obj1->y - (obj2->y + obj2->height) > s) || (obj2->y - (obj1->y + obj1->height) > s)) {}
else doub = 1;
}
if (!doub) cvSeqPush(result, obj1);
doub = 0;
}
return result;
}

CvSeq* detect(IplImage* img, double scale = 1.0)
{
// count time
clock_t time = clock();

CvMemStorage*storage = cvCreateMemStorage(0);
CvHaarClassifierCascade* cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );
// IMAGE PREPARATION:
//
IplImage* gray = cvCreateImage(cvSize(img->width,img->height), 8, 1);
IplImage* small_img = cvCreateImage(cvSize(cvRound(img->width/scale), cvRound(img->height/scale)), 8, 1);
cvCvtColor(img, gray, CV_BGR2GRAY);
cvResize(gray, small_img, CV_INTER_LINEAR);
cvEqualizeHist(small_img, small_img);

// DETECT OBJECTS IF ANY
//
cvClearMemStorage(storage);

CvSeq* faces = cvHaarDetectObjects(small_img, cascade, storage, 1.1, 2,
0 /*CV_HAAR_DO_CANNY_PRUNING*/
//|CV_HAAR_FIND_BIGGEST_OBJECT
//|CV_HAAR_DO_ROUGH_SEARCH
|CV_HAAR_SCALE_IMAGE
, cvSize(20, 20));

cvReleaseImage( &gray );
cvReleaseImage( &small_img );

time = clock() - time;
printf("%f\n", (double)time / CLOCKS_PER_SEC);

return faces;
}

void draw(CvSeq* objects, IplImage* img, double scale = 1.0)
{
// int radius;
static CvScalar colors[] = {
{0, 0, 255}, {0, 128, 255}, {0, 255, 255}, {0, 255, 0},
{255, 128, 0}, {255, 255, 0}, {255, 0, 0}, {255, 0, 255}
}; //some colors to draw with

// LOOP THROUGH FOUND OBJECTS AND DRAW BOXES AROUND THEM
for (int i = 0; i < (objects ? objects->total : 0); i++ ) {
[color="#FF0000"]CvRect* r = (CvRect*)cvGetSeqElem( objects, i );[/color]

//MAP ROTATED OBJECTS TO MAIN IMAGE
//map(cvPoint(r->x, r->y), point,
// cvPoint(small_img->width / 2, small_img->height / 2), angle);

//DRAW CIRCLE
/*int radius = cvRound((r->width + r->height)*0.25*scale);
cvCircle( img,
cvPoint(cvRound((r->x + r->width*0.5)*scale), cvRound((r->y + r->height*0.5)*scale)),
radius, colors[i%8], 2, 8, 0 );*/

//DRAW MAPED ROTATED OBJECTS ON MAIN IMAGE
//cvRectangle(copy, cvPoint(point->x * scale, point->y * scale),
// cvPoint((point->x + r->width) * scale, (point->y + r->height) * scale), colors[i%8]);

//DRAW BOX
[color="#FF0000"]cvRectangle(img, cvPoint(r->x * scale, r->y * scale),
cvPoint((r->x + r->width) * scale, (r->y + r->height) * scale), colors[i%8]);[/color]

}
}

void detect_and_draw(IplImage* img, double scale = 1.0)
{
CvSeq* objects = detect(img, scale);
draw(objects, img, scale);

//DETECT DOUBLE OBJECTS
/*CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* filter = filter_double(objects, storage);
for (int i = 0; i < (filter ? filter->total : 0); i++) {
CvRect* r = (CvRect*)cvGetSeqElem(filter, i);
cvRectangle(img, cvPoint(r->x,r->y), cvPoint(r->x+r->width,r->y+r->height), colors[i%8]);
}*/

}

void write(CvSeq* faces, double scale)
{
//FILE NAME: [folder number]-[image number]-[object number]
char* savepath = "C:\\_work\\facedbs\\students2\\out2\\";
std::stringstream save;
save.str(std::string());

if (count2 < 13) count2++; //13 images in folder
else { count2 = 1; count1++; } //next folder

count3 = 0;
for(int i = 0; i < (faces ? faces->total : 0); i++) {
count3++;
CvRect* ro = (CvRect*)cvGetSeqElem(faces, i);
cvRect(ro->x * scale, ro->y * scale, ro->width * scale, ro->height * scale);
cvSetImageROI(image, cvRect(ro->x * scale, ro->y * scale, ro->width * scale, ro->height * scale));//*r);
save.str("");
save << savepath
<< count1
<< "-"
<< count2
<< "-"
<< count3
<< ".jpg";
printf(save.str().c_str());
printf("\n");
cvSaveImage(save.str().c_str(), image);
cvResetImageROI(image);
}
}

void process_from_file(char* filename)
{
CvSeq* faces;
//char* savepath = "C:\\_work\\facedbs\\students2\\";
char str[100];

FILE* f = fopen(filename, "r");
if (f)
{
// READ LINE BY LINE FROM FILE
int l = 0;
char c;
while ((c = fgetc(f)) != '?')
{

if (c == '\n') {
str[l] = '\0';
//LOAD IMAGE, DETECT AND WRITE OBJECTS TO FILES
image = cvLoadImage(str, 1);
double scale = 4.5;
//copy = cvCreateImage(cvSize(cvRound(image->width/scale), cvRound(image->height/scale)), 8, 3);
//cvResize(image, copy, CV_INTER_LINEAR);
faces = detect(image, scale); //image
write(faces, scale);
cvReleaseImage(&image);
// printf(str);
l = 0;
} else {
str[l] = c;
l++;
}

}
fclose(f);
//cvReleaseImage(&image);
}
}

void interactive()
{
char* name1 = "Face detection";
char* name2 = "Rotated faces";
char* text;

IplImage *rot_img;

image = cvLoadImage(
// "C:\\_work\\facedbs\\Out\\ММЭ-106\\Гуткина Людмила Борисовна\\1.bmp"
// "C:\\_work\\Портрет\\1.jpg"
// "C:\\_work\\facedbs\\students2\\New folder\\DSC02261_800x600.jpg"
"C:\\_work\\facedbs\\students2\\AIS-106\\Anikeeva Elena Igorevna\\DSC02261.jpg"
// "C:\\_work\\3.jpg"
);
//copy = cvCloneImage(image);
double scale = 4.5;
copy = cvCreateImage(cvSize(cvRound(image->width/scale), cvRound(image->height/scale)), 8, 3);

cvResize(image, copy, CV_INTER_LINEAR);
rot_img = cvCloneImage(copy); //image
rot_img->origin = image->origin;;

cvNamedWindow(name1);
cvNamedWindow(name2);
cvCreateTrackbar("Angle", name1, &angle_switch_value, 4, switch_callback_a);
cvShowImage(name1, copy);

cvWaitKey(0);
while(1) {
switch( angleInt ){
case 0:
angle = 20.0;
break;
case 1:
angle = 10.0;
break;
case 2:
angle = 0.0;
break;
case 3:
angle = -10.0;
break;
case 4:
angle = -20.0;
break;
}


rotate(copy, rot_img, angle); //image
//color_filter(rot);
detect_and_draw(rot_img);

// cvPutText(img, angle, cvPoint(10, 30), font, cvScalar(256, 256, 256));
cvShowImage(name1, copy);
cvShowImage(name2, rot_img);
if( cvWaitKey( 0 ) == 27 ) {
cvReleaseImage(&rot_img);
cvReleaseImage(&image);
cvDestroyWindow(name1);
cvDestroyWindow(name2);
break;
}
}
}

void video()
{
int frame_number = 0;
std::stringstream print;
print.str(std::string());
cvNamedWindow("frame1", CV_WINDOW_AUTOSIZE);
//cvNamedWindow( "frame2", CV_WINDOW_AUTOSIZE );
CvCapture* capture = cvCreateFileCapture("c:\\_work\\1.mpg");
//CvCapture* capture = cvCreateCameraCapture(0);
//IplImage frame1, frame2, frame3;
IplImage *pframe1;
IplImage *pframe2;
IplImage *pframe3;
pframe1 = cvQueryFrame(capture);
print << pframe1->width << "x" << pframe1->height;
//frame2 = cvQueryFrame(capture);
pframe3 = cvCloneImage(pframe1);
pframe3->origin = pframe1->origin;
while(1) {
pframe1 = cvQueryFrame(capture);
frame_number++;
if (frame_number == 1) {
pframe2 = cvCloneImage(pframe1);
//cvSmooth(pframe2, pframe2,CV_BLUR, 3, 3);
cvPutText(pframe2, print.str().c_str(), cvPoint(10, 30), &font, cvScalar(256, 256, 256));
cvShowImage("frame1", pframe2);

}
if (frame_number == 2) {
frame_number = 0;
//cvSetImageROI(pframe1, cvRect(0, 0, 50, 200));
//cvSetImageROI(pframe2, cvRect(0, 0, 50, 200));
//cvSetImageROI(pframe3, cvRect(0, 0, 50, 200));
// cvAbsDiff(pframe1, pframe2, pframe3);
// cvThreshold(pframe3, pframe3, 50, 255,
//CV_THRESH_BINARY
//CV_THRESH_BINARY_INV
//CV_THRESH_TRUNC
//CV_THRESH_TOZERO_INV
// CV_THRESH_TOZERO
// );
detect_and_draw(pframe2);
cvShowImage("frame2", pframe2);
//cvShowImage("frame2", pframe3);
/*cvResetImageROI(pframe1);
cvResetImageROI(pframe2);
cvResetImageROI(pframe3);*/
}
//cvAbsDiff(frame2, frame1, frameForeground);
//cvThreshold(frameForeground, frameForeground, 15, 255, CV_THRESH_BINARY);
//cvShowImage("frame2", frameForeground);
//frame = cvQueryFrame(capture);
//if(!frame) break;
////cvPutText(frame, print.str().c_str(), cvPoint(10, 30), &font, cvScalar(256, 256, 256));
//detect_and_draw(frame1);
//cvShowImage("frame1", frame1);

if( cvWaitKey(33) == 27 ) break;
}
cvReleaseCapture(&capture);
//cvReleaseImage(&pframe1);
//cvReleaseImage(&pframe2);
cvReleaseImage(&pframe3);
cvDestroyWindow("frame1");
cvDestroyWindow( "frame2" );
}

//==========Main function==============

int main(int argc, CHAR* argv[])
{
cvInitFont(&font,
CV_FONT_VECTOR0
//CV_FONT_HERSHEY_COMPLEX_SMALL
, 0.5, 0.5);
process_from_file("C:\\_work\\facedbs\\students2\\list3.txt");
//interactive();
//video();
/*IplImage* im1 = cvLoadImage(
"C:\\_work\\facedbs\\DB\\Out\\MME-106\\Blinov_Alexey_Germanovich\\3.bmp"
);
IplImage* im2 = cvLoadImage(
"C:\\_work\\facedbs\\DB\\Out\\MME-106\\Blinov_Alexey_Germanovich\\4.bmp"
);
IplImage* im3 = cvCloneImage(im2);
im3->origin = im1->origin;
cvAbsDiff(im1, im2, im3);
cvThreshold(im3, im3, 50, 255, CV_THRESH_BINARY);
cvNamedWindow("1");
cvShowImage("1", im3);
cvWaitKey(0);
cvReleaseImage(&im1);
cvReleaseImage(&im2);
cvReleaseImage(&im3);
cvDestroyWindow("1");*/
return 0;
}[/codebox]

Далее мне надо получить эти координаты в своем алгоритме сопровождения, т.е. где-то я вставляю cvFindCornerSubPix, но как воспользоваться не понимаю.

И с cvCalcOptionalFlowPyrLK не понял

[codebox]#ifdef _CH_
#pragma package <opencv>
#endif

//#define CV_NO_BACKWARD_COMPATIBILITY

#ifndef _EiC
#include "stdafx.h"
#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 = 20;
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;
}

/* print a welcome message, and the OpenCV version */
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 );

for(;:)
{
IplImage* frame = 0;
int i, k, c;

frame = cvQueryFrame( capture );
if( !frame )
break;

if( !image )
{
/* allocate all the buffers */
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 );
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 );

if( night_mode )
cvZero( image );

if( need_to_init )
{
/* automatic initialization */
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 )
{
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

Share this post


Link to post
Share on other sites

Посмотри еще Шаблоны Движения: http://www.compvision.ru/forum/index.php?showtopic=17

Очень полезная штука. Хотя как полноценно прикрутить трекинг объектов средствами OpenCV, я не придумал...

Если кто подскажет буду очень благодарен.

Share this post


Link to post
Share on other sites

2gonzik: Ну в примерах же всё есть! Надо же просто скомпоновать. Нерадивый студент?

Вот, набросал на скорую руку, но сразу скажу, что не реализовал пункт 5: Если какая-то точка слишком далеко "оторвалась" от объекта (например, на расстояние, превышающее размер лица), то её тоже удаляем.

Сам попробуй, а то уже футбол начался, "Рубин" играет.

#include <algorithm>
#include <limits>

#include <highgui.h>
#include <cv.h>

#undef min
#undef max

#pragma comment(lib, "cv.lib")
#pragma comment(lib, "cxcore.lib")
#pragma comment(lib, "highgui.lib")
////////////////////////////////////////////////////////////////////////////
//Поиск лица на кадре
bool detect_face(IplImage *gray_img, CvHaarClassifierCascade *haar_cascade, CvMemStorage *mem_storage, CvRect &face_rect)
{
cvClearMemStorage(mem_storage);

//Поиск лиц
CvSeq *faces = cvHaarDetectObjects(gray_img, haar_cascade, mem_storage, 1.1, 2, 0, cvSize(20, 20));
if (!faces || !faces->total)
return false;

//Возвращаем первое найденное
face_rect = *(CvRect *)cvGetSeqElem(faces, 0);
return true;
}
////////////////////////////////////////////////////////////////////////////
class LKData
{
private:
//Всякие данные
IplImage *curr_img;
IplImage *prev_img;
IplImage *curr_pyramid_img;
IplImage *prev_pyramid_img;

int points_count;
CvPoint2D32f *curr_points;
CvPoint2D32f *prev_points;
char *status;

CvTermCriteria tc;
int flags;

public:
LKData()
: curr_img(NULL), prev_img(NULL), curr_pyramid_img(NULL), prev_pyramid_img(NULL),
points_count(0), flags(0), curr_points(NULL), prev_points(NULL), status(NULL)
{
tc = cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03);
cvNamedWindow("points", 1);
}
////////////////////////////////////////////////////////////////////////////
~LKData()
{
deinit();
cvDestroyWindow("points");
}
////////////////////////////////////////////////////////////////////////////
//Инициализация данных для сопровождения
void init(const IplImage *gray_img, const CvRect &face_rect)
{
deinit();

curr_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);
prev_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);
curr_pyramid_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);
prev_pyramid_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);

//Покроем прямоугольник лица равномерной сеткой из точек
const int step_x = face_rect.width / 16;
const int step_y = face_rect.height / 16;
points_count = (face_rect.width / step_x + 1) * (face_rect.height / step_y + 1);
curr_points = new CvPoint2D32f[points_count];
prev_points = new CvPoint2D32f[points_count];
status = new char[points_count];

size_t pc = 0;
for (int i = face_rect.y, stop_i = face_rect.y + face_rect.height; i < stop_i; i += step_y)
{
for (int j = face_rect.x, stop_j = face_rect.x + face_rect.width; j < stop_j; j += step_x)
{
curr_points[pc] = cvPoint2D32f((double)j, (double)i);
++pc;
}
}
points_count = (int)pc;

cvCopyImage(gray_img, curr_img);
cvFindCornerSubPix(curr_img, curr_points, points_count, cvSize(3, 3), cvSize(-1, -1), tc);

//Смена указателей на параметры
std::swap(prev_img, curr_img);
std::swap(prev_pyramid_img, curr_pyramid_img);
std::swap(prev_points, curr_points);
}
////////////////////////////////////////////////////////////////////////////
//Деинициализация данных для сопровождения
void deinit()
{
if (curr_img)
{
cvReleaseImage(&curr_img);
curr_img = NULL;
}
if (prev_img)
{
cvReleaseImage(&prev_img);
prev_img = NULL;
}
if (curr_pyramid_img)
{
cvReleaseImage(&curr_pyramid_img);
curr_pyramid_img = NULL;
}
if (prev_pyramid_img)
{
cvReleaseImage(&prev_pyramid_img);
prev_pyramid_img = NULL;
}

points_count = 0;
flags = 0;

if (curr_points)
{
delete []curr_points;
curr_points = NULL;
}
if (prev_points)
{
delete []prev_points;
prev_points = NULL;
}
if (status)
{
delete []status;
status = NULL;
}
}
////////////////////////////////////////////////////////////////////////////
//Сопровождение лица
bool tracking_face(IplImage *gray_img, CvRect &face_rect)
{
cvCopyImage(gray_img, curr_img);

//Вычисление нового положения точек с помощью пирамидального алгоритма анализа оптического потока Лукаса-Канаде
cvCalcOpticalFlowPyrLK(prev_img, curr_img, prev_pyramid_img, curr_pyramid_img,
prev_points, curr_points, points_count, cvSize(20, 20), 3, status, 0, tc, flags);
flags |= CV_LKFLOW_PYR_A_READY;

if (!points_count)
return false;

//Удаление не найденных точек, а также вычисление координат описывающего прямоугольника
double left = std::numeric_limits<double>::max();
double top = std::numeric_limits<double>::max();
double right = std::numeric_limits<double>::min();
double bottom = std::numeric_limits<double>::min();

int k = 0;
for (int i = 0; i < points_count; ++i)
{
if (status[i])
{
curr_points[k] = curr_points[i];

//Вывод точек
cvDrawCircle(gray_img, cvPoint((int)curr_points[k].x, (int)curr_points[k].y), 1, cvScalar(255, 255, 255));

if (curr_points[k].x < left)
left = curr_points[k].x;
if (curr_points[k].x > right)
right = curr_points[k].x;

if (curr_points[k].y < top)
top = curr_points[k].y;
if (curr_points[k].y > bottom)
bottom = curr_points[k].y;

++k;
}
}
points_count = k;

printf("points_count = %i\n", points_count);

face_rect.x = (int)left;
face_rect.y = (int)top;
face_rect.width = (int)(right - left);
face_rect.height = (int)(bottom - top);

//Смена указателей на параметры
std::swap(prev_img, curr_img);
std::swap(prev_pyramid_img, curr_pyramid_img);
std::swap(prev_points, curr_points);

cvShowImage("points", gray_img);

return true;
}
};
////////////////////////////////////////////////////////////////////////////
int main()
{
//Загружаем обученные данные для классификатора
CvHaarClassifierCascade *haar_cascade = (CvHaarClassifierCascade *)cvLoad("c:\\Program Files\\OpenCV_1_1\\data\\haarcascades\\haarcascade_frontalface_alt2.xml", 0, 0, 0);
if (!haar_cascade)
return 1;
CvMemStorage *mem_storage = cvCreateMemStorage(0);

//Захватываем видео (можно и с камеры) с лицами
#if 0
CvCapture *video = cvCaptureFromFile("e:\\video_bmp\\cam_face.avi");
#else
CvCapture *video = cvCaptureFromCAM(0);
#endif
if (!video)
{
cvClearMemStorage(mem_storage);
return 2;
}

cvNamedWindow("frame", 1);
IplImage *gray_img = NULL;

//Прямоугольник найденного лица
CvRect face_rect = cvRect(0, 0, 0, 0);

LKData lk_data;

enum detector_states //Состояния нашего детектора
{
find_face,
track_face
};
detector_states state = find_face;

for (IplImage *frame = cvQueryFrame(video); frame; frame = cvQueryFrame(video))
{
//Вся работа ведётся на изображении в градациях серого
if (!gray_img)
gray_img = cvCreateImage(cvSize(frame->width, frame->height), IPL_DEPTH_8U, 1);
cvCvtColor(frame, gray_img, CV_RGB2GRAY);

//Наш примитивный автомат
switch (state)
{
case find_face: //Поиск лица
if (detect_face(gray_img, haar_cascade, mem_storage, face_rect))
{
//Лицо найдено, инициализируем данные для сопровождения и меняем состояние
lk_data.init(gray_img, face_rect);
state = track_face;
}
break;

case track_face: //Сопровождение лица
if (!lk_data.tracking_face(gray_img, face_rect))
{
//Лицо потеряно, деинициализируем данные сопровождения и меняем состояние
lk_data.deinit();
state = find_face;
}
break;
}

cvDrawRect(frame, cvPoint(face_rect.x, face_rect.y), cvPoint(face_rect.x + face_rect.width, face_rect.y + face_rect.height), cvScalar(255, 0, 0));

cvShowImage("frame", frame);
cvWaitKey(10);
}

cvClearMemStorage(mem_storage);
cvReleaseCapture(&video);
cvDestroyWindow("frame");

return 0;
}
////////////////////////////////////////////////////////////////////////////[/codebox]

P.S. Принимаются от всех заинтересованных лиц идеи по улучшению алгоритма. Идеи (а не реализация по примерам) действительно интересны.

Share this post


Link to post
Share on other sites

Доброго времени суток!

Подскажите алгоритм, как выделить объект движения в рамку.

Поток видео с камеры. Пишут, что легко, а нормального примера не увидел.

#include "stdafx.h"

#include <cv.h>

#include <cxcore.h>

#include <highgui.h>

#include <iostream>

using namespace std;


void processFrame(IplImage*& image);


int main(int argc, char** argv) 

{

 // Инициализируем источник наших изображений.

 // В данном случае - это видеокамера, подключенная по 

 // одному из интерфейсов. 0 означает, что надо

 // выбрать первую попавшуюся камеру. У нас их одна,

 // поэтому не стоит волноваться по этому поводу 

 CvCapture* capture = 0;

 capture = cvCreateCameraCapture(0);

 if (!capture)

 {

  cout << "Initialization failed" << endl;

  return EXIT_FAILURE;

 }


 // Работа с GUI упрощена до невозможного. Необходимо

 // определить идентификатор (по совместительству -

 // заголовок окна), по которому мы будем определять

 // наши окна.

 const char *windowName = "First steps with OpenCV";

 cvNamedWindow(windowName, CV_WINDOW_AUTOSIZE);


 while(true) 

 {

  // Опрашиваем камеру для получения следующего кадра

  IplImage* frame = cvQueryFrame( capture );

  if(!frame) 

   break;


  // processFrame(frame);


  // Отобразим наш фрейм в окне (не забыли, как мы его

  // определили? - через идентификатор)

  cvShowImage(windowName, frame);


  // По нажатию ESC - выход из цикла

  char c = cvWaitKey(33);

  if (c == 27) 

   break;

 }


 // Никогда не забываем освобождать память!

 cvReleaseCapture( &capture );

 cvDestroyWindow(windowName);

 return 0;

}[/code]

Share this post


Link to post
Share on other sites

Советую посмотреть на реализацию стандартных примеров из OpenCV: blobtrack.exe и bgfg_codebook.exe. В основном тебе будет интересен первый пример.

Share this post


Link to post
Share on other sites
Советую посмотреть на реализацию стандартных примеров из OpenCV: blobtrack.exe и bgfg_codebook.exe. В основном тебе будет интересен первый пример.

Exe не запускается. А при компиляции программы выдает эти ошибки:

.\Camera.cpp(1) : warning C4627: #include "cvaux.h": пропущен при поиске использования предкомпилированного заголовка

Добавление директивы в "stdafx.h" или перестройка предкомпилированного заголовка

.\Camera.cpp(2) : warning C4627: #include "highgui.h": пропущен при поиске использования предкомпилированного заголовка

Добавление директивы в "stdafx.h" или перестройка предкомпилированного заголовка

.\Camera.cpp(708) : fatal error C1010: непредвиденный конец файла во время поиска предкомпилированного заголовка. Возможно, вы забыли добавить директиву "#include "stdafx.h"" в источник.

#include "stdafx.h" Даже после того как это ввиду вылазиет еще много разных ошибок.

Может я не так настроил Opencv в C++?

Share this post


Link to post
Share on other sites

Какие ошибки? Может, просто соответствующие *.lib не подключил?

P.S. Русскоязычная студия - это ужас!

Share this post


Link to post
Share on other sites
Какие ошибки? Может, просто соответствующие *.lib не подключил?

P.S. Русскоязычная студия - это ужас!

Да вроде все нормально подключил. У меня сам пример не запускается с ЕХЕшника.

А так вроде норм все. Ну по крайней мере видео с камеры передает. И ни на что не жалуется.

Share this post


Link to post
Share on other sites

Гм, с какой камеры? blobtrack.exe на входе из командной строки получает имя avi-файла с видео (а также параметры алгоритмов). С камерой он не работает. Ты можешь его скомпилировать? Почему стандартный не запускается? Может быть ему просто надо подсунуть майкрософтовскую CRT в ддлках? Или поставить vcredist?

Share this post


Link to post
Share on other sites
Может быть ему просто надо подсунуть майкрософтовскую CRT в ддлках? Или поставить vcredist?

Эээ... Меня маленько это озадачило.

Можно по проще, а точнее, куда сувать dll ?

Share this post


Link to post
Share on other sites

Что такое CRT я думаю ты знаешь. Существует 2 способа собрать .exe:

1. прилинковать к нему CRT в виде статических lib;

2. ничего не прилинковывать, CRT используется из dll (например для 2008-й студии это msvcp80.dll, msvcr80.dll,...).

В первом случае тебе ничего тащить с программой не надо, а во втором эти самые dll. Преимущества и недостатки есть у каждого подхода. Соответственно многие инсталляторы тащат с собой vcredist - майкрософтовский инсталлятор CRT. Или кидают эти dll в папку с программой. В OpenCV 1.1 эти dll лежат в папке bin.

Share this post


Link to post
Share on other sites

Делал, делал... Нефига!

Меня программирование контроллера ждет, а я на этом этапе завис.

Вобщем для полной ясности скажу.

Мне надо чтоб программа определяла движущиеся объекты, определяла их скорость и координату по оси X Y.

Далее на контролер подовалась команда поворота на определенный угол, чтоб объект движения ьыл в центре кадра.

Фото для ясности, а то спешу.

>

Share this post


Link to post
Share on other sites

Программирование контроллера по сравнению с задачей отслеживания объекта поворотной камерой - это ерунда. Даже на стационарной камере точное и быстрое сопровождение сделать непросто. Готовые решения ты вряд ли найдёшь.

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

Share this post


Link to post
Share on other sites

Знаю, что готовых вариантов нет.

Но я примерно себе представляю, как это все должно работать.

С контроллером куда больше примеров чем с opencv. Поэтому с ним мне будет легче.

А вот исходники с видео захватом подойдут любые, я просто в них уже и пропишу команды на контролер.

Проблема все та же... Как поместить движущийся объект в квадратик и измерить его скорость. С координатами сам покумекаю. Главное первых два шага!

Share this post


Link to post
Share on other sites
Проблема все та же... Как поместить движущийся объект в квадратик и измерить его скорость. С координатами сам покумекаю. Главное первых два шага!
Проблема в другом: найти на стационарном изображении движущийся объект - нетрудно. Трудности начнутся, когда ты будешь крутить камерой: смазывание изображения, интерлейсинг, артефакты сжатия... Это для начала.

Потом: раз камера движется, то и задний план будет меняться, сразу отметаются самые популярные алгоритмы вычитания фона. Если ты всё таки захочешь их оставить, то надо будет использовать весьма точное сопоставление и наложение кадров (фактически это задача панорамирования).

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

Короче говоря, это весьма непросто.

P.S. Если хочешь, могу скомпилировать пример blobtrack.exe, чтобы он ничего лишнего не требовал (странно, что у тебя это не получается). Видео брать с камеры или из файла?

Share this post


Link to post
Share on other sites
P.S. Если хочешь, могу скомпилировать пример blobtrack.exe, чтобы он ничего лишнего не требовал (странно, что у тебя это не получается). Видео брать с камеры или из файла?

C камеры.

Я тоже думал о заднем фоне. Разница в том , что он боьше по площади чем движущийся объект в нем.

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

Хоть как то сдвинусь с мертвой точки!

Share this post


Link to post
Share on other sites
бы очень признателен за компиляцию blobtrack.exe )))

Хоть как то сдвинусь с мертвой точки!

Вот здесь. Там есть папка release, запускай через bat-файл, в котором прописаны параметры используемых алгоритмов. Их можно (и нужно!) менять, при разных комбинациях параметров будет разное качество и скорость работы програмы. Видео берёт с первой камеры в системе.

Share this post


Link to post
Share on other sites

А вариант использования 2х или 3х камер с известной геометрией их расположения?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×