Mean Shift算法(3)在OpenCV上的实现——图像分割PyrMeanShiftFiltering

      Meanshift不仅可以用于视频跟踪,还可以用于图像分割。Meanshift算法找到空间上颜色分布的峰值,关注于颜色的空间分布;在视频跟踪领域组更关注于在连续帧中通过事件跟踪这些分布。

      一般而言一副图像的特征点至少可以提取出5维,即(x,y,r,g,b),众所周知,meanshift就是寻找概率密度最大的点(最稠密的地方)。所以这里同样可以用它来寻找这5维空间的密度最大点,由于不同的点最终会收敛到不同的峰值,所以这些点就形成了一类,这样就完成了图像分割的目的,有点聚类的意思在里面。( 图像分割就是求每一个像素点的类标号。类标号取决于它在特征空间所属的cluster。对于每一个cluster,首先得有个类中心,它深深地吸引着一些点,就形成了一个类,好比咱们的太阳系。图像分割问题可以看成对每个像素点,找它的类中心问题。

  有一点需要注意的是图像像素的变化范围和坐标的变化范围是不同的,所以我们在使用窗口对这些数据点进行模态检测时,需要使用不同的窗口半径。因此在opencv自带的meanshift分割函数pyrMeanShiftFiltering()函数中,就专门有2个参数供选择空间搜索窗口半径sr和颜色窗口搜索半径sp的;由函数名pyrMeanShiftFiltering可知,这里是将meanshift算法和图像金字塔相结合用来分割的,所以其参数列表中就有一个专门定义所需金字塔层数的变量max_level。

void PyrMeanShiftFiltering( const CvArr* srcarr,          //输入图像
				    CvArr* dstarr,        //输出图像
				   double  sp,            //颜色域半径
				    double sr,            //空间域半径
				       int max_level,     //金字塔最大层数                    
			    CvTermCriteria termcrit )     //迭代终止条件

src :输入的8-比特,3-信道图象.
dst:和源图象相同大小,相同格式的输出图象.
sp:空间窗的半径
sr:色彩窗的半径
max_level:金字塔的最大层次

      我们先借助Mean Shift算法的分割特性将灰度值相近的元素进行聚类,然后,在此基础上应用阈值分割算法,达到将图像与背景分离的目的。 简单来说,基于Mean Shift的图像分割过程就是首先利用Mean Shift算法对图像中的像素进行聚类,即把收敛到同一点的起始点归为一类,然后把这一类的标号赋给这些起始点,同时把包含像素点太少的类去掉。然后,采用阈值化分割的方法对图像进行二值化处理 基于Mean Shift的图像分割算法将图像中灰度值相近的像素点聚类为一个灰度级,因此,经过Mean Shift算法分割后的图像中的灰度级较该算处理有所减少。 

     该函数严格意义上并不是分割,而是在色彩层面的平滑滤波,中和与色彩分布相近的颜色,平滑掉细节色彩,侵蚀掉面积较小的颜色区域。函数的输出是一个“色调分离”的新图像,意味着去除了精细纹理、颜色梯度大部分变得平坦。如果最终需要图像分割,就可以cv::Canny()结合cv::findContours()进一步细分此类图像。

    参数说明:输入输出必须为8位三通道彩色图像,高宽相同;搜索窗口半径sr和颜色窗口搜索半径sp,对于640*480图像,sr=2, sp=40,max_level=2或3时,效果最好。

     执行过程:

1、迭代空间构建:以(x,y,r,g,b),构成5维空间球体,以点P为圆心(xp,yp,rp,gp,bp);

2、在球形空间内,求所有点相对中心点P的色彩向量之和W后,移动中心点P到该向量W的终点,作为新的中心点P1,重复步骤1,直至中心点Pn不在移动,满足迭代终止条件;

3、然后更新dst上对应的初始点P的色彩值为本轮迭代终点Pn的色彩值,如此完成了一个像素点的均值漂移;

4、对src图像上每个点依次执行1/2/3步骤,遍历所有像素点,完成均值漂移滤波。

(效果好,但计算耗时)

OpenCV实现:

Mat src, dst,mask;
int spatialRadius = 10, colorRadius = 10, maxLevel = 1;
void meanshift(int, void*);
int main() {
	src = imread("123.png", 1);
	namedWindow("dst");
	createTrackbar("spatialRadius", "dst", &spatialRadius, 60, meanshift);
	createTrackbar("colorRadius", "dst", &colorRadius, 60, meanshift);
	createTrackbar("maxLevel", "dst", &maxLevel, 5, meanshift);
	imshow("src", src); 
	imshow("dst", src);//未使用滑动条时,显示原图,调动滑动条时,meanshift会刷新图片
	waitKey();
}
void meanshift(int, void*) {
	pyrMeanShiftFiltering(src, dst, spatialRadius, colorRadius, maxLevel);
	RNG rng = theRNG();
	for (int i = 100; i<130; i++)    //opencv图像等矩阵也是基于0索引//以局部为例,因为全部遍历运行速度太慢,
		for (int j = 100; j<140; j++)
			{
				Scalar newcolor(rng(256), rng(256), rng(256));
				floodFill(dst, Point(j, i), newcolor, 0, Scalar::all(1), Scalar::all(1));
			}
	imshow("dst", dst);
	cout << "ok" << endl;
}

执行结果:

 

你可能感兴趣的:(opencv)