opencv---均值遷移應用

opencv提供了一個api函數可以用於均值遷移對象分析

int cv::meanShift (InputArray probImage, Rect & window, TermCriteria criteria )

函數有三個輸入,分別是probImage,表示直方圖反向投影的結果,這是根據預先計算的直方圖計算每一幀的反向投影;window表示初始的搜索窗口,也就是選擇跟蹤的目標;第三個就是均值遷移的停止條件。

看這三個輸入,要想成功使用meanShift這個函數,需要先計算直方圖,再計算反向投影結果,然後劃定搜索窗口,這幾步。

那麼計算直方圖,是計算RGB空間的直方圖還是HSV空間的直方圖呢,當然是HSV空間的直方圖,因爲在HSV空間裏色彩區分度更明顯,有利於圖像分割。

關於均值遷移詳盡的解釋可以參考博客https://blog.csdn.net/jinshengtao/article/details/30258833?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

hsv空間直方圖的計算

//直方圖參數
	int h_bins = 32, s_bins = 32;//將h和s通道分爲32個等級
	int histSize[] = { h_bins,s_bins };
	float h_ranges[] = { 0,180 };//h通道變化範圍
	float s_ranges[] = { 0,256 };//s通道變化範圍
	const float* ranges[] = { h_ranges,s_ranges };
	int channels[] = { 0,1 };//h和s的通道位置
//計算模板的hsv直方圖

	calcHist(&modelHSV, 1, channels, Mat(), roiHist, 2, histSize, ranges, true, false);
	normalize(roiHist, roiHist, 0, 255, NORM_MINMAX, -1, Mat());//標準化

反向投影,opencv有專門的計算反向投影的函數calcBackProject

void cv::calcBackProject (
const Mat * images, 
int nimages, 
const int * channels, 
InputArray hist, 
OutputArray backProject, 
const float ** ranges, 
double scale = 1, 
bool uniform = true 
)

第一個參數表示輸入要反向投影的圖像,第二個參數表示輸入圖像的個數,第三個參數表示用於計算反向投影的通道列表,這個跟計算直方圖的通道列表一樣,第四個參數表示輸入的直方圖,第五個參數表示反向投影的結果,第六個參數表示直方圖的變化範圍,跟計算直方圖的一樣,第七個表示輸出反向投影的比例,第八個表示是否直方圖是否均勻分佈。

具體的可以看官方文檔的解釋https://docs.opencv.org/3.4.2/d6/dc7/group__imgproc__hist.html#ga3a0af640716b456c3d14af8aee12e3ca

選擇搜索窗口,opencv提供了專門的函數可以實時的選擇ROI區域,輸出一個矩形窗口,用鼠標劃定ROI區域後,回車即可將ROI區域輸入。

Rect cv::selectROI (
const String & windowName, 
InputArray img, 
bool showCrosshair = true, 
bool fromCenter = false 
)

看看結果

完整代碼

#include <opencv2/opencv.hpp>"
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
	VideoCapture capture;
	capture.open("2.mp4");
	if (!capture.isOpened()) {
		cout << "problem!" << endl;
		return -1;
	}
	//選擇模板區域
	Mat frame, model;
	capture.read(frame);
	Rect selection = selectROI(frame, true, false);
	
	int trackObject = 0;//確定是否選擇模板
	//直方圖參數
	int h_bins = 32, s_bins = 32;//將h和s通道分爲32個等級
	int histSize[] = { h_bins,s_bins };
	float h_ranges[] = { 0,180 };//h通道變化範圍
	float s_ranges[] = { 0,256 };//s通道變化範圍
	const float* ranges[] = { h_ranges,s_ranges };
	int channels[] = { 0,1 };//h和s的通道位置
	Mat roiHist;
	while (true) 
	{
		capture >> frame;//讀取每一幀
		if (frame.empty())break;//視頻播放完結束
		Mat modelHSV, frameHSV;
		cvtColor(frame, frameHSV, COLOR_BGR2HSV);//將每一幀灰度化
		if (trackObject <= 0) {//初始化,如果第一次選擇模板,則計算模板的直方圖,
			model = frame(selection);//提取模板圖像
			trackObject = 1;
			cvtColor(model, modelHSV, COLOR_BGR2HSV);//灰度化
			
			//計算模板的hsv直方圖
			calcHist(&modelHSV, 1, channels, Mat(), roiHist, 2, histSize, ranges, true, false);
			normalize(roiHist, roiHist, 0, 255, NORM_MINMAX, -1, Mat());//標準化
		}
		
		MatND backproj;
		calcBackProject(&frameHSV, 1, channels, roiHist, backproj, ranges, 1.0);//反向投影
		meanShift(backproj, selection, TermCriteria(TermCriteria::EPS | TermCriteria::COUNT, 10, 1));//均值遷移
		rectangle(frame, selection, Scalar(0, 0, 255), 1, 8);//畫出目標的位置
		imshow("result", frame);
		char c = waitKey(50);
		if (c == 27)break;
	}
	return 0;
}

 

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