作为即将踏入图像识别、目标跟踪领域的一名研究生,先从基础的学起,本文采用了经典的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
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;iwidth ;i++)
for(int j = 0; jheight ;j++)
{
if( i(rect.x+rect.width))
{
if( j(rect.y+rect.height))
cvSet2D(src ,j , i,cvScalarAll(255));
}
}
}