java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)

本文用到了卷积的内容,如有了解较少的同学建议转到java图像处理(卷积,强调边缘,平滑与高斯模糊)先了解一下

文章目录

        • 基础知识
        • 算子的实现
          • 1、Roberts算子
          • 2、Sobel算子
          • 3、Prewitt算子
          • 4、Krisch算子
          • 5、Laplace算子

基础知识

1、边缘
图像边缘是图像最基本的特征。
所谓边缘(Edge) 是指图像局部特性的不连续性。灰度或结构等信息的突变处称之为边缘。例如,灰度级的突变、颜色的突变,、纹理结构的突变等。
边缘是一个区域的结束,也是另一个区域的开始,利用该特征可以分割图像。
java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)_第1张图片
2、导数
图像的边缘有方向和幅度两种属性。

边缘通常可以通过一阶导数或二阶导数检测得到。
一阶导数是以最大值作为对应的边缘的位置,而二阶导数则以过零点作为对应边缘的位置。

3、梯度

为了达到寻找边缘的目的,检测灰度变化可用一阶导数或二阶导数来完成。下面将讨论一阶导数。

为了在一幅图像f 的(x,y)位置处寻找边缘的强度和方向,所以选择的工具就是梯度,梯度用▽f来表示,并用向量来定义,定义如下所示:
在这里插入图片描述
其中,梯度▽f 为一个向量,它表示f 在位置(x,y) 处的最大变化率的方向,计算方法为
在这里插入图片描述

对于以为函数f(x) 在点 x 处的导数的近似:将函数f(x+△ x) 展开为 x 的泰勒级数,令△ x=1,且只保该级数的线性项,则函数f(x) 的梯度▽f 计算为:
在这里插入图片描述
由上面的数学推导可知,要得到一幅图像的梯度,则要求在图像的每个像素点位置处计算偏导数。我们处理的是数字量,因此要求关于一点的邻域上的偏导数的数字近似,因此一幅图像f,在(x,y)位置处的x和y方向上的梯度大小分别计算为:
java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)_第2张图片
矩阵表示为
java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)_第3张图片
用于计算梯度偏导数的滤波器模板,通常称之为梯度算子、边缘算子和边缘检测子等。

我们可以通过泰勒级数的展开找到可以表示成卷积的可能性

(借鉴于此链接)

算子的实现

(我爱康娜)

前三个算子都是水平垂直方向进行的边缘检测,使用以下两个卷积核对同一点进行操作,然后取梯度值作为测试灰度值,再设置一个阈值,根据阈值判断是否是边界点(图中的max值)

1、Roberts算子

在这里插入图片描述
java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)_第4张图片

2、Sobel算子

在这里插入图片描述
java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)_第5张图片

3、Prewitt算子

在这里插入图片描述
java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)_第6张图片

4、Krisch算子

Krisch算子和Robinson算子都是八个方向进行的边缘检测,使用八个卷积核对同一点进行操作,每个卷积核代表不同的方向,然后取最大值作为测试灰度值
这里只以Krisch算子作为实例
java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)_第7张图片

所指方向即为检测方向
java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)_第8张图片java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)_第9张图片

5、Laplace算子

Laplace算子是在卷积核上直接实现了四个方向或八个方向的边缘检测,即一个卷积核代表八个方向的检测值
在这里我们只实现八个方向的拉普拉斯算子的效果
四个方向的算子卷积核:{ { 0 , 1 , 0 } , { 1 , -4 , 1 } , { 0 , 1 , 0 } }
在这里插入图片描述
java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)_第10张图片
java实现常见边缘检测算子效果(拉普拉斯,Sobel,Robert,Prewitt,Krisch算子)_第11张图片
根据结果我们可以看出,Roberts算子的效果还不错,但是阈值的要求较高,而且准确度相对低;Prewitt算子对相邻点的处理不佳;Krisch算子与Laplace算子的噪点相对较高,Sobel算子效果最好。

