opencv-python:17_图像经典边缘检测算子(边缘检测、图像梯度、Roberts算子、Prewitt算子、Sobel 算子、Laplacian 算子、Canny算子、算子优缺点对比)

图像边缘检测

  • 图像梯度,图像边界等
  • 相关函数:cv2.Sobel(),cv2.Schar(),cv2.Laplacian()

边缘检测

  • 边缘检测是检测图像中的一些像素点,它们周围的像素点的灰度发生了急剧的变化,在此过程中,图像中的物体不同导致了这一变化,因此可以将这些像素点作为一个集合,可以用来标注图像中不同物体的边界。

  • 边缘区域的灰度剖面可以看作是一个阶跃,即图像的灰度在一个很小的区域内变化到另一个相差十分明显的区域。

  • 边缘是图像中的重要的结构性特征,边缘往往存在于目标和背景之间,不同的区域之间,因此它可以作为图像分割的重要依据。

  • 在边缘检测中,它提取的是图像中不连续部分的特征,将闭合的边缘提取出来便可以作为一个区域。

  • 与区域划分相比,边缘检测不需要逐个的对像素进行比较,比较适合大图像的处理.

边缘其实就是图像上灰度级变化很快的点的集合。这些点往往梯度很大。

图像的梯度可以用一阶导数和二阶偏导数来求解。

图像数据以矩阵的形式存储的,所以不能像对直线或者曲线求导一样,对一幅图像的求导相当于对一个平面、曲面求导。

对图像的求导操作,我们采用模板对原图像进行卷积运算,从而达到我们想要的效果。而获取一幅图像的梯度就转化为:模板(Roberts、Prewitt、Sobel、Lapacian算子)对原图像进行卷积。

使用一阶导的算子有,prewitt,sobel和canny;使用二阶导的有lapacian。

一、梯度原理

  • 梯度简单来说就是求导。

  • OpenCV 提供了三种不同的梯度滤波器,或者说高通滤波器:Sobel,Scharr 和 Laplacian。

  • Sobel,Scharr 其实就是求一阶或二阶导数。Scharr 是对 Sobel(使用小的卷积核求解求解梯度角度时)的优化。Laplacian 是求二阶导数。

二、Roberts算子

Roberts 算子又称为交叉微分算子,它是基于交叉差分的梯度算法,通过局部差分计算检测边缘线条。常用来处理具有陡峭的低噪声图像,当图像边缘接近于正 45 度或负 45 度时,该算法处理效果更理想。其缺点是对边缘的定位不太准确,提取的边缘线条较粗。

对应的模板:

三、Prewitt算子

  • Prewitt 是一种图像边缘检测的微分算子,其原理是利用特定区域内像素灰度值产生的差分实现边缘检测。由于 Prewitt 算子采用 33 模板对区域内的像素值进行计算,而Robert算子的模板是 22,故 Prewitt 算子的边缘检测结果在水平方向和垂直方向均比 Robert 算子更加明显,Prewitt算子适合用来识别噪声较多,灰度渐变的图像。

  • Prewitt算子是一种一阶微分算子的边缘检测,利用像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作用 。其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。

  • 经典Prewitt算子认为:凡灰度新值大于或等于阈值的像素点都是边缘点。即选择适当的阈值T,若P(i,j)≥T,则(i,j)为边缘点,P(i,j)为边缘图像。这种判定是欠合理的,会造成边缘点的误判,因为许多噪声点的灰度值也很大,而且对于幅值较小的边缘点,其边缘反而丢失了。

  • Prewitt算子对噪声有抑制作用,抑制噪声的原理是通过像素平均,但是像素平均相当于对图像的低通滤波,所以Prewitt算子对边缘的定位不如Roberts算子。

因为平均能减少或消除噪声,Prewitt梯度算子法就是先求平均,再求差分来求梯度。水平和垂直梯度模板分别为:

该算子与Sobel算子类似,只是权值有所变化,但两者实现起来功能还是有差距的,据经验得知Sobel要比Prewitt更能准确检测图像边缘。

