1. 程式人生 > >【opencv】Camshift目標跟蹤

【opencv】Camshift目標跟蹤

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;  
}