opencv視覺跟蹤——CAMshift(meanshift均值漂移)

關於CAMshift的資料很多,如下鏈接寫的都很不錯,我就說說自己對CAMshift的理解

https://blog.csdn.net/li_dongxuan/article/details/70667170

https://blog.csdn.net/leixiaohua1020/article/details/12236091

總結:

一:HSV對光照影響小,RGB空間對光照影響大。
二:把ROI區域的直方圖求出來,然後以ROI區域直方圖爲依據,對原圖像反向投影, 這樣使得反向投影后的ROI區域的像素點都有值並且都很大,使得原圖像中與ROI區域顏色不一樣的像素點的值很小甚至接近0。這樣就形成了一副密度圖,利用Meanshft均值漂移,使圓形窗口的中心向窗口內稠密度大的質心移動。注意:均值偏移會在上一個ROI區域附近爲起點開始
搜索稠密度最大的質心,而不是對整幅圖像全部搜索,所以上一個ROI區域搜索到的稠密度最大的質心,只是局部最優答案,
不是整幅圖像稠密度最大的質心。
三:由於ROI區域會變大變小,爲了使算法具有魯棒性,添加預測算法。預測算法:
先設定一個最大、最小閾值。當搜索到ROI區域時,如果原圖像中ROI目標變小了,則圓形窗口內的值會變大,當大於閾值時,則使圓形窗口變小,精確追蹤ROI目標。如果原圖像中ROI目標變大了,則圓形窗口內的值會變小,當小於閾值時,則使圓形窗口變大,精確追蹤ROI目標

四:可以看出顏色對該算法影響很大

 

 

 

 

#include<opencv2/opencv.hpp> 
#include<iostream>      
using namespace cv;
using namespace std;

Mat image;
   
bool leftButtonDownFlag = false; //左鍵單擊後視頻暫停播放的標誌位,ture暫停
Point originalPoint; //矩形框起點    
Point processPoint; //矩形框終點    

//定義繪製直方圖的參數
int histSize = 200;//直方圖的bin值
float histR[] = { 0,255 };//每個bin值的範圍
const float *histRange = histR;
int channels[] = { 0,1 };//因爲輸入圖像個數爲2,所以不是 int channels=0;
Mat dstHist;//直方圖

Rect rect;//ROI矩形區域
Mat rectImage;//ROI矩形圖像
Mat targetImageHSV;//ROI區域的HSV空間圖像

Mat imageCopy; //繪製矩形框時用來拷貝原圖的圖像 

vector<Point> pt; //保存目標軌跡  

//鼠標回調函數,相當於中斷調用  
void onMouse(int event, int x, int y, int flags, void* ustc);   

