最近被图像分割整的天昏地暗的,在此感谢老朋友周洋给我关于分水岭算法的指点!本来打算等彩色图像分割有个完满的结果再写这篇文章,但是考虑到到了这一步也算是一个阶段,所以打算对图像分割做一个系列的博文,于是先写这篇。
啰嗦了这么多!先看效果:
效果一般,存在着很多过分割现象,但比没使用滤波之前的效果好很多,过分割是分水岭算法的通病。这个后续博文会继续解决。
本文用java实现的是基于自动种子区域的分水岭算法,注意本文是基于单色的分割,所以将输入图片首先进行灰度化处理,这个比较简单,不多提了;因此,对于彩色图像,会存在一些偏差,这个在后续博文里我会去解决。关于分水岭算法的概念,请自行百度,本系列主讲如何实现以及实现效果。
算法大致分为四步:图像预处理、 种子区域获取、浸水过程(区域增长)、分割结果的合并处理。另外,算法在分水岭过程中的区域表示上,采用并查集的数据结构表示区域块,这样做的好处在于简化生长过程中的合并处理,关于并查集,参见我这篇博文: http://blog.csdn.net/abcd_d_/article/details/40316455。
本文采用二维高斯滤波进行图像(sigma=3.0)的平滑操作,经验证去操平滑效果好于中值滤波,读者可以自己实验其它的滤波。另外,为了简化操作,二维高斯模板可近似为两个一维的高斯模板,也就是在下图中,左边的滤波过程近似为右边的滤波过程。
核心代码为:
/** * * 将图像进行高斯模糊:先利用模糊函数计算高斯模板矩阵,然后进行卷积运算。 * * @高斯模糊 :高斯模糊是一种图像滤波器,它使用正态分布(高斯函数)计算模糊模板,并使用该模板与原图像做卷积运算,达到模糊图像的目的。 * 在实际应用中,在计算高斯函数的离散近似时,在大概3σ距离之外的像素都可以看作不起作用,这些像素的计算也就可以忽略。 * 通常,图像处理程序只需要计算的矩阵就可以保证相关像素影响。 * * @param source * @param index 表示不同的sigma对应的模板 * @return double[][] 模糊后的图像信息矩阵 */ public static double[][] gaussTran(double[][] source,int index){ int height=source.length; int width=source[0].length; ///保存高斯过滤后的结果 double[][] result=new double[height][width]; double[] template=GaussTemplate1D.gettemplateX_Y(index); int tWH=template.length;///模板维数 for(int i=0;i<height;i++){ for(int j=0;j<width;j++){ ///进行模糊处理——————卷积运算 double sum=0.0;///卷积结果 for(int m=0;m<tWH;m++){ ///计算与模板对应的图像上的位置 int x=j-(int)tWH/2+m; int y=i;//-(int)tWH/2+m; //如果模板数据没有超过边界 if(x>=0&&x<width){ sum=sum+source[y][x]*template[m]; } } for(int m=0;m<tWH;m++){ ///计算与模板对应的图像上的位置 int x=j; int y=i-(int)tWH/2+m;//-(int)tWH/2+m; //如果模板数据没有超过边界 if(y>=0&&y<height){ sum=sum+source[y][x]*template[m]; } } result[i][j]=sum/2; } } int i=0; i++; return result; }
效果为(左边是原图):
首先,获取梯度图像:高斯滤波图像使用sobel算子求取梯度图像。
然后,种子区域获取以及标记:梯度图像中,将梯度值小于预先设置的阈值THRESHOLD的像素点的灰度值设置为0,而其他像素点的灰度值等于其梯度值。这样每个像素都标记之后就开始进行区域增长,采用同一区域属于一个并查集的方法,将灰度值为0的区域进行分类标记(这些信息存储在blockData里)。如图,每一个黑色块属于一个类(也就是一个种子区域)。
参照周洋告诉我的方法:三层循环,第一层是梯度值从阈值到最大值,里面两层是二维图像数据的遍历。采用八领域的判断方法,如果某个未被标记为0(未知)的点周围只有一个被标记为零的点(种子),则该点归并到该种子区域中,如果有两个以上被标记的点,并且属于不同的区域,那么该点标记为山脊(轮廓线)。对于外层循环的每一个梯度值,区域生长的停止条件:没有新的点被并入到某个区域。这样其实就有四层循环,因为需要一层来判断是否有新点被并入到种子区域。
效果图如下:
可以看出,分水岭算法的好处在于可以得到一个个封闭区域,这个一般的二值化的轮廓图像是得不到的。
另外,经过对比发现,高斯滤波对减少过分割有着比较理想的作用,但是也不能消除过分割,因此需要有第四步——区域合并。这部分在后续博文中我会继续讲。
因为还未写完,所以代码可能显得比较乱,比如程序中有个map成员变量,实际没多大意义,但考虑到测试用,还望见谅!
附上第一版程序的源码:http://download.csdn.net/detail/abcd_d_/8169869
原文地址:http://blog.csdn.net/abcd_d_/article/details/41218549