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 #include6 #include 7 #include 8 #include 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 //opencv图像等矩阵也是基于0索引的 25 for(int j=0;j ) 26 if(mask.at (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 }