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