博客:http://blog.csdn.net/qianxin_dh
一.前言
《Fast Tracking via Spatio-Temporal Context Learning》是Kaihua Zhang等人发表的一篇文章,文中提到了通过利用时空上下文进行视觉跟踪,具有很好的实时性和鲁棒性。该算法基于贝叶斯框架,建立了我们感兴趣的目标与周围内容的时空关系,在低阶特征上(如图像灰度和位置)对目标与附近区域进行了统计关系建模。通过计算置信图(confidence map),找到似然概率最大的位置,即为跟踪结果。
算法的计算过程主要是利用了傅立叶快速变换,目前作者已经提供了matlab源代码,该代码在i7机器上运行速度可以达到350FPS,速度效果着实明显!同时,在博客 http://blog.csdn.net/zouxy09/article/details/16889905 上,博主给出了他写出的单尺度c++版STC代码。在本篇博客的最后,我也对代码进行了整理,完善,代码中有什么不足的地方,希望大家能够积极指正。
代码下载地址:http://download.csdn.net/detail/qianxin_dh/7882289
本论文官方主页:http://www4.comp.polyu.edu.hk/~cslzhang/STC/STC.htm
二.走进STC
视觉跟踪领域中,由我们感兴趣的目标物体与它附近一定区域范围内的背景共同组成局部上下文(见下图红色框)。因此,局部上下文在连续帧中存在着很强的时空关系。例如,图中女生的脸部发生了明显的遮挡现象,但是对于局部区域来说,只是一部分发生了变化,背景以及遮挡部分与背景之间的相对位置并未发生明显改变,利用这一点,局部上下文就可以在下一帧帮助预测到目标出现的位置。
通常来说,时间上下文帮助我们目标位置,而空间上下文则能提供更精确的信息帮助我们区分目标和背景。利用时空上下文可以实现快速,鲁棒的跟踪物体,其基本的算法流程如下:
1)基于目标与它局部区域内背景的空间关系建立一个空间上下文模型。
2)利用空间上下文模型对下一帧的时空上下文模型进行更新。
结合时空上下文信息,对图像进行卷积操作,获得置信图,并求得它的最大似然概率位置作为最佳目标位置。
三.具体公式
上面一部分提到了要获得目标的跟踪位置,我们需要获得当前帧的置信图。论文中给出了置信图的公式:
x表示目标位置,o表示目标出现。假定当前帧中,我们已经知道了目标位置为X*,则从图像中我们可以获得特征:,I(z)表示位置z处的图像灰度,表示目标X*的局部区域。
上述公式中,我们可以看到C(x)分成了两部分相乘,其中 条件概率P(x|c(z),o)对目标和它的上下文信息进行了空间关系建模,这也是整个算法过程中的主要环节。而上下文先验概率P(c(z)|o)对局部上下文信息进行了建模(见上图),也即是,局部区域内每个点z为目标的概率。
以下就公式中具体的每个环节进行分析,结合整体流程更利于理解该算法,首先,先列出算法的整体运行流程:
A.空间上下文模型(Spatial Context Model)
公式:;
分析:条件概率函数表示了目标位置X*与局部区域内点z之间的相对距离以及方向关系,因此反映了目标与周围区域的空间关系。由于不是一个径向对称函数,因此能帮助解决分辨二异性问题。比如在上面提到的一副图中,Zl和Zr与X*的相对距离是相等的,但是方向不同,因此会产生不同的空间关系,即:;
B.上下文先验概率模型(Context prior Model)
公式:;
分析:I(z)表示z处的灰度值,表示权重函数(a为归一化参数,保证概率取值范围为[0,1],sigma表示尺度参数)。通常距离目标X*越近的点对于跟踪目标越重要,因此赋予它更大的权重。
C.置信图(Confidence Map)
公式:;
分析:论文中这部分主要是对参数beta进行了讨论,经过实验论证,认为beta=1时,跟踪的结果最鲁棒;
D.快速学习空间上下文模型(Fast Learning Spatial Context Model)
公式:
分析:只要我们获得了置信图以及上下文先验概率模型,我们就可以获得空间上下文先验模型。通过傅立叶变换,将上述公式转换到频率域进行运算,即:;进而,我们可以求得:;
E.最后一步,跟踪
我们假定在第一帧时已经通过手动或者一些检测算法对跟踪目标进行了选取。在第t帧时,我们得到此时的空间上下文模型,并用它来更新时空上下文模型,具体更新公式为:。在第t+1帧时,我们基于t帧目标位置裁剪出局部区域,,构建特征集,通过求得t+1帧时的置信图的最大似然概率位置(,其中:),获得新的目标位置。
尺度更新:
四.代码实现
这里我只贴出部分相关代码,完整工程我已上传资源。
头文件:
#pragma once
#include
using namespace cv;
class STC
{
public:
STC();
~STC();
public:
void init(Mat& _frame,Rect& _rect);
void processFrame(Mat& _frame, Rect& _rect, int _frameNum);
private:
void creatHammingWindow();
void getContextPrior(Mat& _frame,Rect& _rect);
void updateStcModel();
void complexOperation(Mat& _src1,Mat& _src2,Mat& _hscf,int flag);
void calcuConfidenceMap(Mat& _stcModel, Mat& _contextPrior,Point& _target,double& _maxVal);
private:
Point centerPoint;
double rho;
double alpha;
double sigma;
double lambda;
int num;
double scale;
vector maxValue;
Rect ctxRegion; //context region
Mat conf; //confidence map
Mat weight;
Mat hmwindow;
Mat contextPrior;
Mat scModel;
Mat stcModel;
};
参数初始化:
STC::STC()
{
rho=0.075; // learning parameter
alpha=2.25; // the parameters of the map function
scale=1.0; //initial scale ratio
lambda=0.25; //Eq.(15)
num=5; //num consecutive frames
}
多尺度部分:
void STC::processFrame(Mat& _frame,Rect& _rect,int _frameNum)
{
getContextPrior(_frame,ctxRegion);
Point target;
double maxVal;
calcuConfidenceMap(stcModel,contextPrior,target,maxVal);
maxValue.push_back(maxVal);
/***********update scale by Eq.(15)**********/
if (_frameNum%(num+2)==0)
{
double scale_curr=0.0;
for (int k=0;k
记录:
最近由于工程需要,把这份代码用opencv1.0的函数结合C又实现了一遍。程序调试过程中主要问题是集中在内存释放以及cvDFT(src,dst,...)函数,傅立叶逆变换过程中,我一度以为dst必须是单通道(其实应该是双通道.....),由于我调用的是opencv2.3.1的_c.h文件(我以为这样应该和1.0的效果是一致的),奇怪的是当时能编译通过,但是最后配置上1.0库运行时就一直抱错了,汗~。