Created
July 23, 2013 16:48
-
-
Save ritsz/6064012 to your computer and use it in GitHub Desktop.
Median flow tracker with Applied condensation algorithm.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <cv.h> | |
| #include <highgui.h> | |
| #include <ml.h> | |
| #include <math.h> | |
| #include <algorithm> | |
| #include <vector> | |
| #include <stdio.h> | |
| #include <iostream> | |
| #include <cxcore.h> | |
| #include <cvaux.h> | |
| #include <cmath> | |
| #include <pthread.h> | |
| using namespace cv; | |
| using namespace std; | |
| vector<CvPoint> trail; | |
| float median; | |
| bool fast = false; | |
| const int winHeight=600; | |
| const int winWidth=800; | |
| static CvMemStorage* storage = 0; // Dynamically growing memory for calculations | |
| static CvHaarClassifierCascade* cascade = 0; | |
| const char* cascade_name = "/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt.xml"; | |
| inline double euclid_dist(const CvPoint2D32f* point1, const CvPoint2D32f* point2) { | |
| //this function calculates the euclidean distance between 2 points | |
| double distance, xvec, yvec; | |
| xvec = point2->x - point1->x; | |
| yvec = point2->y - point1->y; | |
| distance = sqrt((xvec * xvec) + (yvec * yvec)); | |
| return distance; | |
| } | |
| void pairwise_dist(const CvPoint2D32f* features, double *edist, int npoin) { | |
| //calculate m x n euclidean pairwise distance matrix. | |
| for (int i = 0; i < npoin; i++) { | |
| for (int j = 0; j < npoin; j++) { | |
| int ind = npoin*i + j; | |
| edist[ind] = euclid_dist(&features[i],&features[j]); | |
| } | |
| } | |
| } | |
| void ncc_filter(IplImage *frame1, IplImage *frame2, CvPoint2D32f *prev_feat, CvPoint2D32f *curr_feat, | |
| int npoin, int method, IplImage *rec0, IplImage *rec1, IplImage *res, int *ncc_pass) { | |
| int filt = npoin/2; | |
| vector<float> ncc_err (npoin,0.0); | |
| for (int i = 0; i < npoin; i++) { | |
| cvGetRectSubPix( frame1, rec0, prev_feat[i] ); | |
| cvGetRectSubPix( frame2, rec1, curr_feat[i] ); | |
| cvMatchTemplate( rec0,rec1, res, method ); | |
| ncc_err[i] = ((float *)(res->imageData))[0]; | |
| } | |
| vector<float> err_copy (ncc_err); | |
| sort(ncc_err.begin(), ncc_err.end()); | |
| double median = (ncc_err[filt]+ncc_err[filt-1])/2.; | |
| for(int i = 0; i < npoin; i++) { | |
| if (err_copy[i] >= median) { | |
| ncc_pass[i] = 1; | |
| } | |
| else { | |
| ncc_pass[i] = 0; | |
| } | |
| } | |
| } | |
| void fb_filter(const CvPoint2D32f* prev_features, const CvPoint2D32f* backward_features, | |
| const CvPoint2D32f* curr_feat, int *fb_pass, const int npoin) { | |
| //this function implements forward-backward error filtering | |
| vector<double> euclidean_dist (npoin,0.0); | |
| int filt = npoin/2; | |
| for(int i = 0; i < npoin; i++) { | |
| euclidean_dist[i] = euclid_dist(&prev_features[i], &backward_features[i]); | |
| } | |
| vector<double> err_copy (euclidean_dist); | |
| //use the STL sort algorithm to filter results | |
| sort(euclidean_dist.begin(), euclidean_dist.end()); | |
| median = (euclidean_dist[filt]+euclidean_dist[filt-1])/2.; | |
| for(int i = 0; i < npoin; i++) { | |
| if (err_copy[i] <= median) { | |
| fb_pass[i] = 1; | |
| } | |
| else { | |
| fb_pass[i] = 0; | |
| } | |
| } | |
| } | |
| void bbox_move(const CvPoint2D32f* prev_feat, const CvPoint2D32f* curr_feat, const int npoin, | |
| double &xmean, double &ymean) { | |
| /*Calculate bounding box motion. */ | |
| vector<double> xvec (npoin,0.0); | |
| vector<double> yvec (npoin,0.0); | |
| for (int i = 0; i < npoin; i++) { | |
| xvec[i] = curr_feat[i].x - prev_feat[i].x; | |
| yvec[i] = curr_feat[i].y - prev_feat[i].y; | |
| } | |
| sort(xvec.begin(), xvec.end()); | |
| sort(yvec.begin(), yvec.end()); | |
| xmean = xvec[npoin/2]; | |
| ymean = yvec[npoin/2]; | |
| } | |
| int main( int argc,char** argv) | |
| { | |
| const int stateNum = 4; | |
| const int measureNum = 2; | |
| const int sampleNum = 2000; | |
| CvConDensation* condens = cvCreateConDensation(stateNum,measureNum,sampleNum); | |
| CvMat* lowerBound; | |
| CvMat* upperBound; | |
| lowerBound = cvCreateMat(stateNum, 1, CV_32F); | |
| upperBound = cvCreateMat(stateNum, 1, CV_32F); | |
| cvmSet(lowerBound,0,0,0.0 ); | |
| cvmSet(upperBound,0,0,winWidth ); | |
| cvmSet(lowerBound,1,0,0.0 ); | |
| cvmSet(upperBound,1,0,winHeight ); | |
| cvmSet(lowerBound,2,0,0.0 ); | |
| cvmSet(upperBound,2,0,0.0 ); | |
| cvmSet(lowerBound,3,0,0.0 ); | |
| cvmSet(upperBound,3,0,0.0 ); | |
| float A[stateNum][stateNum] ={ | |
| 1,0,1,0, | |
| 0,1,0,1, | |
| 0,0,1,0, | |
| 0,0,0,1 | |
| }; | |
| memcpy(condens->DynamMatr,A,sizeof(A)); | |
| cvConDensInitSampleSet(condens, lowerBound, upperBound); | |
| CvCapture* capture = cvCreateCameraCapture(0); | |
| cascade = (CvHaarClassifierCascade*)cvLoad(cascade_name,0,0,0); | |
| if(!cascade){ fprintf(stderr,"ERROR: Cascade not loaded!! \nCheck the path given for it on line 17 of code \n"); return -1; } // Check whether cascades have been loaded | |
| if(!capture){ fprintf(stderr,"ERROR: Camera not loaded \n"); return -1;} // Check whether camera has been loaded | |
| storage = cvCreateMemStorage(0); | |
| // Loop to get the Bounding Box from haar cascades | |
| IplImage* frame = 0; | |
| IplImage* displayed_image = 0; | |
| CvPoint pt1,pt2; | |
| CvPoint pt; | |
| int i; | |
| detect:cvClearMemStorage(storage); | |
| cvNamedWindow("Results",CV_WINDOW_AUTOSIZE); | |
| cvMoveWindow("Results",700,0); | |
| int pcount_prev = 1; | |
| while(1) | |
| { | |
| frame = cvQueryFrame(capture); | |
| if(!frame) { fprintf(stderr,"Camera doesn't return frames \n"); return -1;} | |
| displayed_image = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,3); | |
| cvCopy(frame,displayed_image,NULL); | |
| CvSeq* faces = cvHaarDetectObjects(frame,cascade,storage,1.1,2,CV_HAAR_DO_CANNY_PRUNING,cvSize(40,40)); | |
| for(i = 0;i<(faces? faces->total:0); i++) | |
| { | |
| CvRect* r = (CvRect*)cvGetSeqElem(faces,i); | |
| pt1.x = r->x; | |
| pt2.x = r->x + r->width; | |
| pt1.y = r->y; | |
| pt2.y = r->y + r->height; | |
| cvRectangle(displayed_image,pt1,pt2,CV_RGB(255,0,0),3,8,0); | |
| } | |
| if(fast) { fast = false ;break; } | |
| else cvShowImage("Results",displayed_image); | |
| char bar = cvWaitKey(33); | |
| if(bar == 32) break; | |
| } | |
| static IplImage *frame1_1C = NULL, *frame2_1C = NULL, *pyramid1 = NULL, *pyramid2 = NULL; | |
| frame1_1C = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1); | |
| frame2_1C = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1); | |
| int npoints = 400; | |
| int pcount; | |
| int winsize = 30; | |
| IplImage *rec0 = cvCreateImage( cvSize(winsize, winsize), 8, 1 ); | |
| IplImage *rec1 = cvCreateImage( cvSize(winsize, winsize), 8, 1 ); | |
| IplImage *res = cvCreateImage( cvSize( 1, 1 ), IPL_DEPTH_32F, 1 ); | |
| /* This array will contain the locations of the points from frame 1 in frame 2. */ | |
| vector<CvPoint2D32f> frame1_features(npoints); | |
| vector<CvPoint2D32f> frame2_features(npoints); | |
| vector<CvPoint2D32f> FB_features(npoints); | |
| /* The i-th element of this array will be non-zero if and only if the i-th feature of | |
| * frame 1 was found in frame 2. | |
| */ | |
| char optical_flow_found_feature[npoints]; | |
| char optical_flow_found_feature2[npoints]; | |
| float optical_flow_feature_error[npoints]; | |
| vector<int> fb_pass(npoints); | |
| vector<int> ncc_pass(npoints); | |
| double bbox_x = pt1.x; | |
| double bbox_y = pt1.y; | |
| double bbox_width = (pt2.x - pt1.x); | |
| double bbox_height = (pt2.y - pt1.y ); | |
| printf("\n\tRectangle made at (%g,%g) and size (%g,%g)\n\tWe have got a Bounding Box\n\n",bbox_x,bbox_y,bbox_width,bbox_height); | |
| bool isPredictOnly=false; | |
| //Main Loop for tracking | |
| while(true) | |
| { | |
| cvConvertImage(frame,frame1_1C,0); | |
| //Making 400 new points for PyrLK to track | |
| for(i = 0;i<20;i++) | |
| { | |
| for(int j = 0;j<20;j++) | |
| { | |
| int l = i*20 + j; | |
| frame1_features[l].x = bbox_x + (bbox_width/20)*i + (bbox_width/40); | |
| frame1_features[l].y = bbox_y + (bbox_height/20)*j + (bbox_height/40); | |
| } | |
| } | |
| // 400 new feature points made | |
| //Second Frame Capture, converted to grey scale and displayed | |
| frame = cvQueryFrame(capture); | |
| cvConvertImage(frame,frame2_1C,0); | |
| cvCopy(frame,displayed_image,NULL); | |
| // Pyr Lucas kanade Optical Flow | |
| CvTermCriteria term = cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.3); | |
| pyramid1 = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1); | |
| pyramid2 = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1); | |
| cvCalcOpticalFlowPyrLK(frame1_1C,frame2_1C,pyramid1,pyramid2,&frame1_features[0],&frame2_features[0],npoints,cvSize(3,3),5,optical_flow_found_feature,optical_flow_feature_error,term,0); | |
| cvCalcOpticalFlowPyrLK(frame2_1C,frame1_1C,pyramid1,pyramid2,&frame2_features[0],&FB_features[0] ,npoints,cvSize(3,3),5,optical_flow_found_feature2,optical_flow_feature_error,term,0); | |
| double xmean = 0; | |
| double ymean = 0; | |
| //filter the cascade | |
| fb_filter(&frame1_features[0], &FB_features[0], &frame2_features[0], &fb_pass[0], npoints); | |
| ncc_filter(frame1_1C,frame2_1C,&frame1_features[0],&frame2_features[0], | |
| npoints,CV_TM_CCOEFF_NORMED, rec0, rec1, res, &ncc_pass[0]); | |
| pcount = 0; | |
| for(int i = 0; i<npoints;i++) | |
| { | |
| if(fb_pass[i] && ncc_pass[i]) | |
| { | |
| pcount++; | |
| } | |
| } | |
| float scaled = 1; | |
| if( (pcount/pcount_prev < 2) && (pcount/pcount_prev > 0) ) | |
| { | |
| scaled = pcount/pcount_prev; | |
| } | |
| pcount_prev = pcount; | |
| fprintf(stderr,"Passed Points %d ### median %f \n",pcount , median ); | |
| if(pcount == 0) { fprintf(stderr," No point tracked currently: EXITING"); return -1; } | |
| vector<CvPoint2D32f> curr_features2(pcount),prev_features2(pcount); | |
| int j = 0; | |
| for( i = 0; i< npoints; i++) | |
| { | |
| if(fb_pass[i] && ncc_pass[i]) | |
| { | |
| curr_features2[j] = frame2_features[i]; | |
| prev_features2[j] = frame1_features[i]; | |
| j++; | |
| } | |
| } | |
| int n2 = pcount*pcount; | |
| vector<double> pdist_prev(n2),pdist_curr(n2),pdiv(n2); | |
| pairwise_dist(&prev_features2[0],&pdist_prev[0],pcount); | |
| pairwise_dist(&curr_features2[0],&pdist_curr[0],pcount); | |
| for (int i = 0; i < n2; i++) { | |
| if (pdist_prev[i] > 0.0) { | |
| pdiv[i] = pdist_curr[i]/pdist_prev[i]; | |
| } | |
| } | |
| sort(pdiv.begin(),pdiv.end()); | |
| //fprintf(stderr, "box_scale: %3.4f", pdiv[n2/2]); | |
| double box_scale; | |
| box_scale = pdiv[n2/2]; | |
| double track_x,track_y; | |
| if(median > 100.0){fast = true; goto detect;} | |
| bbox_move(&prev_features2[0],&curr_features2[0],pcount,xmean,ymean); | |
| bbox_x = bbox_x + (xmean) - bbox_width*(box_scale - 1.)/2.; | |
| bbox_y = bbox_y + (ymean) - bbox_height*(box_scale - 1.)/2.; | |
| bbox_width = bbox_width * (box_scale); | |
| bbox_height = bbox_height * (box_scale); | |
| track_x = bbox_x + (bbox_width)/2; | |
| track_y = bbox_y + (bbox_height)/2; | |
| pt.x = track_x; | |
| pt.y = track_y; | |
| trail.push_back(cvPoint(track_x,track_y)); | |
| if(trail.size() >= 30) | |
| { | |
| trail.erase(trail.begin()); | |
| } | |
| for( int b = 0; b<trail.size() - 1; b++) | |
| { | |
| cvLine(displayed_image,trail[i],trail[i+1],cvScalar(0,0,255),2); | |
| } | |
| CvPoint predict_pt=cvPoint((int)condens->State[0],(int)condens->State[1]); | |
| float variance[measureNum]={0}; | |
| //get variance/standard deviation of each state | |
| for (i=0;i<measureNum;i++) { | |
| //sum | |
| float sumState=0; | |
| for (int j=0;j<condens->SamplesNum;j++) { | |
| sumState+=condens->flSamples[i][j]; | |
| } | |
| //average | |
| sumState/=sampleNum; | |
| //variance | |
| for (int j=0;j<condens->SamplesNum;j++) { | |
| variance[i]+=(condens->flSamples[i][j]-sumState)* | |
| (condens->flSamples[i][j]-sumState); | |
| } | |
| variance[i]/=sampleNum-1; | |
| } | |
| //3.update particals confidence | |
| if (isPredictOnly) { | |
| pt=predict_pt; | |
| }else{ | |
| pt=cvPoint(track_x,track_y); | |
| } | |
| for (int i=0;i<condens->SamplesNum;i++) { | |
| float probX=(float)exp(-1*(pt.x-condens->flSamples[i][0]) | |
| *(pt.x-condens->flSamples[i][0])/(2*variance[0])); | |
| float probY=(float)exp(-1*(pt.y-condens->flSamples[i][1]) | |
| *(pt.y-condens->flSamples[i][1])/(2*variance[1])); | |
| condens->flConfidence[i]=probX*probY; | |
| } | |
| //4.update condensation | |
| cvConDensUpdateByTime(condens); | |
| cvRectangle(displayed_image, cvPoint(bbox_x, bbox_y), | |
| cvPoint(bbox_x+bbox_width,bbox_y+bbox_height), | |
| cvScalar(0xff,0x00,0x00) ); | |
| for (int i = 0; i < pcount; i++) { | |
| cvCircle(displayed_image, cvPoint(curr_features2[i].x, curr_features2[i].y), 1, cvScalar(255,255,255)); | |
| } | |
| //cvCircle(displayed_image,cvPoint(track_x,track_y),5,cvScalar(0,0,255),5); | |
| char online_dist[] = "0.0"; | |
| CvFont bfont; | |
| double hscale = 0.5; | |
| double vscale = 0.5; | |
| cvInitFont(&bfont, CV_FONT_HERSHEY_SIMPLEX, hscale, vscale,0,1); | |
| cvPutText(displayed_image, online_dist, cvPoint(bbox_x+bbox_width + 20,bbox_y), &bfont, cvScalar(0,0,255)); | |
| if (isPredictOnly) { | |
| pt=predict_pt; | |
| }else{ | |
| pt=cvPoint(track_x,track_y); | |
| } | |
| for (int i=0;i<condens->SamplesNum;i++) { | |
| float probX=(float)exp(-1*(pt.x-condens->flSamples[i][0]) | |
| *(pt.x-condens->flSamples[i][0])/(2*variance[0])); | |
| float probY=(float)exp(-1*(pt.y-condens->flSamples[i][1]) | |
| *(pt.y-condens->flSamples[i][1])/(2*variance[1])); | |
| condens->flConfidence[i]=probX*probY; | |
| } | |
| if(track_x >600 || track_x<20) track_x = pt.x; | |
| trail.push_back(pt); | |
| if(trail.size() == 10) | |
| { | |
| trail.erase(trail.begin()); | |
| } | |
| for( int b = 0; b<trail.size() - 1; b++) | |
| { | |
| cvLine(displayed_image,trail[i],trail[i+1],cvScalar(255,0,0),2); | |
| } | |
| cvCircle(displayed_image,predict_pt,5,CV_RGB(0,255,0),3);//predicted point with green | |
| cvCircle(displayed_image,cvPoint(track_x,track_y),5,CV_RGB(0,0,255),3); | |
| cvShowImage("Results",displayed_image); | |
| char esc = cvWaitKey(33); | |
| if(esc == 27) break; | |
| if(esc == 32) goto detect; | |
| } // End of Main While Loop | |
| cvReleaseImage(&frame); | |
| cvDestroyWindow("Results"); | |
| cvReleaseImage(&frame1_1C); | |
| cvReleaseImage(&frame2_1C); | |
| cvReleaseImage(&pyramid1); | |
| cvReleaseImage(&pyramid2); | |
| cvReleaseCapture(&capture); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment