新手学,java使用分水岭算法进行图像分割(一)

最近被图像分割整的天昏地暗的,在此感谢老朋友周洋给我关于分水岭算法的指点!本来打算等彩色图像分割有个完满的结果再写这篇文章,但是考虑到到了这一步也算是一个阶段,所以打算对图像分割做一个系列的博文,于是先写这篇。

啰嗦了这么多!先看效果:

效果一般,存在着很多过分割现象,但比没使用滤波之前的效果好很多,过分割是分水岭算法的通病。这个后续博文会继续解决。

本文用java实现的是基于自动种子区域的分水岭算法,注意本文是基于单色的分割,所以将输入图片首先进行灰度化处理,这个比较简单,不多提了;因此,对于彩色图像,会存在一些偏差,这个在后续博文里我会去解决。关于分水岭算法的概念,请自行百度,本系列主讲如何实现以及实现效果。

算法大致分为四步:图像预处理、 种子区域获取、浸水过程(区域增长)、分割结果的合并处理。另外,算法在分水岭过程中的区域表示上,采用并查集的数据结构表示区域块,这样做的好处在于简化生长过程中的合并处理,关于并查集,参见我这篇博文: http://blog.csdn.net/abcd_d_/article/details/40316455

1、图像预处理过程:

本文采用二维高斯滤波进行图像(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;
	}

效果为(左边是原图):

2、种子区域选取:

首先,获取梯度图像:高斯滤波图像使用sobel算子求取梯度图像。

然后,种子区域获取以及标记:梯度图像中,将梯度值小于预先设置的阈值THRESHOLD的像素点的灰度值设置为0,而其他像素点的灰度值等于其梯度值。这样每个像素都标记之后就开始进行区域增长,采用同一区域属于一个并查集的方法,将灰度值为0的区域进行分类标记(这些信息存储在blockData里)。如图,每一个黑色块属于一个类(也就是一个种子区域)。

3、浸水过程(也就是区域生长过程):

参照周洋告诉我的方法:三层循环,第一层是梯度值从阈值到最大值,里面两层是二维图像数据的遍历。采用八领域的判断方法,如果某个未被标记为0(未知)的点周围只有一个被标记为零的点(种子),则该点归并到该种子区域中,如果有两个以上被标记的点,并且属于不同的区域,那么该点标记为山脊(轮廓线)。对于外层循环的每一个梯度值,区域生长的停止条件:没有新的点被并入到某个区域。这样其实就有四层循环,因为需要一层来判断是否有新点被并入到种子区域。

效果图如下:

可以看出,分水岭算法的好处在于可以得到一个个封闭区域,这个一般的二值化的轮廓图像是得不到的。

另外,经过对比发现,高斯滤波对减少过分割有着比较理想的作用,但是也不能消除过分割,因此需要有第四步——区域合并。这部分在后续博文中我会继续讲。

因为还未写完,所以代码可能显得比较乱,比如程序中有个map成员变量,实际没多大意义,但考虑到测试用,还望见谅!

附上第一版程序的源码:http://download.csdn.net/detail/abcd_d_/8169869

时间: 2024-10-06 13:59:36

新手学,java使用分水岭算法进行图像分割(一)的相关文章

新手学,java使用分水岭算法进行图像分割(二)

最近要考试了,所以现在不写,怕这段时间都没空写了.(算法的效率暂时不考虑,有时间了再来解决彩色信息和效率问题) 继上一篇的算法:http://blog.csdn.net/abcd_d_/article/details/41218549,本文对分水岭算法进行了区域合并,合并准则采用hsv颜色空间的区域特征的直方图相似度进行合并.且看效果:图一是原图,图二是采用之前的文章算法的效果,图三为进行了区域合并后的效果.(大小被我调整过) (图一) (图二) (图三) 在第一篇的基础之上,增加了区域合并算法

新手学JAVA(三)----StringBuilder类

上一篇文章新手学JAVA(二)----String类与StringBuffer类的区别中了解到,String的值是不可变的,这就导致 每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间,StringBuffer是可变 类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象. StringBuffer类和StringBuilder类功能基本相似.算是两个双胞胎. 下面主要说两点 第一点  线程安全 StringBuffer  线程安全

