本文分享自华为云社区”【Python图像处理】XVII。用于图像锐化和边缘检测的Roberts算子、Prewitt算子、Sobel算子和Laplacian算子-云社区-华为云”由eastmount。
采集图像数据的设备或传输数字图像的通道存在一些质量缺陷,文物的图像是长寿命的,或者受一些其他外界因素和动态不稳定的影响来采集图像,使得图像模糊、有噪声,从而影响图像识别的发展。这时就需要进行图像锐化和边缘检测,加强原始图像的高频部分,锐化和突出图像的边缘细节,提高图像的对比度,使模糊的图像更加清晰。
图像锐化和边缘提取技术可以消除图像中的噪声,提取图像信息中一些用来表征图像的变量,为图像识别提供依据。通常采用灰度差法对图像的边缘和轮廓进行处理并突出显示。本文采用拉普拉斯算子、罗伯特算子、Prewitt算子和Sobel算子进行图像锐化边缘处理实验。本文主要讲解灰度的线性变换。希望基础知识对你有帮助。
一.罗伯茨算子二。普鲁伊特操作员iii。索贝尔算子四。拉普拉斯算子诉摘要代码
这个系列在github的所有源代码中都有:
Https://github.com/eastmountyxz/I***geProcessing-Python·罗伯特算子
Roberts算子又称交叉差分算法,是一种基于交叉差分的梯度算法,通过局部差分计算来检测边缘线。它通常用于处理具有陡峭低噪声的图像。当图像边缘接近正45度或负45度时,该算法的处理效果较好。其缺点是边缘定位不准确,提取的边缘线较粗。
罗伯茨算子的模板分为水平方向和垂直方向,如公式(11.7)所示。从其模板可以看出,Roberts算子可以增强正负45度的图像边缘。
详细计算公式如下:(PS-参考下图自己的书和论文)
在Python中,Roberts算子主要通过Numpy定义模板,然后调用OpenCV的filter2D()函数实现边缘提取。该函数主要利用内核实现图像的卷积运算,其原型如下:
dst = filter2D(src,ddepth,kernel[,dst[,anchor[,delta[,borderType]]])
src输入图像dst表示输出边缘图像,并且其尺寸和通道号与输入图像的尺寸和通道号相同。ddepth表示目标图像所需的深度。kernel表示卷积核。单通道浮点矩阵锚点表示内核的参考点,默认值为(-1,-1)。位于中间的增量表示在存储目标图像之前添加到像素的可选值,对于边框模式,默认值为0borderType。
Python实现代码如下:
# -*- coding: utf-8 -*-import cv2 import numpy as np import ***tplotlib.pyplot as plt #读取图像img = cv2.imread('lena.png')lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)#灰度化处理图像grayI***ge = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #Roberts算子kernelx = np.array([[-1,0],[0,1]], dtype=int)kernely = np.array([[0,-1],[1,0]], dtype=int)x = cv2.filter2D(grayI***ge, cv2.CV_16S, kernelx)y = cv2.filter2D(grayI***ge, cv2.CV_16S, kernely)#转uint8 absX = cv2.convertScaleAbs(x) absY = cv2.convertScaleAbs(y) Roberts = cv2.addWeighted(absX,0.5,absY,0.5,0)#用来正常显示中文标签plt.rcParams['font.sans-serif']=['SimHei']#显示图形titles = [u'原始图像', u'Roberts算子'] i***ges = [lenna_img, Roberts] for i in xrange(2): plt.subplot(1,2,i+1), plt.imshow(i***ges[i], 'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
运行结果如下图所示:#-*-编码:utf-8-*-import cv2 import numpy as NP import ***tplotlib . py plot as PLT # Read i***ge img = cv2 . im Read('lena.png & # 039)lenna _ img = cv2.cvtcolor (img,cv2 . color _ bgr 2 RGB)# gray i***ge = cv2 . CVT color(img,cv2.color _ bgr2gray) # Roberts算子kernelx = np.array ([[-1,0],[0 dtype = int)kernel y = NP . array([[0,-1],[1,0]],dtype = int)x = cv2 . filter 2d(gray i***ge,cv2。CV_16S,kernel x)y = cv2 . filter 2d(gray i***ge,cv2。CV_16S,Kerny)# turn uint 8 absx = cv2 . convertscaleabs(x)absy = cv2 . convertscaleabs(y)Roberts = cv2 . add weighted(absx,0.5,absy,0.5,0) #用于显示中文标签plt.rcParams['正常['font . sans-serif ']=['希姆黑'] #显示图形标题=[u '原始图像',u & # 039罗伯茨算子'] i***ges = [lenna_img,Roberts]for I in x range(2):PLT . subplot(1,2,i+1),plt.imshow(i***ges[i],'格雷')PLT。标题(标题[I]) PLT。x票([]),PLT。y票([]) PLT。Show()运行结果如下图所示:
二。Prewitt算子
Prewitt是图像边缘检测的微分算子。其原理是利用特定区域像素灰度值产生的差异来实现边缘检测。由于Prewitt算子使用33个模板计算区域内的像素值,而Robert算子使用22个模板,因此Prewitt算子的边缘检测结果在水平和垂直方向都比Robert算子更明显。Prewitt算子适用于识别噪声较多、渐变的图像,其计算公式如下。
在Python中,Prewitt算子的实现过程类似于Roberts算子。通过Numpy定义模板,然后调用OpenCV的filter2D()函数实现图像的卷积运算,最后通过convertScaleAbs()和addWeighted()函数实现边缘提取。代码如下:
# -*- coding: utf-8 -*-import cv2 import numpy as np import ***tplotlib.pyplot as plt #读取图像img = cv2.imread('lena.png')lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)#灰度化处理图像grayI***ge = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #Prewitt算子kernelx = np.array([[1,1,1],[0,0,0],[-1,-1,-1]],dtype=int)kernely = np.array([[-1,0,1],[-1,0,1],[-1,0,1]],dtype=int)x = cv2.filter2D(grayI***ge, cv2.CV_16S, kernelx)y = cv2.filter2D(grayI***ge, cv2.CV_16S, kernely)#转uint8absX = cv2.convertScaleAbs(x) absY = cv2.convertScaleAbs(y) Prewitt = cv2.addWeighted(absX,0.5,absY,0.5,0)#用来正常显示中文标签plt.rcParams['font.sans-serif']=['SimHei']#显示图形titles = [u'原始图像', u'Prewitt算子'] i***ges = [lenna_img, Prewitt] for i in xrange(2): plt.subplot(1,2,i+1), plt.imshow(i***ges[i], 'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
输出结果如下图所示,左边为原始图像,右边为Prewitt算子图像锐化提取的边缘轮廓,其效果图的边缘检测结果在水平方向和垂直方向均比Robert算子更加明显。#-*-编码:utf-8-*-import cv2 import numpy as NP import ***tplotlib . py plot as PLT # Read i***ge img = cv2 . im Read('lena.png & # 039)lenna _ img = cv2.cvtcolor (img,cv2 . color _ bgr 2 RGB)# gray i***ge = cv2 . CVT color(img,cv2.color _ bgr2gray) # prewitt算子kernelx = np.array ([[1,1,1],)0],[-1,-1,-1]],dtype = int)kernel y = NP . array([-1,0,1],[-1,0,1],[-1,0,1],[-1,0,1CV_16S,kernel x)y = cv2 . filter 2d(gray i***ge,cv2.cv _ 16s,kernel y)# turn uint 8 absx = cv2 . convertscaleabs(x)absy = cv2 . convertscaleabs(y)prewitt = cv2 . add weighted(absx,0.5,absy font . sans-serif ']=['希姆黑'] #显示图形标题=[u '原始图像',u & # 039Prewitt算子'] i***ges = [lenna_img,Prewitt]for I in x range(2):PLT . subplot(1,2,i+1),plt.imshow(i***ges[i],'格雷')PLT。标题(标题[I]) PLT。x票([]),PLT。y票([]) PLT。Show()输出结果如下图所示。左边是原始图像,右边是Prewitt算子图像锐化后提取的边缘轮廓。渲染图的边缘检测结果无论在水平方向还是垂直方向都比Robert算子的边缘检测结果更加明显。
3.索贝尔算子
Sobel算子是一种用于边缘检测的离散微分算子,它结合了高斯平滑和微分求导。该算子用于计算图像亮度的近似值,根据图像边缘的亮度,将区域内超过一定数量的特定点记录为边缘。Sobel算子在Prewitt算子的基础上增加了权重的概念,认为相邻点之间的距离对当前像素的影响不同,距离越近对当前像素的影响越大,从而锐化图像,突出边缘轮廓。
Sobel算子的边缘定位比较准确,常用于噪声多、灰度渐变的图像。算法模板如公式所示,其中dx代表水平方向,dy代表垂直方向。
Sobel算子是根据一个像素的上、下、左、右邻居的灰度加权差在边缘处达到极值的现象来检测边缘的。它可以平滑噪声并提供更准确的边缘方向信息。因为Sobel算子结合了高斯平滑和微分,所以结果会更抗噪。当精度不是很高时,Sobel算子是一种常用的边缘检测方法。
dst = Sobel(src,ddepth,dx,dy[,dst[,ksize[,scale[,delta[,borderType]]])
src输入图像dst表示输出边缘图像,并且其尺寸和通道号与输入图像的尺寸和通道号相同。ddepth表示目标图像所需的深度。对于不同的输入图像,输出目标图像具有不同的深度。dx表示X方向的微分阶,1或0dy表示Y方向的微分阶,1或0ksize表示Sobel算子的大小。这些值必须是正数和奇数。scale表示比例导数的比例常数。默认情况下,没有缩放系数。delta表示在结果存储到目标图像之前。添加到结果中的可选增量值borderType指示边框模式。有关更多详细信息,请参考BorderTypes。
注意,经过Sobel算子处理后,还需要调用convertScaleAbs()函数计算绝对值,将图像转换成8位图进行显示。其算法原型如下:
dst = convertScaleAbs(src[,dst[,alpha[,beta]])
Src表示原数组dst表示输出数组,深度8位alpha表示缩放因子beta表示原数组元素缩放后增加的值。
Sobel算子的实现代码如下:
# -*- coding: utf-8 -*-import cv2 import numpy as np import ***tplotlib.pyplot as plt #读取图像img = cv2.imread('lena.png')lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)#灰度化处理图像grayI***ge = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #Sobel算子x = cv2.Sobel(grayI***ge, cv2.CV_16S, 1, 0) #对x求一阶导y = cv2.Sobel(grayI***ge, cv2.CV_16S, 0, 1) #对y求一阶导absX = cv2.convertScaleAbs(x) absY = cv2.convertScaleAbs(y) Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)#用来正常显示中文标签plt.rcParams['font.sans-serif']=['SimHei']#显示图形titles = [u'原始图像', u'Sobel算子'] i***ges = [lenna_img, Sobel] for i in xrange(2): plt.subplot(1,2,i+1), plt.imshow(i***ges[i], 'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
最终输出结果如下图所示:#-*-编码:utf-8-*-import cv2 import numpy as NP import ***tplotlib . py plot as PLT # Read i***ge img = cv2 . im Read('lena.png & # 039)lenna _ img = cv2.cvtcolor (img,cv2 . color _ bgr 2 RGB)# gray i***ge = cv2 . CVT color(img,cv2.color _ bgr2gray) # Sobel算子x = cv2.sobel (grayi***ge,cv2.cv _ 16s,1,0) #求x的一阶导数y = cv2.sobel (grayi***ge,cv2.cv _ 16s,0,1) #取y的一阶导数absx = cv2。convertscaleabs (x) absy = cv2。convertscaleabs (y) sobel = cv2。addweighted (absx,0.5,absy,0.5,0) #用于显示中文标签plt.rcParams['正常情况下['font . sans-serif ']=['希姆黑'] #显示图形标题=[u '原始图像',u & # 039索贝尔算子'] i***ges = [lenna_img,Sobel]for I in x range(2):PLT . subplot(1,2,i+1),plt.imshow(i***ges[i],'格雷')PLT . title(titles[I])PLT . x tickets([]),PLT.y tickets ([]) PLT.show()最终输出结果如下图所示:
四。拉普拉斯算子
拉普拉斯算子是N维Euclid 空中的二阶微分算子,常用于图像增强和边缘提取。它通过灰度差计算邻域内的像素,基本过程是:判断图像中心像素的灰度值及其周围其他像素的灰度值,如果中心像素的灰度值较高,则提高中心像素的灰度值;相反,降低中心像素的灰度,从而实现图像锐化操作。在算法执行过程中,拉普拉斯算子计算邻域中心像素在四个方向或八个方向的梯度,然后对梯度求和,判断中心像素的灰度与邻域其他像素的灰度之间的关系。最后,根据梯度运算的结果调整像素的灰度。
拉普拉斯算子分为四邻域和八邻域。四个邻域对于四个方向上的邻域的中心像素是梯度的,八个邻域对于八个方向是梯度的。四邻域模板如公式所示:
通过模板可以发现,当邻域内像素灰度相同时,模板的卷积结果为0;当中心像素的灰度高于邻域内其他像素的平均灰度时,模板的卷积结果为正;当中心像素的灰度低于邻域中其他像素的平均灰度时,模板的卷积为负。卷积运算的结果经过适当的弱化因子处理,加到原中心像素上,使图像得到锐化。
拉普拉斯算子的八邻域模板如下:
Python和OpenCV将拉普拉斯算子封装在拉普拉斯()函数中,其函数原型如下:
dst =拉普拉斯(src,ddepth[,dst[,ksize[,scale[,delta[,borderType]]])
Src输入图像dst表示输出边缘图,其大小和通道号与输入图像相同。ddepth表示目标图像所需的深度。ksize表示用于计算二阶导数的过滤器的孔径大小。它的值必须是正数和奇数,默认值为1。有关更多详细信息,请参考getDerivKernelsscale来指示用于计算拉普拉斯值的可选缩放因子。默认值为1。有关更多详细信息,请参考getDerivKernelsdelta,以指示在结果存储到目标映像之前添加到结果中的可选增量值。默认值为0borderType,表示边框模式。有关更多详细信息,请参考边框类型。
注意,拉普拉斯算子实际上主要是利用Sobel算子的运算,通过对Sobel算子计算出的图像在X方向和Y方向上的导数相加,得到输入图像的图像锐化结果。同时,经过拉普拉斯算子处理后,需要调用convertScaleAbs()函数计算绝对值,将图像转换成8位图进行显示。
ksize=1时,拉普拉斯()函数用3×3孔径(四邻域模板)变换。以下代码使用ksize=3的拉普拉斯算子进行图像锐化,其代码如下:
# -*- coding: utf-8 -*-import cv2 import numpy as np import ***tplotlib.pyplot as plt #读取图像img = cv2.imread('lena.png')lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)#灰度化处理图像grayI***ge = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #拉普拉斯算法dst = cv2.Laplacian(grayI***ge, cv2.CV_16S, ksize = 3)Laplacian = cv2.convertScaleAbs(dst) #用来正常显示中文标签plt.rcParams['font.sans-serif']=['SimHei']#显示图形titles = [u'原始图像', u'Laplacian算子'] i***ges = [lenna_img, Laplacian] for i in xrange(2): plt.subplot(1,2,i+1), plt.imshow(i***ges[i], 'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
最终输出结果如下图所示:#-*-编码:utf-8-*-import cv2 import numpy as NP import ***tplotlib . py plot as PLT # Read i***ge img = cv2 . im Read('lena.png & # 039)lenna _ img = cv2.cvtcolor (img,cv2 . color _ bgr 2 RGB)# gray i***ge = cv2 . CVT color(img,cv2.color _ bgr2gray) #拉普拉斯算法dst = cv2。拉普拉斯算子(灰度图像,2。CV _ 16s,k size = 3)拉普拉斯= CV2。ConvertScaleabs (DST) #用于显示中文标签plt.rcParams['正常情况下['font . sans-serif ']=['希姆黑'] #显示图形标题=[u '原始图像',u & # 039拉普拉斯算子'] i***ges = [lenna_img,Laplacian]for I in x range(2):PLT . subplot(1,2,i+1),plt.imshow(i***ges[i],'格雷')PLT . title(titles[I])PLT . x tickets([]),PLT.y tickets ([]) PLT.show()最终输出结果如下图所示:
动词 (verb的缩写)汇总代码
边缘检测算法主要是基于图像强度的一阶和二阶导数,但是导数通常对噪声比较敏感,所以需要用滤波器滤除噪声,调用图像增强或阈值算法进行处理,最后进行边缘检测。下面是用高斯滤波器去噪阈值化后边缘检测的过程,比较了四种常见的边缘提取算法。
# -*- coding: utf-8 -*-import cv2 import numpy as np import ***tplotlib.pyplot as plt#读取图像img = cv2.imread('lena.png')lenna_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)#灰度化处理图像grayI***ge = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#高斯滤波gaussianBlur = cv2.GaussianBlur(grayI***ge, (3,3), 0)#阈值处理ret, binary = cv2.threshold(gaussianBlur, 127, 255, cv2.THRESH_BINARY)#Roberts算子kernelx = np.array([[-1,0],[0,1]], dtype=int)kernely = np.array([[0,-1],[1,0]], dtype=int)x = cv2.filter2D(binary, cv2.CV_16S, kernelx)y = cv2.filter2D(binary, cv2.CV_16S, kernely)absX = cv2.convertScaleAbs(x) absY = cv2.convertScaleAbs(y) Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)#Prewitt算子kernelx = np.array([[1,1,1],[0,0,0],[-1,-1,-1]], dtype=int)kernely = np.array([[-1,0,1],[-1,0,1],[-1,0,1]], dtype=int)x = cv2.filter2D(binary, cv2.CV_16S, kernelx)y = cv2.filter2D(binary, cv2.CV_16S, kernely)absX = cv2.convertScaleAbs(x) absY = cv2.convertScaleAbs(y) Prewitt = cv2.addWeighted(absX,0.5,absY,0.5,0)#Sobel算子x = cv2.Sobel(binary, cv2.CV_16S, 1, 0)y = cv2.Sobel(binary, cv2.CV_16S, 0, 1) absX = cv2.convertScaleAbs(x) absY = cv2.convertScaleAbs(y) Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)#拉普拉斯算法dst = cv2.Laplacian(binary, cv2.CV_16S, ksize = 3)Laplacian = cv2.convertScaleAbs(dst) #效果图titles = ['Source I***ge', 'Binary I***ge', 'Roberts I***ge', 'Prewitt I***ge','Sobel I***ge', 'Laplacian I***ge'] i***ges = [lenna_img, binary, Roberts, Prewitt, Sobel, Laplacian] for i in np.arange(6): plt.subplot(2,3,i+1),plt.imshow(i***ges[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
输出结果如图所示。其中,Laplacian算子对噪声比较敏感,由于其算法可能会出现双像素边界,常用来判断边缘像素位于图像的明区或暗区,很少用于边缘检测;Robert算子对陡峭的低噪声图像效果较好,尤其是边缘正负45度较多的图像,但定位准确率较差;Prewitt算子对灰度渐变的图像边缘提取效果较好,而没有考虑相邻点的距离远近对当前像素点的影响;Sobel算子考虑了综合因素,对噪声较多的图像处理效果更好。#-*-编码:utf-8-*-import cv2 import numpy as NP import ***tplotlib . py plot as PLT # Read i***ge img = cv2 . im Read('lena.png & # 039)lenna _ img = cv2.cvtcolor (img,cv2 . color _ bgr 2 RGB)# gray i***ge = cv2 . CVT color(img,2 .color _ bgr2gray) #高斯滤镜高斯模糊= cv2。高斯模糊(灰度图像,(3,3),0) #阈值处理ret,binary = cv2。阈值(高斯模糊,127,255,2。CVThresh _ binary) # Roberts算子kernelx = np.array ([[-1,0],[0,1]],dtype = int) kernely = np.array ([[0,-1],[1,0]],dtype = int)。cv2。CV_16S,kernelx)y = cv2.filter2D(二进制,cv2。CV_16S,kernely)absX = cv2 . convertscaleabs(x)absY = cv2 . convertscaleabs(y)Roberts = cv2 . add weighted(absX,0.5,absY,0.5,0)#Prewitt算子kernelx = np.array ([[1,1,1],[0,0,0],[-1,-1,-1]],dtype = int)kernely = NP . array([-1,0,1],[ dtype=intCV_16S,kernelx)y = cv2.filter2D(二进制,cv2。CV_16S,Kerny) ABSx = CV2。ConvertScaleabs (x) ABSy = CV2。ConvertScaleabs (y) Prewitt = CV2。加加权(ABSx,0.5,ABSy,0.5,0) # Sobel算子x = cv2。Sobel(二进制,cv2。CV_16S,1,0)y = cv2。Sobel(二进制,cv2。CV_16S,0,1)absX = CV2 . convertscaleabs(x)absY = CV2 . convertscaleabs(y)Sobel = CV2 . add weighted(absX,0.5,absY,0.5,0)#拉普拉斯算法DST = CV2。拉普拉斯(二进制,CV2。CV _ 16s,k size = 3)拉普拉斯= CV2。ConvertScaleabs(DST)# renders titles =['源图像', '二进制图像', '罗伯茨形象', 'Prewitt I***ge & # 039,'索贝尔图像', '拉普拉斯图像'] i***ges = [lenna_img,binary,Roberts,Prewitt,Sobel,Laplacian]for I in NP . arange(6):PLT . subplot(2,3,i+1),plt.imshow(i***ges[i],'格雷')PLT。标题(标题[I]) PLT。x票([]),PLT。y票([]) PLT。Show()输出结果如图所示。其中,拉普拉斯算子对噪声敏感。由于其算法可能存在双像素边界,所以常用来判断边缘像素是位于图像的亮区还是暗区,而很少用于边缘检测。罗伯特算子对陡峭的低噪声图像,尤其是正负45度边缘较多的图像效果较好,但定位精度较差。Prewitt算子对有灰度的图像边缘提取效果较好,但没有考虑相邻点之间的距离对当前像素的影响。Sobel算子综合考虑了各种因素,对噪声较多的图像处理有较好的效果。
参考资料:
杨等.基于苗族服饰的图像锐化和边缘提取技术研究[J].现代计算机,2018(10)。《数字图像处理》(第三版),冈萨雷斯著,阮译,电子工业出版社,2013。《数字图像处理》(第3版),阮,电子工业出版社,2008电子工业出版社,2015。【数字图像处理】七。MFC图像增强的详细说明,包括普通平滑,高斯平滑,拉普拉斯,Sobel和Prewitt锐化。
点击下方,第一时间了解华为云鲜技术~
华为云博客_大数据博客_AI博客_云计算博客_开发者中心-华为云
本文来自霜华投稿,不代表舒华文档立场,如若转载,请注明出处:https://www.chinashuhua.cn/24/614135.html