【opencv】Camshift目標跟蹤
CamShift演算法的全稱是"Continuously Adaptive Mean-SHIFT",即:連續自適應的MeanShift演算法。其基本思想是對視訊序列的所有影象幀都作MeanShift運算,並將上一幀的結果(即搜尋視窗的中心位置和視窗大小)作為下一幀MeanShift演算法的搜尋視窗的初始值,如此迭代下去。簡單點說,meanShift是針對單張圖片尋找最優迭代結果,而camShift則是針對視訊序列來處理,並對該序列中的每一幀圖片都呼叫meanShift來尋找最優迭代結果。正是由於camShift針對一個視訊序列進行處理,從而保證其可以不斷調整視窗的大小,如此一來,當目標的大小發生變化的時候,該演算法就可以自適應地調整目標區域繼續跟蹤。
在自帶的camShift的例子當中,是通過計算目標在HSV空間下的H分量直方圖,通過直方圖反向投影得到目標畫素的概率分佈,然後通過呼叫OpenCV的CAMSHIFT演算法,自動跟蹤並調整目標視窗的中心位置與大小。該演算法對於簡單背景下的單目標跟蹤效果較好,但如果被跟蹤目標與背景顏色或周圍其它目標顏色比較接近,則跟蹤效果較差。另外,由於採用顏色特徵,所以它對被跟蹤目標的形狀變化有一定的抵抗能力。
http://blog.csdn.net/carson2005/article/details/7439125
分為三個部分:
1--色彩投影圖(反向投影):
(1).RGB顏色空間對光照亮度變化較為敏感,為了減少此變化對跟蹤效果的影響,首先將影象從RGB空間轉換到HSV空間。(2).然後對其中的H分量(色調)作直方圖,在直方圖中代表了不同H分量值出現的概率或者畫素個數,就是說可以查找出H分量大小為h的概率或者畫素個數,即得到了顏色概率查詢表。(3).將影象中每個畫素的值用其顏色出現的概率對替換,就得到了顏色概率分佈圖。這個過程就叫反向投影,顏色概率分佈圖是一個灰度影象。
2--meanshift
meanshift演算法是一種密度函式梯度估計的非引數方法,通過迭代尋優找到概率分佈的極值來定位目標。
演算法過程為:
(1).在顏色概率分佈圖中選取搜尋窗W
(2).計算零階距:
計算一階距:
計算搜尋窗的質心:
(3).調整搜尋窗大小
寬度為;長度為1.2s;
(4).移動搜尋窗的中心到質心,如果移動距離大於預設的固定閾值,則重複2)3)4),直到搜尋窗的中心與質心間的移動距離小於預設的固定閾值,或者迴圈運算的次數達到某一最大值,停止計算。關於meanshift的收斂性證明可以google相關文獻。
3--camshift
將meanshift演算法擴充套件到連續影象序列,就是camshift演算法。它將視訊的所有幀做meanshift運算,並將上一幀的結果,即搜尋窗的大小和中心,作為下一幀meanshift演算法搜尋窗的初始值。如此迭代下去,就可以實現對目標的跟蹤。
演算法過程為:
(1).初始化搜尋窗
(2).計算搜尋窗的顏色概率分佈(反向投影)
(3).執行meanshift演算法,獲得搜尋窗新的大小和位置。
(4).在下一幀視訊影象中用(3)中的值重新初始化搜尋窗的大小和位置,再跳轉到(2)繼續進行。
camshift能有效解決目標變形和遮擋的問題,對系統資源要求不高,時間複雜度低,在簡單背景下能夠取得良好的跟蹤效果。但當背景較為複雜,或者有許多與目標顏色相似畫素干擾的情況下,會導致跟蹤失敗。因為它單純的考慮顏色直方圖,忽略了目標的空間分佈特性,所以這種情況下需加入對跟蹤目標的預測演算法。
__________________________________________________________________________________________________________________________________________
半自動跟蹤思路:輸入視訊,用畫筆圈出要跟蹤的目標,然後對物體跟蹤。
用過opencv的都知道,這其實是camshiftdemo的工作過程。
第一步:選中物體,記錄你輸入的方框和物體。
第二步:求出視訊中有關物體的反向投影圖。
第三步:根據反向投影圖和輸入的方框進行meanshift迭代,由於它是向重心移動,即向反向投影圖中概率大的地方移動,所以始終會移動到目標上。
第四步:然後下一幀影象時用上一幀輸出的方框來迭代即可。
全自動跟蹤思路:輸入視訊,對運動物體進行跟蹤。
第一步:運用運動檢測演算法將運動的物體與背景分割開來。
第二步:提取運動物體的輪廓,並從原圖中獲取運動影象的資訊。
第三步:對這個資訊進行反向投影,獲取反向投影圖。
第四步:根據反向投影圖和物體的輪廓(也就是輸入的方框)進行meanshift迭代,由於它是向重心移動,即向反向投影圖中概率大的地方移動,所以始終會移動到物體上。
第五步:然後下一幀影象時用上一幀輸出的方框來迭代即可。
下面是一個全自動跟蹤的例子
程式碼是copy來的,從哪來的忘了……
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
//對輪廓按面積降序排列
bool biggerSort(vector<Point> v1, vector<Point> v2)
{
return contourArea(v1)>contourArea(v2);
}
int main()
{
//視訊不存在,就返回
VideoCapture cap("3.AVI");
if(cap.isOpened()==false)
return 0;
//定義變數
int i;
Mat frame; //當前幀
Mat foreground; //前景
Mat bw; //中間二值變數
Mat se; //形態學結構元素
//用混合高斯模型訓練背景影象
BackgroundSubtractorMOG mog;
for(i=0;i<10;++i)
{
cout<<"正在訓練背景:"<<i<<endl;
cap>>frame;
if(frame.empty()==true)
{
cout<<"視訊幀太少,無法訓練背景"<<endl;
getchar();
return 0;
}
mog(frame,foreground,0.01);
}
//目標外接框、生成結構元素(用於連線斷開的小目標)
Rect rt;
se=getStructuringElement(MORPH_RECT,Size(5,5));
//統計目標直方圖時使用到的變數
vector<Mat> vecImg;
vector<int> vecChannel;
vector<int> vecHistSize;
vector<float> vecRange;
Mat mask(frame.rows,frame.cols,DataType<uchar>::type);
//變數初始化
vecChannel.push_back(0);
vecHistSize.push_back(32);
vecRange.push_back(0);
vecRange.push_back(180);
Mat hsv; //HSV顏色空間,在色調H上跟蹤目標(camshift是基於顏色直方圖的演算法)
MatND hist; //直方圖陣列
double maxVal; //直方圖最大值,為了便於投影圖顯示,需要將直方圖規一化到[0 255]區間上
Mat backP; //反射投影圖
Mat result; //跟蹤結果
//視訊處理流程
while(1)
{
//讀視訊
cap>>frame;
if(frame.empty()==true)
break;
//生成結果圖
frame.copyTo(result);
//檢測目標(其實是邊訓練邊檢測)
mog(frame,foreground,0.01);
imshow("混合高斯檢測前景",foreground);
moveWindow("混合高斯檢測前景",400,0);
//對前景進行中值濾波、形態學膨脹操作,以去除偽目標和接連斷開的小目標
medianBlur(foreground,foreground,5);
imshow("中值濾波",foreground);
moveWindow("中值濾波",800,0);
morphologyEx(foreground,foreground,MORPH_DILATE,se);
//檢索前景中各個連通分量的輪廓
foreground.copyTo(bw);
vector<vector<Point>> contours;
findContours(bw,contours,RETR_EXTERNAL,CHAIN_APPROX_NONE);
if(contours.size()<1)
continue;
//對連通分量進行排序
std::sort(contours.begin(),contours.end(),biggerSort);
//結合camshift更新跟蹤位置(由於camshift演算法在單一背景下,跟蹤效果非常好;
//但是在監控視訊中,由於解析度太低、視訊質量太差、目標太大、目標顏色不夠顯著
//等各種因素,導致跟蹤效果非常差。 因此,需要邊跟蹤、邊檢測,如果跟蹤不夠好,
//就用檢測位置修改
cvtColor(frame,hsv,COLOR_BGR2HSV);
vecImg.clear();
vecImg.push_back(hsv);
for(int k=0;k<contours.size();++k)
{
//第k個連通分量的外接矩形框
if(contourArea(contours[k])<contourArea(contours[0])/5)
break;
rt=boundingRect(contours[k]);
mask=0;
mask(rt)=255;
//統計直方圖
calcHist(vecImg,vecChannel,mask,hist,vecHistSize,vecRange);
minMaxLoc(hist,0,&maxVal);
hist=hist*255/maxVal;
//計算反向投影圖
calcBackProject(vecImg,vecChannel,hist,backP,vecRange,1);
//camshift跟蹤位置
Rect search=rt;
RotatedRect rrt=CamShift(backP,search,TermCriteria(TermCriteria::COUNT+TermCriteria::EPS,10,1));
Rect rt2=rrt.boundingRect();
rt&=rt2;
//跟蹤框畫到視訊上
rectangle(result,rt,Scalar(0,255,0),2);
}
//結果顯示
imshow("原圖",frame);
moveWindow("原圖",0,0);
imshow("膨脹運算",foreground);
moveWindow("膨脹運算",0,350);
imshow("反向投影",backP);
moveWindow("反向投影",400,350);
imshow("跟蹤效果",result);
moveWindow("跟蹤效果",800,350);
waitKey(30);
}
getchar();
return 0;
}
3.avi是我寫的一個目標運動模擬,用來做這個實驗
BackgroundSubtractorMOG
int main()
{
VideoCapture video("3.avi");
Mat frame,mask,thresholdImage, output;
BackgroundSubtractorMOG bgSubtractor;
while(true)
{
video>>frame;
if (frame.empty())
break;
bgSubtractor(frame,mask,0.1);
imshow("mask",mask);
waitKey(10);
}
return 0;
}