【经验分享】新手学Java编程语言怎么入门?

新手学Java编程语言怎么入门?掌握语言基础是第一步,了解java基础知识,Java关键字.核心概念或者基本编码技术.掌握操作符.控制执行流程.访问权限控制.复用类.多态.接口.字符串.泛型.数组.容器深入研究.JavaI/O系统.枚举类型.并发以及图形化用户界面等内容. 为了帮助大家更轻松的学好java开发,给大家分享一套java开发学习资料,小编推荐一个学java开发技术的学习裙:三七四三二零二八二,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干

新手学,java使用分水岭算法进行图像切割(一)

近期被图像切割整的天昏地暗的,在此感谢老朋友周洋给我关于分水岭算法的指点!本来打算等彩色图像切割有个完满的结果再写这篇文章,可是考虑到到了这一步也算是一个阶段,所以打算对图像切割做一个系列的博文,于是先写这篇. 啰嗦了这么多!先看效果: 效果一般,存在着非常多过切割现象,但比没使用滤波之前的效果好非常多,过切割是分水岭算法的通病.这个兴许博文会继续解决. 本文用java实现的是基于自己主动种子区域的分水岭算法,注意本文是基于单色的切割,所以将输入图片首先进行灰度化处理,这个比較简单,不多提了:因

新手转行学java难吗?新手学java需要注意的6个方面!

新手转行在成都学java到底难不难,对于这个问题,我们专门做过一个调查,超过1000名已经在职的java从业者,其中有80%的程序员觉得学java不难,20%的程序员觉得前期有点难,其中对于50%自学的新手认为java很难学,遇到问题没人解答,又或者是答案太多,根本不知道哪个回答是正确的.所以,我们得出结论,能系统学习的,有老师指导的java课程学起来相对容易,而没有人指导,仅仅靠看视频学习的同学来说会无形中增加难度. 很多新手在准备转行学习java之前,在网上看到或听到很多不懂的人会说,jav

新手学Java笔记心得之Java基础

第一次学习Java,一些基本的常用的快捷方式和一些很基础的知识点(笔记比较零散比较乱) Java是一种既面向对象又可以跨平台的语言,即:编写一次,随处运行: Java不仅适用于单机应用程序和基于网络的程序,也可用于创建消费类设备的附件程序,如移动电话.掌上设备等. Java命名规范首字母可以是字母,下划线'_','$'符号后面加上任意多的数字,字母,下划线,'$'符号通常第一个单词的首字母小写,其后单词的 首字母大写.例如:myScore MyEclipse编写Java代码的快捷键(新手不推荐使

新手学JAVA(六)----处理随机性的数据

基础知识 实例 在我们的日常生活中会遇到很多随机性的事情,比如:摇奖,彩票,掷色子,这些都可以通过程序计算其中奖的概率.在JAVA的类库中,有一个专门操作这种随机性数据的类--Random类. 基础知识 Random类一般使用在那些随机性比较强的场合,因为这些场合都含有一种随机的特性,也就是使用同样的方法去操作数据,每次操作的结果都不一样. 下面是Random类的常用方法: 方法 表示 public boolean nextBoolean() 返回true或者是false public doub

新手学Java(一)——Equals和“==”的区别

一句话概括: 如果操作两边都是对象句柄,就比较两个句柄是否指向同一个对象.如果两边是基本类型,则比较的就是值. ==不属于任何类,而equals则是任何类(Java中)的一个方法. equals()是每个对象与生俱来的方法,因为所有类的最终基类就是Object(除去Object本身):而equals()是Object的方法之一. 我们可以这样比较: 1)Primitive1(基本类型)== Primitive2(基本类型) 2)Object1 Reference1(对象引用)== Object1

新手学JAVA(七)----Override VS Overload

一 Overload 二 Override 三 多态性 一. Overload 在同一个类中的许多方法可以拥有相同的名字,只要他们的参数声明不同即可,这种方法被称为重载. 举个例子: public class overload{ '''一个重则输出方法''' void print(){ System.out.println("Hello!"); } '''重载上面的输出方法,加上了参数''' void print(String name) { System.out.println(na