四、Sobel 算子

  • Sobel 算子是高斯平滑与微分操作的结合体,所以它的抗噪声能力很好。

  • Sobel算子是一种用于边缘检测的离散微分算子,它结合了高斯平滑和微分求导。该算子用于计算图像明暗程度近似值。根据图像边缘旁边明暗程度把该区域内超过某个数的特定点记为边缘。

  • Sobel 算子在Prewitt算子的基础上增加了权重的概念,认为相邻点的距离远近对当前像素点的影响是不同的,距离越近的像素点对应当前像素的影响越大,从而实现图像锐化并突出边缘轮廓。

  • Sobel算子的边缘定位更准确,常用于噪声较多,灰度渐变的图像。

你可以设定求导的方向(xorder 或 yorder)。还可以设定使用的卷积核的大小(ksize)。如果 ksize=-1,会使用 3x3 的 Scharr 滤波器,它的的效果要比 3x3 的 Sobel 滤波器好(而且速度相同,所以在使用 3x3 滤波器时应该尽量使用 Scharr 滤波器)。3x3 的 Scharr 滤波器卷积核如下:

  • Sobel算子是典型的基于一阶导数的边缘检测算子,由于该算子中引入了类似局部平均的运算,因此对噪声具有平滑作用,能很好的消除噪声的影响。Sobel算子是在Prewitt算子的基础上改进的,在中心系数上使用一个权值,与Prewitt算子、Roberts算子相比因此效果更好,能较好的抑制(平滑)噪声。

  • Sobel 算子根据像素点上下,左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘,对噪声具有平滑作用,提供较为精确的边缘方向信息。因为Sobel算子结合了高斯平滑和微分求导(分化),因此结果会具有更多的抗噪性,当对精度要求不是很高时,Sobel 算子是一种较为常用的边缘 检测方法。

  • dst = Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])

    • src表示输入图像

    • dst表示输出的边缘图,其大小和通道数与输入图像相同

    • ddepth表示目标图像所需的深度,针对不同的输入图像,输出目标图像有不同的深度。-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度;

    • dx表示x方向上的差分阶数,取值1或 0,0表示这个方向上没有求导。

    • dy表示y方向上的差分阶数,取值1或0,0表示这个方向上没有求导。

    • ksize表示Sobel算子的大小,其值必须是正数和奇数。

    • scale表示缩放导数的比例常数,默认情况下没有伸缩系数。

    • delta表示将结果存入目标图像之前,添加到结果中的可选增量值。

    • borderType表示判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。

  • 注意,在进行Sobel算子处理之后,还需要调用convertScaleAbs()函数计算绝对值,并将图像转换为8位图进行显示。其算法原型如下:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img=cv2.imread('tower.jpg',0)

#cv2.CV_64F 输出图像的深度(数据类型),可以使用-1, 与原图像保持一致 np.uint8
laplacian=cv2.Laplacian(img,cv2.CV_64F)

# 参数 1,0 为只在 x 方向求一阶导数,最大可以求 2 阶导数。
sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)

# 参数 0,1 为只在 y 方向求一阶导数,最大可以求 2 阶导数。
sobely=cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)

# 显示图像
plt.figure(figsize = (20,15))
plt.subplot(141),plt.imshow(img,cmap = 'gray'),plt.title('Original'),plt.xticks([]), plt.yticks([])
plt.subplot(142),plt.imshow(laplacian,cmap = 'gray'),plt.title('Laplacian'),plt.xticks([]), plt.yticks([])
plt.subplot(143),plt.imshow(sobelx,cmap = 'gray'),plt.title('Sobel X'),plt.xticks([]), plt.yticks([])
plt.subplot(144),plt.imshow(sobely,cmap = 'gray'),plt.title('Sobel Y'),plt.xticks([]), plt.yticks([])
plt.show()