算子处的代码,请注意注释内容

	public void ceshi(String text){
		int size = 0;
		//根据不同请求调用不同的处理方法
		if(text.equals("Roberts边缘检测"))size = 2;
		if(text.equals("Sobel边缘检测"))size = 3;
		if(text.equals("Prewitt边缘检测"))size = 3;
		if(text.equals("Krisch边缘检测"))size = 3;
		if(text.equals("Laplace边缘检测"))size = 3;
		//设置算子的卷积核
		//Roberts算子卷积核
		double[][] robertsX = {{1,0},{0,-1}};
		double[][] robertsY = {{0,1},{-1,0}};
		//Sobel算子卷积核
		double[][] sobelX = {{1,0,-1},{2,0,-2},{1,0,-1}};
		double[][] sobelY = {{1,2,1},{0,0,0},{-1,-2,-1}};
		//Prewitt算子卷积核
		double[][] prewittX = {{-1,0,1},{-1,0,1},{-1,0,1}};
		double[][] prewittY = {{1,1,1},{0,0,0},{-1,-1,-1}};
		//Krisch算子卷积核
		double[][] krischN = {{5,5,5},{-3,0,-3},{-3,-3,-3}};
		double[][] krischNE = {{-3,5,5},{-3,0,5},{-3,-3,-3}};
		double[][] krischE = {{-3,-3,5},{-3,0,5},{-3,-3,5}};
		double[][] krischSE = {{-3,-3,-3},{-3,0,5},{-3,5,5}};
		double[][] krischS = {{-3,-3,-3},{-3,0,-3},{5,5,5}};
		double[][] krischSW = {{-3,-3,-3},{5,0,-3},{5,5,-3}};
		double[][] krischW = {{5,-3,-3},{5,0,-3},{5,-3,-3}};
		double[][] krischNW = {{5,5,-3},{5,0,-3},{-3,-3,-3}};
		//Laplace算子卷积核
		double[][] laplace = {{1,1,1},{1,-8,1},{1,1,1}};
		//首先将图片灰度化
		toGray();
		//下面代码中width代表图片宽度,height代表图片高度,gray为图片的灰度数组,存储的是每个像素点的灰度值
		//前三个算子请仔细读第一个算子的代码,之后的是一样的
		if(text.equals("Roberts边缘检测")){
			for(int x = 0;x < width-size+1;x++){
				for(int y = 0;y < height-size+1;y++){
					//设置x,y方向的结果变量,一定要在循环内初始化,因为每次循环都要清零重新加
					int tempX = 0;
					int tempY = 0;
					//对size*size区域进行卷积操作
					for(int i = 0;i < size;i++){
						for(int j = 0;j < size;j++){
							tempX += gray[x+i][y+j]*robertsX[i][j];
							tempY += gray[x+i][y+j]*robertsY[i][j];
						}
					}
					//求梯度值
					int result = (int) Math.sqrt(tempX*tempX+tempY*tempY);
					//设置阈值
					int RMax = 200;
					//根据阈值设置黑白度
					if(result > RMax)result = 255;
					if(result <= RMax)result = 0;
					//设置颜色来描点
					Color color = new Color(result,result,result);
					graphics.setColor(color);
					graphics.drawLine(x+size/2,y+size/2, x+size/2, y+size/2);
				}
			}
		}
		if(text.equals("Sobel边缘检测")){
			for(int x = 0;x < width-size+1;x++){
				for(int y = 0;y < height-size+1;y++){
					int tempX = 0;
					int tempY = 0;
					for(int i = 0;i < size;i++){
						for(int j = 0;j < size;j++){
							tempX += gray[x+i][y+j]*sobelX[i][j];
							tempY += gray[x+i][y+j]*sobelY[i][j];
						}
					}
					int result = (int) Math.sqrt(tempX*tempX+tempY*tempY);
					//设置阈值
					int GMax = 200;
					
					if(result > GMax)result = 255;
					if(result <= GMax)result = 0;
					
					Color color = new Color(result,result,result);
					graphics.setColor(color);
					graphics.drawLine(x+size/2,y+size/2, x+size/2, y+size/2);
				}
			}
		}
		if(text.equals("Prewitt边缘检测")){
			for(int x = 0;x < width-size+1;x++){
				for(int y = 0;y < height-size+1;y++){
					int tempX = 0;
					int tempY = 0;
					for(int i = 0;i < size;i++){
						for(int j = 0;j < size;j++){
							tempX += gray[x+i][y+j]*prewittX[i][j];
							tempY += gray[x+i][y+j]*prewittY[i][j];
						}
					}
					int result = (int) Math.sqrt(tempX*tempX+tempY*tempY);
					//设置阈值
					int PMax = 200;
					
					if(result > PMax)result = 255;
					if(result < PMax)result = 0;
					
					Color color = new Color(result,result,result);
					graphics.setColor(color);
					graphics.drawLine(x+size/2,y+size/2, x+size/2, y+size/2);
				}
			}
		}
		if(text.equals("Krisch边缘检测")){
			for(int x = 0;x < width-size+1;x++){
				for(int y = 0;y < height-size+1;y++){
					//设置一个数组存储八个方向的值,按顺时针方向从北极开始
					int[] temp = {0,0,0,0,0,0,0,0};
					//对size*size区域进行卷积操作
					for(int i = 0;i < size;i++){
						for(int j = 0;j < size;j++){
							temp[0] += gray[x+i][y+j]*krischN[i][j];
							temp[1] += gray[x+i][y+j]*krischNE[i][j];
							temp[2] += gray[x+i][y+j]*krischE[i][j];
							temp[3] += gray[x+i][y+j]*krischSE[i][j];
							temp[4] += gray[x+i][y+j]*krischS[i][j];
							temp[5] += gray[x+i][y+j]*krischSW[i][j];
							temp[6] += gray[x+i][y+j]*krischW[i][j];
							temp[7] += gray[x+i][y+j]*krischNW[i][j];
						}
					}
					;
					//找出八个方向的最大值(代码为数组列表求最大值)
					int result = Arrays.stream(temp).max().getAsInt();
					//若是求得结果超出灰度值的0-255范围,将其设成最大值或最小值
					if(result > 255)result = 255;
					if(result < 0)result = 0;
					//画图
					Color color = new Color(result,result,result);
					graphics.setColor(color);
					graphics.drawLine(x+size/2,y+size/2, x+size/2, y+size/2);
				}
			}
		}
		if(text.equals("Laplace边缘检测")){
			for(int x = 0;x < width-size+1;x++){
				for(int y = 0;y < height-size+1;y++){
					int result = 0;
					//对size*size区域进行卷积操作
					for(int i = 0;i < size;i++){
						for(int j = 0;j < size;j++){
							result += gray[x+i][y+j]*laplace[i][j];
						}
					}
					//设置阈值
					int LMax = 150;
					//根据阈值设置黑白度
					if(result > LMax)result = 255;
					if(result < LMax)result = 0;
					//画图
					Color color = new Color(result,result,result);
					graphics.setColor(color);
					graphics.drawLine(x+size/2,y+size/2, x+size/2, y+size/2);
				}
			}
		}

你可能感兴趣的:(Java)