opencv學習筆記四十三:CamShift目標跟蹤
CamShift演算法,全稱是 Continuously AdaptiveMeanShift,顧名思義,它是對Mean Shift 演算法的改進,能夠自動調節搜尋視窗大小來適應目標的大小,可以跟蹤視訊中尺寸變化的目標。基本思想是以視訊影象中運動物體的顏色資訊作為特徵,對輸入影象的每一幀分別作 Mean-Shift 運算,並將上一幀的目標中心和搜尋視窗大小(核函式頻寬)作為下一幀 Mean shift 演算法的中心和搜尋視窗大小的初始值,如此迭代下去,就可以實現對目標的跟蹤。因為在每次搜尋前將搜尋視窗的位置和大小設定為運動目標當前中心的位置和大小,而運動目標通常在這區域附近,縮短了搜尋時間;另外,在目標運動過程中,顏色變化不大,故該演算法具有良好的魯棒性。已被廣泛應用到運動人體跟蹤,人臉跟蹤等領域。
二、演算法流程
具體步驟如下:
步驟一:計算目標區域內的顏色直方圖。 通常是將輸入影象轉換到HSV顏色空間,目標區域為初始設定的搜尋視窗範圍,分離出色調H分量做該區域的色調直方圖計算。因為 RGB 顏色空間對光線條件的改變較為敏感,要減小該因素對跟蹤效果的影響,CamShift 演算法通常採用 HSV 色彩空間進行處理,當然也可以用其它顏色空間計算。這樣即得到目標模板的顏色直方圖。
步驟二:根據獲得的顏色直方圖將原始輸入影象轉化成顏色概率分佈圖像, 該過程稱為“反向投影"。所謂直方圖反向投影,就是輸入影象在已知目標顏色直方圖的條件下的顏色概率密度分佈圖,包含了目標在當前幀中的相干資訊。對於輸入影象中的每一個畫素,查詢目標模型顏色直方圖,對於目標區域內的畫素,可得到該畫素屬於目標畫素的概率,而對於非目標區域內的畫素,該概率為0。
步驟三:Mean Shift迭代過程。
即右邊大矩形框內的部分,它是 CamShift 演算法的核心,目的在於找到目標中心在當前幀中的位置。首先在顏色概率分佈圖中選擇搜尋視窗的大小和初始位置,然後計算搜尋視窗的質心位置。設畫素點(i, j)位於搜尋視窗內,I(i, j)是顏色直方圖的反向投影圖中該畫素點對應的值,定義搜尋視窗的零階矩 M00 和一階矩M10,M01如下:
則搜尋視窗的質心位置為:(M10/M00, M01/M00)。
接著調整搜尋視窗中心到質心。零階矩反映了搜尋視窗尺寸,依據它調整視窗大小,並將搜尋視窗的中心移到質心,如果移動距離大於設定的閾值,則重新計算調整後的視窗質心,進行新一輪的視窗位置和尺寸調整。直到視窗中心與質心之間的移動距離小於閾值,或者迭代次數達到某一最大值,認為收斂條件滿足,將搜尋視窗位置和大小作為下一幀的目標位置輸入,開始對下一幀影象進行新的目標搜尋。
三、總結
CamShift演算法改進了 Mean-Shift 跟蹤演算法的第二個缺陷,在跟蹤過程中能夠依據目標的尺寸調節搜尋視窗大小,對 有尺寸變化的目標可準確定位。但是,一方面 CamShfit 演算法在計算目標模板直方圖分佈時,沒有使用核函式進行加權處理,也就是說目標區域內的每個畫素點在目標模型中有著相同的權重,故 CamShfit 演算法的抗噪能力低於Mean-Shift跟蹤演算法。另一方面,CamShift 演算法中沒有定義候選目標,直接利用目標模板進行跟蹤。除此以外,CamShift 演算法採用 HSV 色彩空間的H分量建立目標直方圖模型,仍然只是依據目標的色彩資訊來進行跟蹤,當目標與背景顏色接近或者被其他物體遮擋時,CamShift 會自動將其包括在內,導致跟蹤視窗擴大,有時甚至會將跟蹤視窗擴大到整個視訊大小,導致目標定位的不準確,連續跟蹤下去造成目標的丟失。
#include<opencv2\opencv.hpp>
#include<opencv2\tracking.hpp>
using namespace cv;
using namespace std;
int bins = 16;
float ranges[] = { 0,180 };
const float *hranges = ranges ;
Mat frame,hsv,hue,hist,backproject;
Mat drawHist = Mat::zeros(255, 300, CV_8UC3);
int main(int arc, char** argv) {
VideoCapture capture(0);
//capture.open("vtest.avi");
namedWindow("input", CV_WINDOW_AUTOSIZE);
namedWindow("output", CV_WINDOW_AUTOSIZE);
bool firstFrame = true;
Rect selection;
capture.read(frame);
capture.read(frame);
imshow("input", frame);
selection = selectROI("input", frame);
cvtColor(frame, hsv, CV_BGR2HSV);
vector<Mat> channels;
split(hsv, channels);
hue = channels[0];
Mat roi = hue(selection);
calcHist(&roi, 1, 0, Mat(), hist, 1, &bins, &hranges);
normalize(hist, hist, 0, drawHist.rows, CV_MINMAX);
int bin_width = drawHist.cols / bins;
for (int i = 0; i < bins; i++) {
int val = saturate_cast<int>(hist.at<float>(i));
printf("%d\n", val);
rectangle(drawHist, Point(i*bin_width, drawHist.rows), Point((i + 1)*bin_width, drawHist.rows - val), Scalar(0, 255, 0), 2);
}
imshow("histogram", drawHist);
while (capture.read(frame)) {
imshow("input", frame);
cvtColor(frame, hsv, CV_BGR2HSV);
split(hsv, channels);
hue = channels[0];
calcBackProject(&hue, 1, 0, hist, backproject, &hranges);
RotatedRect trackbox = CamShift(backproject, selection, TermCriteria((TermCriteria::COUNT | TermCriteria::EPS), 100, 1));
ellipse(frame, trackbox, Scalar(0, 0, 255), 2,8);
imshow("output", frame);
char c = waitKey(100);
if (c == 27) {
break;
}
}
capture.release();
return 0;
}