opencv視覺跟蹤——CAMshift(meanshift均值漂移)
阿新 • • 發佈:2019-01-13
關於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);//歸一化 } }