Добрый день!
Решаю следующую задачу:
на вход программе подается видеофайл, на котором актер с нарисованными синим маркером на лице 64 точками говорит что-то, изображая одну из эмоций(злость, грусть, радость и т.д.). Необходимо сделать трэкинг этих точек и на выходе дать файл с координатами каждой точки в каждом кадре.
На данный момент имею следующее:
//03.04.13
#include <OpenCV/highgui.h>
#include <OpenCV/cv.h>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
char * itoa( int value, char * str, int base )
{
char * rc;
char * ptr;
char * low;
// Check for supported base.
if ( base < 2 || base > 36 )
{
*str = '\0';
return str;
}
rc = ptr = str;
// Set '-' for negative decimals.
if ( value < 0 && base == 10 )
{
*ptr++ = '-';
}
// Remember where the numbers start.
low = ptr;
// The actual conversion.
do
{
// Modulo is negative for negative value. This trick makes abs() unnecessary.
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + value % base];
value /= base;
} while ( value );
// Terminating the string.
*ptr-- = '\0';
// Invert the numbers.
while ( low < ptr )
{
char tmp = *low;
*low++ = *ptr;
*ptr-- = tmp;
}
return rc;
}
// задаём ROI
int x = 85;
int y = 5;
int width = 170;
int height = 185;
// добавочная величина
int add = 00;
// устанавливаем ROI
// cvSetImageROI(frame, cvRect(x,y,width,height));
// сбрасываем ROI
// cvResetImageROI(frame);
/*CvScalar s;
s=cvGet2D(imgThresh,centerX,centerY); // get the (i,j) pixel value
s.val[0]=180;
s.val[1]=100;
s.val[2]=100;
cvSet2D(imgThresh,centerX,centerY,s); // set the (i,j) pixel value
*/
//cvScalar(75,0,50), cvScalar(150,165,195)
//This function threshold the ROI of HSV image and create a binary image
IplImage* GetThresholdedImage(IplImage* imgHSV){
IplImage* imgThresh=cvCreateImage(cvGetSize(imgHSV),IPL_DEPTH_8U, 1);
cvZero(imgThresh);
cvSetImageROI(imgHSV, cvRect(x,y,width,height));
cvSetImageROI(imgThresh, cvRect(x,y,width,height));
cvInRangeS(imgHSV, cvScalar(75,5,50), cvScalar(150,165,195), imgThresh);//(src, lower[3], upper[3])
cvResetImageROI(imgThresh);
cvResetImageROI(imgHSV);
return imgThresh;
}
void GetCentralPoints(CvSeq* contours){
CvSeq *contour;
CvMoments moments;
double M00, M01, M10;
int centerX, centerY, pointnum;
contour = contours; //Now contour points to the first contour in the list
pointnum = 1;
while(contour){
cvMoments(contour,&moments);
M00 = cvGetSpatialMoment(&moments,0,0);
M10 = cvGetSpatialMoment(&moments,1,0);
M01 = cvGetSpatialMoment(&moments,0,1);
centerX = (int)(M10/M00);
centerY = (int)(M01/M00);
std::cout << "point№" << pointnum << "\n";
//printf("X=", centerX);
std::cout << "X=" << centerX << " ";
//printf("\n");
std::cout << "Y=" << centerY << "\n";
//printf("Y=", centerY);
//printf("\n");
contour = (CvSeq *)(contour->h_next);
pointnum = pointnum+1;
}
}
int main(){
CvCapture* capture =0;
//capture = cvCaptureFromCAM(0);
capture = cvCaptureFromAVI("/Users/agortinskiy/Downloads/AudioVisualClip/DC/a1.avi");
CvMemStorage* g_storage = NULL;
CvSeq* contours = 0;
if(!capture){
printf("Capture failure\n");
return -1;
}
IplImage* frame=0;
frame = cvQueryFrame(capture);
if(!frame) return -1;
cvNamedWindow("Original");
cvNamedWindow("Contour");
cvNamedWindow("Binary");
//iterate through each frames of the video
while(true){
frame = cvQueryFrame(capture);
if(!frame) break;
frame=cvCloneImage(frame);
//cvSmooth(frame, frame, CV_BILATERAL,3,3,0.1,0.1); //smooth the original image using Gaussian kernel
cvSmooth(frame, frame, CV_GAUSSIAN, 3, 3);
IplImage* imgHSV = cvCreateImage(cvGetSize(frame), IPL_DEPTH_8U, 3);
cvCvtColor(frame, imgHSV, CV_BGR2HSV); //Change the color format from BGR to HSV
IplImage* imgThresh = GetThresholdedImage(imgHSV);
//cvSmooth(imgThresh, imgThresh, CV_BILATERAL, 3, 3, 0.1, 0.1); //smooth the binary image using Gaussian kernel
cvSmooth(imgThresh, imgThresh, CV_GAUSSIAN, 3, 3);
if( g_storage==NULL ) {
g_storage = cvCreateMemStorage(0);
} else {
cvClearMemStorage( g_storage );
}
cvShowImage("Binary", imgThresh);
cvFindContours( imgThresh, g_storage,&contours,sizeof(CvContour),CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE,cvPoint(0,0));
GetCentralPoints(contours);
cvZero( imgThresh );
// для отметки контуров
CvFont font;
cvInitFont(&font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0);
char buf[128];
int counter=0;
/* if( contours ){
cvDrawContours(
imgThresh,
contours,
cvScalarAll(255),
cvScalarAll(255),
100
);
// выводим его номер
CvPoint2D32f point; float rad;
cvMinEnclosingCircle(contours,&point,&rad); // получим окружность содержащую контур
cvPutText(imgThresh, itoa(++counter, buf, 10), cvPointFrom32f(point), &font, CV_RGB(255,255,255));
}
*/
// нарисуем контуры изображения
if(contours){
for(CvSeq* seq0 = contours;seq0!=0;seq0 = seq0->h_next){
// рисуем контур
cvDrawContours(imgThresh, seq0, cvScalarAll(255),
cvScalarAll(255), 0, 1, 8);
// выводим его номер
CvPoint2D32f point; float rad;
cvMinEnclosingCircle(seq0,&point,&rad); // получим окружность содержащую контур
cvPutText(imgThresh, itoa(++counter, buf, 10), cvPointFrom32f(point), &font, cvScalarAll(255));
}
}
cvShowImage("Contour", imgThresh);
cvShowImage("Original", frame);
//Clean up used images
cvReleaseImage(&imgHSV);
cvReleaseImage(&imgThresh);
cvReleaseImage(&frame);
//Wait 50mS
int c = cvWaitKey(10);
//If 'ESC' is pressed, break the loop
if((char)c==27 ) break;
}
cvDestroyAllWindows() ;
cvReleaseCapture(&capture);
return 0;
}
Программа уже преобразует изображение в бинарное с выбранным порогом в hsv цветовой палитре, затем выделяются контуры, считаются моменты для вычисления центров контуров(и есть координаты искомые мной), ну и нумеруются точки.
Проблемы 2:
1. Т.к. поиск контуров производится на каждом кадре независимо от предыдущего кадра, то и точки нумеруются в разном порядке, что неверно в условиях моей задачи. Каким образом можно закрепить порядок следования точек от кадра к кадру?
2. Ошибочно детектируются глаза на некоторых кадрах. Видимо, при некоторых положениях головы актера цветовые параметры глаза укладываются в диапозон, заданный для отбора синих точек. Как можно исключить глаза?