一个重要的事!

  • 在查看上面这个例子的注释时不知道你有没有注意到:当我们可以通过参数 -1 来设定输出图像的深度(数据类型)与原图像保持一致,但是我们在代码中使用的却是 cv2.CV_64F。这是为什么呢?想象一下一个从黑到白的边界的导数是整数,而一个从白到黑的边界点导数却是负数。如果原图像的深度是np.int8 时,所有的负值都会被截断变成 0,换句话说就是把把边界丢失掉。

  • 所以如果这两种边界你都想检测到,最好的的办法就是将输出的数据类型设置的更高,比如 cv2.CV_16S,cv2.CV_64F 等。取绝对值然后再把它转到 cv2.CV_8U。下面的示例演示了输出图片的深度不同造成的不同效果。

注意

  • 在Sobel函数的第二个参数这里使用了cv2.CV_16S。因为OpenCV文档中对Sobel算子的介绍中有这么一句:“in the case of 8-bit input images it will result in truncated derivatives”。即Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,所以Sobel建立的图像位数不够,会有截断。因此要使用16位有符号的数据类型,即cv2.CV_16S。

  • 在经过处理后,别忘了用convertScaleAbs()函数将其转回原来的uint8形式。否则将无法显示图像,而只是一副灰色的窗口。convertScaleAbs()的原型为:

    • dst = cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])其中可选参数alpha是伸缩系数,beta是加到结果上的一个值。结果返回uint8类型的图片。
  • 由于Sobel算子是在两个方向计算的,最后还需要用cv2.addWeighted()函数将其组合起来。其函数原型为:

    • dst = cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]]) 其中alpha是第一幅图片中元素的权重,beta是第二个的权重,gamma是加到最后结果上的一个值。
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('test.jpg',0)

# Output dtype = cv2.CV_8U
sobelx8u = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=5)

# Output dtype = cv2.CV_64F. Then take its absolute and convert to cv2.CV_8U
sobelx64f = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)


# 显示图像
plt.figure(figsize = (20,15))
plt.subplot(131),plt.imshow(img,cmap = 'gray'),plt.title('Original'),plt.xticks([]), plt.yticks([])
plt.subplot(132),plt.imshow(sobelx8u,cmap = 'gray'),plt.title('Sobel CV_8U'),plt.xticks([]), plt.yticks([])
plt.subplot(133),plt.imshow(sobel_8u,cmap = 'gray'),plt.title('Sobel abs(CV_64F)'),plt.xticks([]), plt.yticks([])
plt.show()

五、Laplacian 算子

拉普拉斯(Laplacian)算子是n维欧几里德空间中的一个二阶微分算子,常用于图像增强领域和边缘提取。它通过灰度差分计算邻域内的像素,基本流程是:判断图像中心像素灰度值与它周围其他像素的灰度值,如果中心像素的灰度更高,则提升中心像素的灰度;反之降低中心像素的灰度,从而实现图像锐化操作。在算法实现过程中,Laplacian算子通过对邻域中心像素的四方向或八方向求梯度,再将梯度相加起来判断中心像素灰度与邻域内其他像素灰度的关系,最后通过梯度运算的结果对像素灰度进行调整。

拉普拉斯算子可以使用二阶导数的形式定义,可假设其离散实现类似于二阶 Sobel 导数,事实上,OpenCV 在计算拉普拉斯算子时直接调用 Sobel 算
子。计算公式如下:

拉普拉斯算子的模板形式

从模板形式容易看出,如果在图像中一个较暗的区域中出现了一个亮点,那么用拉普拉斯运算就会使这个亮点变得更亮。因为图像中的边缘就是那些灰度发生跳变的区域,所以拉普拉斯锐化模板在边缘检测中很有用。一般增强技术对于陡峭的边缘和缓慢变化的边缘很难确定其边缘线的位置。但此算子却可用二次微分正峰和负峰之间的过零点来确定,对孤立点或端点更为敏感,因此特别适用于以突出图像中的孤立点、孤立线或线端点为目的的场合。同梯度算子一样,拉普拉斯算子也会增强图像中的噪声,有时用拉普拉斯算子进行边缘检测时,可将图像先进行平滑处理。