int main(int argc, char*argv[])
{
	VideoCapture video;
	Mat frame;
	video.open(0);
	namedWindow("跟蹤木頭人", CV_WINDOW_NORMAL);
	//調用鼠標
	setMouseCallback("跟蹤木頭人", onMouse);

	while (video.read(frame))
	{
		if (!leftButtonDownFlag) //判定鼠標左鍵有沒有按下,沒有則採取播放視頻,則此時!leftButtonDownFlag爲ture
		{
			image= frame;
		}
		if (!image.data || waitKey(100) == 27)  //圖像爲空或Esc鍵按下退出播放    
		{
			break;
		}
		if (originalPoint != processPoint && !leftButtonDownFlag)//如果矩陣框起點不等於終點以及鼠標左鍵沒有按下
			//左鍵沒有按下分爲一直沒有按下和選擇ROI後的鬆開左鍵。  當左鍵一直沒有按下使,originalPoint與processPoint爲空,就不想等,則跳過if
			//當是選擇了ROI區域的鬆開左鍵,此時originalPoint與processPoint是有具體數值的。並且選擇ROI區域後,這兩個值會一直不相等
		{
			Mat imageHSV;//整幅圖像的HSV空間
			Mat calcBackImage;//反向投影
			cvtColor(image, imageHSV, CV_RGB2HSV);//原圖像轉換成HSV空間
			calcBackProject(&imageHSV, 2, channels, dstHist, calcBackImage, &histRange);  //反向投影 
			//dstHist輸入直方圖,calcBackImage輸出圖像

			TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.001);
			//這個類是作爲迭代算法的終止條件的.該類變量需要3個參數,一個是類型,第二個參數爲迭代的最大次數,最後一個是特定的閾值。
			//類型有CV_TERMCRIT_ITER、CV_TERMCRIT_EPS、CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,
			//分別代表着迭代終止條件爲達到最大迭代次數終止,迭代到閾值終止,或者兩者都作爲迭代終止條件。
			//以上的宏對應的c++的版本分別爲TermCriteria::COUNT、TermCriteria::EPS,這裏的COUNT也可以寫成MAX_ITER。
			CamShift(calcBackImage, rect, criteria);
			//rect爲輸入和輸出的ROI窗口,窗口的尺寸會自動調整

			Mat imageROI = imageHSV(rect);   //更新HSV空間下的ROI             
			calcHist(&imageROI, 2, channels, Mat(), dstHist, 1, &histSize, &histRange);//更新ROI的直方圖
			normalize(dstHist, dstHist, 0.0, 1.0, NORM_MINMAX);   //歸一化  
			rectangle(image, rect, Scalar(255, 0, 0), 3);    //繪製新的ROI矩形    
			pt.push_back(Point(rect.x + rect.width / 2, rect.y + rect.height / 2));
			//C++中容器中的push_back用的時候,容器的大小不能給定。空號裏的點座標附加給容器pt結尾,而容器pt會自動調整容器容量
			//pt.insert(a),也能把點座標附加給容器pt結尾,但是容器pt的容量不會自動改變,需要自己編程序
			for (int i = 0; i<pt.size() - 1; i++)
			{
				line(image, pt[i], pt[i + 1], Scalar(0, 255, 0), 2.5);//畫質心線
			}
		}
		imshow("跟蹤木頭人", image);
		waitKey(100);
	}
	return 0;
}

//*******************************************************************//      
//鼠標回調函數      
void onMouse(int event, int x, int y, int flags, void *ustc)
{
	if (event == CV_EVENT_LBUTTONDOWN)//如果按下鼠標左鍵
	{
		leftButtonDownFlag = true; //標誌位    
		originalPoint = Point(x, y);  //設置左鍵按下點的矩形起點    
		processPoint = originalPoint;//此時的起點跟終點座標一樣
	}
	if (event == CV_EVENT_MOUSEMOVE && leftButtonDownFlag)//如果鼠標左鍵按下並且移動
	{
		imageCopy = image.clone();
		processPoint = Point(x, y);//把移動的座標賦給矩形終點
		if (originalPoint != processPoint)//如果矩形起點不等於終點
		{
			//在複製的圖像上繪製矩形    
			rectangle(imageCopy, originalPoint, processPoint, Scalar(255, 0, 0), 2);
		}
		imshow("跟蹤木頭人", imageCopy);
	}
	if (event == CV_EVENT_LBUTTONUP)//如果鼠標左鍵放開
	{
		leftButtonDownFlag = false;//改變鼠標是否按下的標誌位
		rect = Rect(originalPoint, processPoint);//ROI區域
		rectImage = image(rect); //ROI圖像顯示    
		imshow("ROI", rectImage);

		cvtColor(rectImage, targetImageHSV, CV_RGB2HSV);//ROI區域轉換成HSV空間
		imshow("ROI HSV", targetImageHSV);

		calcHist(&targetImageHSV, 2, channels, Mat(), dstHist, 1, &histSize, &histRange, true, false);//直方圖
		normalize(dstHist, dstHist, 0.0, 1.0, CV_MINMAX);//歸一化
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章