opencv提供了一個api函數可以用於均值遷移對象分析
int cv::meanShift (InputArray probImage, Rect & window, TermCriteria criteria )
函數有三個輸入,分別是probImage,表示直方圖反向投影的結果,這是根據預先計算的直方圖計算每一幀的反向投影;window表示初始的搜索窗口,也就是選擇跟蹤的目標;第三個就是均值遷移的停止條件。
看這三個輸入,要想成功使用meanShift這個函數,需要先計算直方圖,再計算反向投影結果,然後劃定搜索窗口,這幾步。
那麼計算直方圖,是計算RGB空間的直方圖還是HSV空間的直方圖呢,當然是HSV空間的直方圖,因爲在HSV空間裏色彩區分度更明顯,有利於圖像分割。
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;
}