OpenCV4學習筆記(37)——CAMShift(Continuously Adaptive MeanShift)算法

在上次的筆記《OpenCV4學習筆記(36)——基於均值遷移(MeanShift)算法和直方圖反向投影的目標移動跟蹤》中,整理記錄了一種針對目標的移動跟蹤算法,主要是基於均值遷移和直方圖反向投影來實現的。而今天要記錄的筆記內容,依然是一種針對目標的移動跟蹤算法——CAMShift目標移動跟蹤算法,這種算法是基於MeanShift目標移動跟蹤算法的改進。

CAMShift算法其實就是連續自適應的MeanShift算法,是對MeanShift 算法的改進,它能夠自動調節搜索窗口大小來適應目標的大小,可以跟蹤視頻中尺寸變化的目標。

CAMShift算法相比起MeanShift算法主要具有兩個改進方向:
一是CAMShift算法會根據跟蹤對象的大小變化來自動調整搜索窗口大小;
二是返回的檢測目標信息更加完整,不僅包含位置信息,同時還包含了角度信息,也即返回的是目標所在的最小外接旋轉矩形。

在OpenCV中提供了CamShift()這個API來實現CAMShift算法,它的返回值爲一個包含位置和角度的旋轉矩形,其參數列表如下:

第一個參數probImage:輸入的概率分佈圖像,也就是反向投影圖像;

第二個參數window:目標位置的初始窗口;

第三個參數criteria:遷移終止條件,TermCriteria::EPS爲遷移距離小於閾值時停止遷移,TermCriteria::COUNT爲遷移迭代次數達到設定的最大值時停止遷移,可以兩者同時使用。

下面看一下具體的代碼演示:

	VideoCapture capture;
	capture.open("D:\\opencv_c++\\opencv_tutorial\\data\\images\\balltest.mp4");
	//capture.open(0);
	if (!capture.isOpened())
	{
		return 0;
	}
	//提取第一幀圖像中ROI區域並將其轉化爲HSV圖像
	Mat first_image, first_hsv, roi_image, roi_hsv;
	capture.read(first_image);
	Rect roi_rect = selectROI("first_image", first_image, false, false);
	roi_image = first_image(roi_rect).clone();
	cvtColor(roi_image, roi_hsv, COLOR_BGR2HSV);
	//計算ROI圖像的直方圖
	Mat roi_hist;
	int dims = 2;
	int channels[2] = { 0,1 };
	int histSize[2] = { 180,255 };
	float ranges_h[2] = { 0,180 };
	float ranges_s[2] = { 0,255 };
	const float* histRanges[2] = { ranges_h, ranges_s };
	calcHist(&roi_hsv, 1, channels, Mat(), roi_hist, dims, histSize, histRanges);
	normalize(roi_hist, roi_hist, 0, 255, NORM_MINMAX);

	Mat frame, frame_hsv;
	vector<Point2f> centers;
	while (capture.read(frame))
	{
		flip(frame, frame, 1);
		cvtColor(frame, frame_hsv, COLOR_BGR2HSV);
		//對每一幀圖像進行反向投影
		Mat backProject;
		calcBackProject(&frame_hsv, 1, channels, roi_hist, backProject, histRanges);
		//進行CamShift算法,不斷更新目標窗口位置;返回值爲一個包含位置和角度的旋轉矩形
		RotatedRect new_rect = CamShift(backProject, roi_rect, TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 10, 1));
		// //參數probImage:輸入的概率分佈圖像,也就是反向投影圖像;
		//	//參數window:目標位置的初始窗口;
		//	//參數criteria:遷移終止條件;TermCriteria::EPS爲遷移距離小於閾值時停止遷移,TermCriteria::COUNT爲遷移迭代次數達到設定的最大值時停止遷移,可以兩者同時使用;
		ellipse(frame, new_rect, Scalar(0, 255, 0), 1, 8);			//繪製目標區域位置

		//獲取當前目標中心座標,並存入點集中;不繪製完整目標移動軌跡,如果點集中的點數量達到規定個數,就清空點集;也就是每次只繪製最近的移動軌跡
		Point2f center = new_rect.center;	
		if (centers.size() % 20 == 0)
		{
			centers.clear();
		}
		centers.push_back(center);

		if (centers.size() > 2)		//只有當目標出現移動時,也就是目標中心座標至少有兩個點時才進行繪製
		{
			for (size_t k = 1; k < centers.size(); k++)
			{
				float  flag = float(k) / float(centers.size());			//標記當前點位於當前繪製的軌跡中的位置
				//通過標記位置的不同,來繪製不同粗細的曲線,從而使繪製出的軌跡曲線有逐漸消失的效果,類似於彗星尾巴
				if (flag < 0.2)
				{
					line(frame, centers[k - 1], centers[k], Scalar(0, 255, 0), 1, LINE_AA, 0);
				}
				else if(flag >= 0.2 && flag <= 0.4)
				{
					line(frame, centers[k - 1], centers[k], Scalar(0, 255, 0), 2, LINE_AA, 0);
				}
				else if (flag >= 0.4 && flag <= 0.6)
				{
					line(frame, centers[k - 1], centers[k], Scalar(0, 255, 0), 3, LINE_AA, 0);
				}
				else if(flag >= 0.6 && flag <= 0.8)
				{
					line(frame, centers[k - 1], centers[k], Scalar(0, 255, 0), 4, LINE_AA, 0);
				}
				else if (flag >= 0.8 && flag <= 1)
				{
					line(frame, centers[k - 1], centers[k], Scalar(0, 255, 0), 5, LINE_AA, 0);
				}
			}
		}
		imshow("frame", frame);

		char ch = waitKey(20);
		if (ch == 27)
		{
			break;
		}

	}
	capture.release();

在上述代碼中,首先讀取視頻流,然後提取第一幀圖像中我們需要的ROI區域圖像並將其轉化到HSV色彩空間,並且計算該ROI圖像的直方圖。

接着讀取視頻流的每一幀圖像,對每一幀圖像都轉化到HSV色彩空間進行反向投影,得到反向投影圖。並對獲取到的反向投影圖進行CAMShift算法計算,得到在每幀圖像中檢測到的目標位置,以一個旋轉矩形來表示。

在進行跟蹤顯示的時候,我們做一點視覺上的優化處理。我們不繪製移動目標的完整移動軌跡,也就是每次只繪製最近若干幀圖像中的移動軌跡,並且對於軌跡的顏色是從深到淺變化,粗細程度是從粗到細變化、直至消失。這樣,就把移動軌跡變成了類似於彗星拖尾的感覺。

下面看一下演示效果截圖:
在這裏插入圖片描述
這是通過鼠標來進行ROI區域的提取。

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
上面4張圖像是視頻播放過程的截圖。可以看到,隨着畫面中球體目標的不斷運動,用於標記目標位置的橢圓也不斷隨之運動,而且尺寸、形狀也是不斷變化,並且後續的運動軌跡也是符合我們的視覺要求,形成一條類似的”彗尾”。
這樣我們就實現了對視頻流中目標物體的移動跟蹤,並進行視覺優化顯示。

好了,今天的筆記內容就整理到此,謝謝閱讀~

PS:本人的註釋比較雜,既有自己的心得體會也有網上查閱資料時摘抄下的知識內容,所以如有雷同,純屬我向前輩學習的致敬,如果有前輩覺得我的筆記內容侵犯了您的知識產權,請和我聯繫,我會將涉及到的博文內容刪除,謝謝!

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