Laplacian算子分为四邻域和八邻域,四邻域是对邻域中心像素的四方向求梯度,八邻域是对八方向求梯度。其中四邻域模板如公式所示:

通过模板可以发现,当邻域内像素灰度相同时,模板的卷积运算结果为0;当中心像素灰度高于邻域内其他像素的平均灰度时,模板的卷积运算结果为正数;当中心像素的灰度低于邻域内其他像素的平均灰度时,模板的卷积为负数。对卷积运算的结果用适当的衰弱因子处理并加在原中心像素上,就可以实现图像的锐化处理。

Laplacian算子的八邻域模板如下:

  • 拉式算子用来改善因扩散效应的模糊特别有效,因为它符合降制模型。扩散效应是成像过程中经常发生的现象。

    Laplacian算子一般不以其原始形式用于边缘检测,因为其作为一个二阶导数,Laplacian算子对噪声具有无法接受的敏感性;同时其幅值产生算边缘,这是复杂的分割不希望有的结果;最后Laplacian算子不能检测边缘的方向;所以Laplacian在分割中所起的作用包括:(1)利用它的零交叉性质进行边缘定位;(2)确定一个像素是在一条边缘暗的一面还是亮的一面;一般使用的是高斯型拉普拉斯算子(Laplacian of a Gaussian,LoG),由于二阶导数是线性运算,利用LoG卷积一幅图像与首先使用高斯型平滑函数卷积改图像,然后计算所得结果的拉普拉斯是一样的。所以在LoG公式中使用高斯函数的目的就是对图像进行平滑处理,使用Laplacian算子的目的是提供一幅用零交叉确定边缘位置的图像;图像的平滑处理减少了噪声的影响并且它的主要作用还是抵消由Laplacian算子的二阶导数引起的逐渐增加的噪声影响。
    
  • 图像锐化处理的作用是使灰度反差增强,从而使模糊图像变得更加清晰。图像模糊的实质就是图像受到平均运算或积分运算,因此可以对图像进行逆运算,如微分运算能够突出图像细节,使图像变得更为清晰。由于拉普拉斯是一种微分算子,它的应用可增强图像中灰度突变的区域,减弱灰度的缓慢变化区域。因此,锐化处理可选择拉普拉斯算子对原图像进行处理,产生描述灰度突变的图像,再将拉普拉斯图像与原始图像叠加而产生锐化图像。

  • 这种简单的锐化方法既可以产生拉普拉斯锐化处理的效果,同时又能保留背景信息,将原始图像叠加到拉普拉斯变换的处理结果中去,可以使图像中的各灰度值得到保留,使灰度突变处的对比度得到增强,最终结果是在保留图像背景的前提下,突现出图像中小的细节信息。

六、Canny算子

Canny边缘检测是一种比较新的边缘检测算子,具有很好地边缘检测性能,该算子功能比前面几种都要好,但是它实现起来较为麻烦,Canny算子是一个具有滤波,增强,检测的多阶段的优化算子,在进行处理前,Canny算子先利用高斯平滑滤波器来平滑图像以除去噪声,Canny分割算法采用一阶偏导的有限差分来计算梯度幅值和方向,在处理过程中,Canny算子还将经过一个非极大值抑制的过程,最后Canny算子还采用两个阈值来连接边缘(高低阈值输出二值图像)。

Canny边缘检测基本原理

  • (1)图象边缘检测必须满足两个条件:一能有效地抑制噪声;二必须尽量精确确定边缘的位置。

  • (2)根据对信噪比与定位乘积进行测度,得到最优化逼近算子。这就是Canny边缘检测算子。

  • (3)类似与Marr(LoG)边缘检测方法,也属于先平滑后求导数的方法。

