KCF: Kernelized correlation filter
KCF是一种鉴别式追踪方法,这类方法一般都是在追踪过程中训练一个目标检测器,使用目标检测器去检测下一帧预测位置是否是目标,然后再使用新检测结果去更新训练集进而更新目标检测器。而在训练目标检测器时一般选取目标区域为正样本,目标的周围区域为负样本,当然越靠近目标的区域为正样本的可能性越大。
论文:High-Speed Tracking with Kernelized Correlation Filters
算法源码:opencv_contribe/modules//tracking/tackerkcf.cpp
调用:参考../opencv_contrib/modules/tracking/samples/kcf.cpp
备注: opencv3.1.0与opencv_contrib配置参考;
我原本也打算写一篇关于KCF算法原理的认知的,最后还是打算放弃,因为我学习参考的博主都写的很不错,所以还是打算把代码贴出。。。。
算法调用
调用很简单就2个函数,但是在调用之前你需要将opencv3.1.0与opencv_contrib配置好。第一个函数初始化:init(Mat frame, Rect2d roi), //frame 为输入的彩色图,roi是要跟踪的目标区域,大小为刚好将目标框住就OK, Rect2就是Rect(x,y,w,h);
第二个函数的目的是训练检测,因为该算法的原因就是先训练再检测,再训练再检测,这样不断循环:update(Mat frame, Rect2d result); //frame 为输入的彩色图, result是跟踪到目标的区域,大小跟初始化时roi的大小相等。
主要的头文件:
#include <opencv2/core/utility.hpp>
#include <opencv2/tracking.hpp>
例子:
// 创建一个跟踪对象
Ptr<Tracker> tracker = Tracker::create( "KCF" );
// 设置输入的视频
VideoCapture cap(“ETH-Linthescher.mp4”);
Mat frame;
cap >> frame;
//quit if ROI was not selected
if(roi.width==0 || roi.height==0)
return 0;
// 初始化
tracker->init(frame,roi);
//开始跟踪
printf("Start the tracking process, press ESC to quit.\n");
for ( ;; ) {
// get frame from the video
cap >> frame;
// stop the program if no more images
if(frame.rows==0 || frame.cols==0)
break;
// 更新跟踪结果
tracker->update(frame,roi);
// draw the tracked object
rectangle( frame, roi, Scalar( 255, 0, 0 ), 2, 1 );
// show image with the tracked object
imshow("tracker",frame);
//quit on ESC button
if(waitKey(1)==27)break;
}
学习完KCF算法的调用,是不是感觉该算法其实很简单,就2个函数而已,那你就错了,那是因为主角还没登场。。。大家以热烈的掌声有请我们的主角闪亮登场吧! 初始化:
这是算法中初始化函数的定义:bool TrackerKCFImpl::initImpl(const Mat& /*image*/, const Rect2d& boundingBox ),细心的朋友会发现其实初始化跟输入的图像没关系。
初始化完成了4个任务:
1)目标区域填充;
2)创建汉宁窗;
3)生成一个高斯响应;
4)对高斯响应求傅里叶变换。
目标区域填充
所有工作就是在扩大后的区域内进行的。
//params.resize = true (true表示图像可以缩放), max_patch_size=80*80;
if(params.resize && roi.width*roi.height>params.max_patch_size)
{
resizeImage=true; // 如果为true,表示后面都是缩小一倍的图像进行处理,所有的框也跟着缩小一倍 roi.x/=2.0;
roi.y/=2.0;
roi.width/=2.0;
roi.height/=2.0;
}
roi.x-=roi.width/2;
roi.y-=roi.height/2;
roi.width*=2;
roi.height*=2; 由于跟踪的区域是固定的,所有摄像机要与目标保持固定的距离。
创建汉宁窗
目的是采样时为不同的样本分配不同的权重,kcf算法实际是采样了一个样本作为base样本,别的样本都是通过对base样本的平移得到,虚拟出来的样本。一般对样本都采用非正即负的方法来标记训练样本,即正样本标签为1,负样本为0。这样标记样本的方法不能很好的反应每个负样本的权重,即对离中心目标远的样本和离中心目标近的样本等同看待。但是在实际中我们需要对不同的负样本不同的看待,就有了根据样本中心离目标的远近分别赋值 [0,1]范数的数。离目标越近,值越趋向于1,离目标越远,值越趋向于0. kcf就是根据汉宁窗来对不同样本分配不同的权重。
1 - cos( (2*pi*j) / (h-1))];
hann(i,j) 的值就是每样样本的权重。
函数是: createHanningWindow(hann, roi.size(), CV_64F);
生成一个高斯响应
//计算高斯函数的均方差
output_sigma=sqrt(roi.width*roi.height)*params.output_sigma_factor; //params.output_sigma_factor=1.0/16.0
output_sigma=-0.5/(output_sigma*output_sigma); //a=-1/(2*a^2)
y(i,j) = exp(output_sigma
//(i,j) 是位置,(w,h)是窗口大小,y(i,j) 的值就是每个样本的实际响应值。
4)高斯响应的傅里叶变换
fft2(y,yf); //yf 是高斯响应的傅里叶变换
训练:
1)特征提取:在这个函数getSubWindow()完成,提取特征为彩色+灰度;彩色特征存放在这features_npca,灰度特征存放在features_pca。
提取灰度特征:
Mat feat = frame(roi); //w*h*1
feat=feat/255.0-0.5; // normalize to range -0.5 .. 0.5
feat=feat.mul(hann); // hann window filter X[1] = feat; //w*h*1
提取彩色特征:
Mat patch= frame(roi);
feat w*h*10, 该算法是将颜色划分的更细,内容在featureColorName.cpp
feat=feat.mul(hann_cn); // hann window filter
X[0] = feat; //w*h*10
2) 将彩色特征feat(w*h*10)进行pca主成分分析,其实就是压缩,默认的参数设置压缩大小compress_size=2,压缩后彩色特征X[0]就变成w*h*2
检测:
1. 先提取特征;
2. // extract the maximum response
minMaxLoc( response, &minVal, &maxVal, &minLoc, &maxLoc ); //maxLoc的位置代表跟踪到目标的中心点
roi.x+=(maxLoc.x-roi.width/2+1);
roi.y+=(maxLoc.y-roi.height/2+1); //跟踪结果,目标大小是固定的
备注:
1. KCF跟踪算法是不带识别的,也不需要特定哪一类物体;
2.测试opencv_contrib带的KCF跟踪算法(灰色+彩色特征)与github中作者给出的hog特征,前者效果比较好,但是论文中给出后者效果好。
参考:
http://www.jianshu.com/p/9aacd075a689
http://www.360doc.com/content/16/0302/13/25664332_538789414.shtml