部落格:http://blog.csdn.net/qianxin_dh
郵箱:[email protected]
一.前言
《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 <opencv2/opencv.hpp>
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<double> 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<num;k++)
{
scale_curr+=sqrt(maxValue[_frameNum-k-1]/maxValue[_frameNum-k-2]);
}
scale=(1-lambda)*scale+lambda*(scale_curr/num);
sigma=sigma*scale;
}
centerPoint.x=target.x+ctxRegion.x;
centerPoint.y=target.y+ctxRegion.y;
_rect.x=centerPoint.x-_rect.width*0.5;
_rect.y=centerPoint.y-_rect.height*0.5;
_rect&=Rect(0,0,_frame.cols,_frame.rows);
ctxRegion.x=centerPoint.x-ctxRegion.width*0.5;
ctxRegion.y=centerPoint.y-ctxRegion.height*0.5;
ctxRegion&=Rect(0,0,_frame.cols,_frame.rows);
getContextPrior(_frame,ctxRegion);
updateStcModel();
}
記錄: 最近由于工程需要,把這份代碼用opencv1.0的函數結合C又實作了一遍。程式調試過程中主要問題是集中在記憶體釋放以及cvDFT(src,dst,...)函數,傅立葉逆變換過程中,我一度以為dst必須是單通道(其實應該是雙通道.....),由于我調用的是opencv2.3.1的_c.h檔案(我以為這樣應該和1.0的效果是一緻的),奇怪的是當時能編譯通過,但是最後配置上1.0庫運作時就一直抱錯了,汗~。