0 前言
最近一直在看关于目标跟踪方面的算法实现,也是时候整理下思路看看怎么实现了。 这次我将带领大家看看基于
OpenCV的目标跟踪算法及其基本实现。由于目标跟踪方法众多,我将分为几次讲解逐个讲解。当然只是起个索引的
效果,要好的跟踪实现有待自己去深化。
概述
目标跟踪作为Machine Learning的一个重要分支,加之其广泛的应用(行人、军事中飞机导弹等),对其的研究
受到国内外学者的喜爱。
总的来说整个目标跟踪过程可分为目标特征提取和目标跟踪两部分。
其中目标特征提取又可以细分为以下几种:
1. 各种色彩空间直方图,利用色彩空间的直方图分布作为目标跟踪的特征的一个显著性特点是可以减少物体远
近距离对跟踪的影响,因为其颜色分布大致相同。
2.轮廓特征,提取目标的轮廓特征不但可以加快算法的速度还可以在目标有小部分影响的情况下同样有效果。
3. 纹理特征,如果被跟踪目标是有纹理的,根据其纹理特征来跟踪,效果会有所改善。
当前目标跟踪算法大体可分为以下七种
1.质心跟踪算法(Centroid):这种跟踪方式用于跟踪有界目标如飞机,目标完全包含在摄像机的视场范围内,
对于这种跟踪方式可选用一些预处理算法:如白热(正对比度)增强、黑热(负对比度)增强,和基于直方图的统计
(双极性)增强。
2.多目标跟踪算法(MTT):多目标跟踪用于有界目标如飞机、地面汽车等。它们完全在跟踪窗口内。在复杂环境里
的小目标跟踪MMT能给出一个较好的性能
3.相关跟踪算法(Correlation):相关可用来跟踪多种类型的目标,当跟踪目标无边界且动态不是很强时这种方式
非常有效。典型应用于:目标在近距离的范围,且目标扩展到摄像机视场范围外,如一艘船。
4.边缘跟踪算法(Edge):当跟踪目标有一个或多个确定的边缘而同时却又具有不确定的边缘,这时边缘跟踪是最有
效的算法。典型地火箭发射,它有确定好的前边缘,但尾边缘由于喷气而不定
5.相位相关跟踪算法(Phase Correlation):相位相关算法是非常通用的算法,既可以用来跟踪无界目标也可以用
来跟踪有界目标。在复杂环境下(如地面的汽车)能给出一个好的效果。
6.场景锁定算法(SceneLock):该算法专门用于复杂场景的跟踪。适合于空对地和地对地场景。这个算法跟踪场景
中的多个目标,然后依据每个点的运动,从而估计整个场景全局运动,场景中的目标和定位是自动选择的。当存在跟踪点
移动到摄像机视场外时,新的跟踪点能自动被标识。瞄准点初始化到场景中的某个点,跟踪启动,同时定位瞄准线。在这
种模式下,能连续跟踪和报告场景里的目标的位置
7.组合(Combined)跟踪算法:顾名思义这种跟踪方式是两种具有互补特性的跟踪算法的组合:相关类算法 +质
心类算法。它适合于目标尺寸、表面、特征改变很大的场景(如小船在波涛汹涌的大海里行驶)。
基于质心的跟踪算法大家可以参考《具有记忆跟踪功能的质心跟踪算法》刘士建,吴滢跃 这篇文章.从这篇文章中我们
可以看出本算法实现的基本思路是通过对目标的位置、面积、灰度和形状等信息来判断目标是否被遮挡,如被遮挡则对目
标的位置进行预测,直至目标重新出现。这种实现不仅使算法具有记忆跟踪的功,还使改进算法基本不受目标大小、旋转
变化的影响,提高了跟踪算法的稳定性和可靠性下面是该算法的代码实现
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
/********gloable variable**********/
bool isFirstFrame = true;
bool drawBox = false;
bool isChooseObj = false;
Rect box;//tracking object
Rect roi;//
float w = 0.4;//
vector > contours;
vector hierarchy;
/**********************************************************
* input:
* return : NULL
* Description: mouse callback function
**********************************************************/
static void mouse_callback(int event, int x, int y, int, void *)
{
switch( event )
{
case CV_EVENT_MOUSEMOVE:
if (drawBox)
{
box.width = x-box.x;
box.height = y-box.y;
}
break;
case CV_EVENT_LBUTTONDOWN:
drawBox = true;
box = Rect(x, y, 0, 0);
break;
case CV_EVENT_LBUTTONUP:
drawBox = false;
if( box.width < 0 )
{
box.x += box.width;
box.width *= -1;
}
if( box.height < 0 )
{
box.y += box.height;
box.height *= -1;
}
isChooseObj = true;
break;
}
}
/**********************************************************
* input:
* image---the image need to segment
* return:
* threshold--segment value
* Description: used to get the threshold for image binary
**********************************************************/
int getThresholeValue(cv::Mat& image)
{
assert(image.channels() == 1);
int width = image.cols ;
int height = image.rows ;
int x = 0,y = 0;
int pixelCount[256] = { 0 };
float pixelPro[256] = { 0 };
int i, j, pixelSum = width * height, threshold = 0;
uchar* data = image.ptr(0);
//count every pixel number in whole image
for(i = y; i < height; i++)
{
for(j = x;j deltaMax)
{
deltaMax = deltaTmp;
threshold = i;
}
}
//return the best threshold;
return threshold;
}
/**********************************************************
*
* input:
* curPoint-----current mass center point
* prePoint-----previous mass center point
* prePrePoint--prePre mass center point
* return:next frame's obj mass center point
* Description : use current mass center point,pre mess center
* point,prePre mass center point to preict next
* mass center point
*
**********************************************************/
Point objStatPredict(Point curPoint,Point prePoint,Point prePrePoint)
{
Point nextMassCenter;
nextMassCenter.x = (4*curPoint.x +prePrePoint.x - 2*prePoint.x)/3;
nextMassCenter.y = (4*curPoint.y +prePrePoint.y - 2*prePoint.y)/3;
return nextMassCenter;
}
/**********************************************************
*
* input:
* argc-----current mass center point
* argv-----previous mass center point
*
**********************************************************/
int main(int argc,char **argv)
{
Mat pFrame;//read frame from video
VideoCapture pCapture;
pCapture.open("./plane.mov");
if(!pCapture.isOpened())
{
cout<<"open video error"< maxArea)
{
maxArea = tempArea;
maxAreaIdx = k;
}
}
Rect boundRect[1];
boundRect[0] = boundingRect(contours[maxAreaIdx]);
///Get the moments
vector mu(1);
mu[0] = moments(contours[maxAreaIdx],false);
/// Get the mass centers:
vector mc(1);
mc[0] = Point2f(mu[0].m10/mu[0].m00+roi.x , mu[0].m01/mu[0].m00+roi.y);
/// Draw contours
//draw obj contour
//drawContours(pFrame(roi),contours, maxAreaIdx,Scalar(0,0,255), \
// 2, 8, hierarchy, 0, Point() );
//draw mass center
circle(pFrame,mc[maxAreaIdx], 4, Scalar(0,255,0), -1, 8, 0 );
//calculates and returns the minimal up-right bounding
//rectangle for the contours[maxAreaIdx] set.
rectangle(pFrame,Rect(boundRect[0].tl().x+roi.x, \
boundRect[0].tl().y+roi.y,boundRect[0].width,\
boundRect[0].height), Scalar(0,0,255), 2, 8, 0 );
imshow("centroid tracking",pFrame);
//imshow("centroid tracking",thresholdFrame);
waitKey(33);
}
return 0;
}
实现效果如下:
先简单实现质心跟踪算法,由于该视频只有一个目标,所以代码中直接提取的最大的那个轮廓。论文的实现我会做好后会重新更新。