原創文章,轉貼請註明:http://blog.csdn.net/crzy_sparrow/article/details/7407604
本文目錄:
一.基於特徵點的目標跟蹤的一般方法
二.光流法
三.opencv中的光流法函數
四.用類封裝基於光流法的目標跟蹤方法
五.完整代碼
六.參考文獻
一.基於特徵點的目標跟蹤的一般方法
基於特徵點的跟蹤算法大致可以分爲兩個步驟:
1)探測當前幀的特徵點;
2)通過當前幀和下一幀灰度比較,估計當前幀特徵點在下一幀的位置;
3)過濾位置不變的特徵點,餘下的點就是目標了。
很顯然,基於特徵點的目標跟蹤算法和1),2)兩個步驟有關。特徵點可以是Harris角點(見我的另外一篇博文),也可以是邊緣點等等,而估計下一幀位置的方法也有不少,比如這裏要講的光流法,也可以是卡爾曼濾波法(咱是控制系的,上課經常遇到這個,所以看光流法看着看着就想到這個了)。
本文中,用改進的Harris角點提取特徵點(見我另一篇博文:http://blog.csdn.net/crzy_sparrow/article/details/7391511),用Lucas-Kanade光流法實現目標跟蹤。
二.光流法
這一部分《learing opencv》一書的第10章Lucas-Kanade光流部分寫得非常詳細,推薦大家看書。我這裏也粘帖一些選自書中的內容。
另外我對這一部分附上一些個人的看法(謬誤之處還望不吝指正):
1.首先是假設條件:
(1)亮度恆定,就是同一點隨着時間的變化,其亮度不會發生改變。這是基本光流法的假定(所有光流法變種都必須滿足),用於得到光流法基本方程;
(2)小運動,這個也必須滿足,就是時間的變化不會引起位置的劇烈變化,這樣灰度才能對位置求偏導(換句話說,小運動情況下我們才能用前後幀之間單位位置變化引起的灰度變化去近似灰度對位置的偏導數),這也是光流法不可或缺的假定;
(3)空間一致,一個場景上鄰近的點投影到圖像上也是鄰近點,且鄰近點速度一致。這是Lucas-Kanade光流法特有的假定,因爲光流法基本方程約束只有一個,而要求x,y方向的速度,有兩個未知變量。我們假定特徵點鄰域內做相似運動,就可以連立n多個方程求取x,y方向的速度(n爲特徵點鄰域總點數,包括該特徵點)。
2.方程求解
多個方程求兩個未知變量,又是線性方程,很容易就想到用最小二乘法,事實上opencv也是這麼做的。其中,最小誤差平方和爲最優化指標。
3.好吧,前面說到了小運動這個假定,聰明的你肯定很不爽了,目標速度很快那這貨不是二掉了。幸運的是多尺度能解決這個問題。首先,對每一幀建立一個高斯金字塔,最大尺度圖片在最頂層,原始圖片在底層。然後,從頂層開始估計下一幀所在位置,作爲下一層的初始位置,沿着金字塔向下搜索,重複估計動作,直到到達金字塔的底層。聰明的你肯定發現了:這樣搜索不僅可以解決大運動目標跟蹤,也可以一定程度上解決孔徑問題(相同大小的窗口能覆蓋大尺度圖片上儘量多的角點,而這些角點無法在原始圖片上被覆蓋)。
三.opencv中的光流法函數
opencv2.3.1中已經實現了基於光流法的特徵點位置估計函數(當前幀位置已知,前後幀灰度已知),介紹如下(摘自opencv2.3.1參考手冊):
calcOpticalFlowPyrLK
Calculates an optical flow for a sparse feature set using the iterative Lucas-Kanade method with pyramids.
void calcOpticalFlowPyrLK(InputArray prevImg, InputArray nextImg, InputArray prevPts,
InputOutputArray nextPts, OutputArray status, OutputArray err,
Size winSize=Size(15,15), int maxLevel=3, TermCriteria crite-
ria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01),
double derivLambda=0.5, int flags=0 )
Parameters
prevImg – First 8-bit single-channel or 3-channel input image.
nextImg – Second input image of the same size and the same type as prevImg .
prevPts – Vector of 2D points for which the flow needs to be found. The point coordinates
must be single-precision floating-point numbers.
nextPts – Output vector of 2D points (with single-precision floating-point coordinates)
containing the calculated new positions of input features in the second image. When
OPTFLOW_USE_INITIAL_FLOW flag is passed, the vector must have the same size as in the
input.
status – Output status vector. Each element of the vector is set to 1 if the flow for the
corresponding features has been found. Otherwise, it is set to 0.
err – Output vector that contains the difference between patches around the original and
moved points.
winSize – Size of the search window at each pyramid level.
maxLevel – 0-based maximal pyramid level number. If set to 0, pyramids are not used
(single level). If set to 1, two levels are used, and so on.
criteria – Parameter specifying the termination criteria of the iterative search algorithm
(after the specified maximum number of iterations criteria.maxCount or when the search
window moves by less than criteria.epsilon .
derivLambda – Not used.
flags – Operation flags:
– OPTFLOW_USE_INITIAL_FLOW Use initial estimations stored in nextPts . If the
flag is not set, then prevPts is copied to nextPts and is considered as the initial estimate.
四.用類封裝基於光流法的目標跟蹤方法
廢話少說,附上代碼,包括特徵點提取,跟蹤特徵點,標記特徵點等。
//幀處理基類
class FrameProcessor{
public:
virtual void process(Mat &input,Mat &ouput)=0;
};
//特徵跟蹤類,繼承自幀處理基類
class FeatureTracker : public FrameProcessor{
Mat gray; //當前灰度圖
Mat gray_prev; //之前的灰度圖
vector<Point2f> points[2];//前後兩幀的特徵點
vector<Point2f> initial;//初始特徵點
vector<Point2f> features;//檢測到的特徵
int max_count; //要跟蹤特徵的最大數目
double qlevel; //特徵檢測的指標
double minDist;//特徵點之間最小容忍距離
vector<uchar> status; //特徵點被成功跟蹤的標誌
vector<float> err; //跟蹤時的特徵點小區域誤差和
public:
FeatureTracker():max_count(500),qlevel(0.01),minDist(10.){}
void process(Mat &frame,Mat &output){
//得到灰度圖
cvtColor (frame,gray,CV_BGR2GRAY);
frame.copyTo (output);
//特徵點太少了,重新檢測特徵點
if(addNewPoint()){
detectFeaturePoint ();
//插入檢測到的特徵點
points[0].insert (points[0].end (),features.begin (),features.end ());
initial.insert (initial.end (),features.begin (),features.end ());
}
//第一幀
if(gray_prev.empty ()){
gray.copyTo (gray_prev);
}
//根據前後兩幀灰度圖估計前一幀特徵點在當前幀的位置
//默認窗口是15*15
calcOpticalFlowPyrLK (
gray_prev,//前一幀灰度圖
gray,//當前幀灰度圖
points[0],//前一幀特徵點位置
points[1],//當前幀特徵點位置
status,//特徵點被成功跟蹤的標誌
err);//前一幀特徵點點小區域和當前特徵點小區域間的差,根據差的大小可刪除那些運動變化劇烈的點
int k = 0;
//去除那些未移動的特徵點
for(int i=0;i<points[1].size ();i++){
if(acceptTrackedPoint (i)){
initial[k]=initial[i];
points[1][k++] = points[1][i];
}
}
points[1].resize (k);
initial.resize (k);
//標記被跟蹤的特徵點
handleTrackedPoint (frame,output);
//爲下一幀跟蹤初始化特徵點集和灰度圖像
std::swap(points[1],points[0]);
cv::swap(gray_prev,gray);
}
void detectFeaturePoint(){
goodFeaturesToTrack (gray,//輸入圖片
features,//輸出特徵點
max_count,//特徵點最大數目
qlevel,//質量指標
minDist);//最小容忍距離
}
bool addNewPoint(){
//若特徵點數目少於10,則決定添加特徵點
return points[0].size ()<=10;
}
//若特徵點在前後兩幀移動了,則認爲該點是目標點,且可被跟蹤
bool acceptTrackedPoint(int i){
return status[i]&&
(abs(points[0][i].x-points[1][i].x)+
abs(points[0][i].y-points[1][i].y) >2);
}
//畫特徵點
void handleTrackedPoint(Mat &frame,Mat &output){
for(int i=0;i<points[i].size ();i++){
//當前特徵點到初始位置用直線表示
line(output,initial[i],points[1][i],Scalar::all (0));
//當前位置用圈標出
circle(output,points[1][i],3,Scalar::all(0),(-1));
}
}
};
五.完整代碼
完整的運行代碼有300+行,粘上來太多了,大家去我傳的資源裏下載吧。
下載地址:http://download.csdn.net/detail/crzy_sparrow/4183674
運行結果:
六.參考文獻
【1】The classic article by B. Lucas and T. Kanade, An iterative image registration technique with an application to stereo vision in Int. Joint Conference in Artificial Intelligence, pp. 674-679,1981, that describes the original feature point tracking algorithm.【2】The article by J. Shi and C. Tomasi, Good Features to Track in IEEE Conference on Computer Vision and Pattern Recognition, pp. 593-600, 1994, that describes an improved version of the original feature point tracking algorithm.