Canny边缘检测算法步骤

  • step1: 用高斯滤波器平滑图象;

    • 噪声去除。由于边缘检测很容易受到噪声影响,所以第一步是使用 5x5 的高斯滤波器去除噪声,这个前面我们已经学过了。
  • step2: 用一阶偏导的有限差分来计算梯度的幅值和方向;

    • 对平滑后的图像使用 Sobel 算子计算水平方向和竖直方向的一阶导数(图像梯度)(Gx 和 Gy)。根据得到的这两幅梯度图(Gx 和 Gy)找到边界的梯度和方向,公式如下:
    • 梯度的方向一般总是与边界垂直。梯度方向被归为四类:垂直,水平,和两个对角线。
  • step3: 对梯度幅值进行非极大值抑制

    • 在获得梯度的方向和大小之后,应该对整幅图像做一个扫描,去除那些非边界上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。如下图所示:

    • 现在你得到的是一个包含“窄边界”的二值图像。

  • step4: 用双阈值算法检测和连接边缘

    • 现在要确定那些边界才是真正的边界。这时我们需要设置两个阈值:minVal 和 maxVal。当图像的灰度梯度高于 maxVal 时被认为是真的边界,那些低于 minVal 的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。如下图:

    • A 高于阈值 maxVal 所以是真正的边界点,C 虽然低于 maxVal 但高于minVal 并且与 A 相连,所以也被认为是真正的边界点。而 B 就会被抛弃,因为他不仅低于 maxVal 而且不与真正的边界点相连。所以选择合适的 maxVal和 minVal 对于能否得到好的结果非常重要。在这一步一些小的噪声点也会被除去,因为我们假设边界都是一些长的线段。

OpenCV 中的 Canny 边界检测

在 OpenCV 中只需要一个函数:cv2.Canny(),就可以完成以上几步。让我们看如何使用这个函数。这个函数的第一个参数是输入图像。第二和第三个分别是 minVal 和 maxVal。第三个参数设置用来计算图像梯度的 Sobel卷积核的大小,默认值为 3。最后一个参数是 L2gradient,它可以用来设求梯度大小的方程。如果设为 True,就会使用我们上面提到过的方程,否则使用方程:Edgee Gradient(G) = |G2x| + |G2y| 代替,默认值为 False。

import cv2
import numpy as np
from matplotlib import pyplot as plt


img = cv2.imread('tower.jpg',0)
edges = cv2.Canny(img,100,200)

# 显示图像
plt.figure(figsize = (10,8))
plt.subplot(121),plt.imshow(img,cmap = 'gray'),plt.title('Original'),plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray'),plt.title('Edge'),plt.xticks([]), plt.yticks([])
plt.show()

七、各种算子比较

  • Robert算子是一阶微分算子,比较简单,计算量小,对细节反应敏感。

  • Sobel算子是基于一阶导数的微分算子,其中引入了类似局部平均的运算,对于噪声有平滑作用,能很好地消除噪声的影响,所以计算量变大。简言之就是,产生的边缘有强弱,抗噪性好,计算量小

  • Prewitt算子和Sobel一样,图像中的点用Sobel的两个卷积核卷积,取最大值作为输出值。也增加了计算量。

  • laplace 对边缘敏感,可能有些是噪声的边缘,也被算进来了

  • Canny算子比较复杂,既要计算多个方向的梯度,又要记录方向和大小,然后又进行NMS非极大值抑制,大小阈值,很复杂。 产生的边缘很细,可能就一个像素那么细,没有强弱之分。计算量大,但是准确。

Sobel算子,Robert算子,prewitt算子的比较

Sobel算子是滤波算子的形式来提取边缘,X,Y方向各用一个模板,两个模板组合起来构成一个梯度算子。X方向模板对垂直边缘影响最大,Y方向模板对水平边缘影响最大。

Robert算子是一种梯度算子,它用交叉的查分表示梯度,是一种利用局部差分算子寻找边缘的算子,对具有陡峭的低噪声的图像效果最好。

prewitt算子是加权平均算子,对噪声有抑制作用,但是像素平均相当于对图像进行的同滤波,所以prewitt算子对边缘的定位不如robert算子。

参考

https://www.cnblogs.com/eilearn/p/9459260.html
https://www.jianshu.com/p/bed4ffe996a1

  • 6
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值