作为即将踏入图像识别、目标跟踪领域的一名研究生,先从基础的学起,本文采用了经典的mean-shift算法,思路简单,实现的思路来源于一个网址:http://zhidao.baidu.com/link?url=v2PlqAHX45kjCWJUnSsZYBwHkPVCX8vp6oIZnRXV7IKG0phmuy0vwQ02_SRBgK1OLieVctpFJHR1cGoaxlDAIK,得到的跟踪结果较为勉强。
meanshift算法是一种密度函数梯度估计的非参数方法,通过迭代寻优找到概率分布的极值来定位目标。算法的步骤如下:(1)选择搜索窗(2)计算得窗口的重心(3)将窗口的重心设置在计算出的重心处(4).移动搜索窗的中心到重心,如果移动距离大于预设的固定阈值,则重复2)3)4),直到搜索窗的中心与质心间的移动距离小于预设的固定阈值,或者循环运算的次数达到某一最大值,停止计算。关于meanshift的收敛性证明可以google相关文献。
Opencv中实现该算法的函数是cvMeanShift( const CvArr* prob_image ,CvRect window, CvTermCriteria criteria, CvConnectedComp* comp) ,prob_image为单通道图像,应为由只含目标的图像经过计算H分量直方图、计算直方图的方向投影得到的概率图像,window为指定搜索初始位置的窗口,criteria为终止条件,主要由mean-shift移动的最大迭代次数和可视为窗口位置收敛的最小移动距离组成,comp->rect中包含收敛后搜索窗口的位置,而comp->area包含了窗口内部所有像素和。
具体代码
我的代码是基于VS2010+ OpenCV2.4.6的。代码可以读入视频,也可以读摄像头,两者的选择只需要在代码中稍微修改即可。无论选择何种,首先打开了视频后,需要由鼠标手动画圆选择视频中要跟踪的目标,由于我的摄像头分辨率低,采用手机拍摄视频后读入。跟踪目标的纹理与周围纹理差别较大时,效果较好吧。
具体代码:
#include "cv.h" #include "highgui.h" #include <cxcore.h> void my_mouse_callback(int event ,int x,int y,int flags ,void* param); void getTarget(IplImage* src ,CvRect rect); //得到目标图像 bool drawing_circle = false; int circle_r = 0; CvPoint circlr_center =cvPoint(0,0); int circle_x = 0; int circle_y = 0; bool flag = false; int getting_target = 0; int main() { CvCapture* capture = cvCreateFileCapture("testMoving.avi"); if (!capture) { printf("Can not open the file./n"); return -1; } IplImage* frames ; frames = cvQueryFrame(capture); cvNamedWindow("Test Meanshift" ,1); cvSetMouseCallback( "Test Meanshift" ,my_mouse_callback,(void*)frames ); CvRect rect; IplImage* pFrames = cvCreateImage(cvGetSize(frames) ,frames->depth ,3); CvHistogram* hist; IplImage* result=cvCreateImage(cvGetSize(frames),IPL_DEPTH_8U,1); cvZero(result); CvConnectedComp comp ; IplImage* srcImage =cvCreateImage(cvGetSize(frames),IPL_DEPTH_8U,1); CvBox2D box; while(1) { if(flag) { cvCircle(frames ,circlr_center ,circle_r ,CV_RGB(255 ,0,0),3,8,0); rect = cvRect(circlr_center.x-circle_r,circlr_center.y-circle_r,2*circle_r ,2*circle_r); if(getting_target) { pFrames = cvCloneImage(frames); getTarget(pFrames ,rect); IplImage* target_hsv =cvCreateImage( cvGetSize(pFrames), IPL_DEPTH_8U, 3 ); IplImage* target_hue =cvCreateImage( cvGetSize(pFrames), IPL_DEPTH_8U, 1 ); cvCvtColor(pFrames,target_hsv,CV_BGR2HSV); //转化到HSV空间 cvSplit( target_hsv, target_hue, NULL, NULL, NULL ); //获得H分量 IplImage* h_plane=cvCreateImage( cvGetSize(target_hsv),IPL_DEPTH_8U,1 ); int hist_size[]={255}; //将H分量的值量化到[0,255] float h_ranges[] ={0,360}; float* ranges[] = {h_ranges}; //H分量的取值范围是[0,360) hist= cvCreateHist(1,hist_size, CV_HIST_ARRAY ,ranges ,1); cvCalcHist(&target_hue, hist, 0, NULL); //计算得直方图hist getting_target = 0; cvReleaseImage(&target_hsv); cvReleaseImage(&target_hue); } cvCvtColor(frames ,srcImage ,CV_BGR2GRAY); cvCalcBackProject(&srcImage,result,hist); cvMeanShift(result ,rect ,cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS ,20 ,0.3),&comp ); circlr_center = cvPoint(comp.rect.x+0.5*comp.rect.width ,comp.rect.y+0.5*comp.rect.height); circle_r = comp.rect.width/2 ; } cvShowImage( "Test Meanshift" ,frames); if(cvWaitKey(33) == 27) break; frames = cvQueryFrame(capture); } return 0; } void my_mouse_callback(int event ,int x,int y,int flags ,void* param) { IplImage* images = (IplImage* )param; switch(event) { case CV_EVENT_MOUSEMOVE: { if (drawing_circle == true) { circlr_center = cvPoint((x+circle_x)/2 ,(y+circle_y)/2); circle_r = (x-circle_x)/2; } } break; case CV_EVENT_LBUTTONDOWN: { drawing_circle = true ; circle_x = x; circle_y = y; } break; case CV_EVENT_LBUTTONUP: { drawing_circle = false; flag = true ; getting_target = 1; } break; } } void getTarget(IplImage* src ,CvRect rect) { for(int i=0;i<src->width ;i++) for(int j = 0; j<src->height ;j++) { if( i<rect.x || i>(rect.x+rect.width)) { if( j<rect.y ||j>(rect.y+rect.height)) cvSet2D(src ,j , i,cvScalarAll(255)); } } }