Meanshift不僅可以用於圖像濾波,視頻跟蹤,還可以用於圖像分割。
一般而言一副圖像的特征點至少可以提取出5維,即(x,y,r,g,b),眾所周知,meanshift經常用來尋找模態點,即密度最大的點。所以這里同樣可以用它來尋找這5維空間的模態點,由於不同的點最終會收斂到不同的峰值,所以這些點就形成了一類,這樣就完成了圖像分割的目的,有點聚類的意思在里面。
有一點需要注意的是圖像像素的變化范圍和坐標的變化范圍是不同的,所以我們在使用窗口對這些數據點進行模態檢測時,需要使用不同的窗口半徑。因此在opencv自帶的meanshift分割函數pyrMeanShiftFiltering()函數中,就專門有2個參數供選擇空間搜索窗口半徑和顏色窗口搜索半徑的。
由函數名pyrMeanShiftFiltering可知,這里是將meanshift算法和圖像金字塔相結合用來分割的,所以其參數列表中就有一個專門定義所需金字塔層數的變量。
本次試驗來源於opencv2.3.1版本中自帶的一個sample。其主要過程是,首先設置好參數,然后用函數pyrMeanShiftFiltering()對輸入的圖像進行分割。分割后的結果保存在該函數的第二個參數即輸出圖像中,最后根據該分割圖像的特點用floodFill()函數對其分割的結果用不同的顏色進行填充。當然該函數的使用暫時沒有徹底的弄清楚。
實驗結果如下:
分割前的圖片:
分割后的圖片:
不同的顏色代表不同的分割類別。
關於本次試驗的一點總結:
對createTrackbar()函數的使用,即滑動條函數,該函數有個參數就是滑動條響應函數的參數為(int,void *),這個是固定的,不能改。但是在調用的時候可以不寫,函數實現的時候當然不能少了,見代碼注釋。
之所以createTrackbar()能響應,是因為主函數中使用了waitKey()函數,該函數不僅能夠無限等待,還能夠與用戶進行交互,見代碼注釋。
關於本程序經常運行着會報內存錯誤,比如有時拖動滑動條就會內存報錯,不知道為什么,opencv自帶的sample,其代碼一般是很優秀的。不過opencv本身的bug也不少,一直在完善中,情有可原。也有可能是自己稍微改了其代碼造成的,不過本人認為這個應該不是主要原因。
實驗代碼如下:
1 // meanshift_segmentation.cpp : 定義控制台應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <opencv2/core/core.hpp> 6 #include <opencv2/highgui/highgui.hpp> 7 #include <opencv2/imgproc/imgproc.hpp> 8 #include <iostream> 9 10 using namespace cv; 11 using namespace std; 12 13 14 Mat src,dst; 15 int spatialRad=10,colorRad=10,maxPryLevel=1; 16 //const Scalar& colorDiff=Scalar::all(1); 17 18 void meanshift_seg(int,void *) 19 { 20 //調用meanshift圖像金字塔進行分割 21 pyrMeanShiftFiltering(src,dst,spatialRad,colorRad,maxPryLevel); 22 RNG rng=theRNG(); 23 Mat mask(dst.rows+2,dst.cols+2,CV_8UC1,Scalar::all(0)); 24 for(int i=0;i<dst.rows;i++) //opencv圖像等矩陣也是基於0索引的 25 for(int j=0;j<dst.cols;j++) 26 if(mask.at<uchar>(i+1,j+1)==0) 27 { 28 Scalar newcolor(rng(256),rng(256),rng(256)); 29 floodFill(dst,mask,Point(i,j),newcolor,0,Scalar::all(1),Scalar::all(1)); 30 // floodFill(dst,mask,Point(i,j),newcolor,0,colorDiff,colorDiff); 31 } 32 imshow("dst",dst); 33 } 34 35 36 int main(int argc, uchar* argv[]) 37 { 38 39 namedWindow("src",WINDOW_AUTOSIZE); 40 namedWindow("dst",WINDOW_AUTOSIZE); 41 42 src=imread("stuff.jpg"); 43 CV_Assert(!src.empty()); 44 45 spatialRad=10; 46 colorRad=10; 47 maxPryLevel=1; 48 49 //雖然createTrackbar函數的參數onChange函數要求其2個參數形式為onChange(int,void*) 50 //但是這里是系統響應函數,在使用createTrackbar函數時,其調用的函數可以不用寫參數,甚至 51 //括號都不用寫,但是其調用函數的實現過程中還是需要滿足(int,void*)2個參數類型 52 createTrackbar("spatialRad","dst",&spatialRad,80,meanshift_seg); 53 createTrackbar("colorRad","dst",&colorRad,60,meanshift_seg); 54 createTrackbar("maxPryLevel","dst",&maxPryLevel,5,meanshift_seg); 55 56 // meanshift_seg(0,0); 57 58 imshow("src",src); 59 /*char c=(char)waitKey(); 60 if(27==c) 61 return 0;*/ 62 imshow("dst",src); 63 waitKey();//無限等待用戶交互響應 64 // while(1);//這里不能用while(1)的原因是需要等待用戶的交互,而while(1)沒有該功能。雖然2者都有無限等待的作用。 65 return 0; 66 }