python图像特征提取进行分割,图像边缘提取算法
图像锐化和边缘提取技术可以消除图像中的噪声,提取图像信息中一些用来表征图像的变量,为图像识别提供依据。
本文由eastmount分享自华为云社区《[Python图像处理] 十七.图像锐化与边缘检测之Roberts算子、Prewitt算子、Sobel算子和Laplacian算》。
采集图像数据的设备或传输数字图像的通道存在一些质量缺陷,文物的图像是长寿命的,或者受一些其他外界因素和动态不稳定的影响来采集图像,使得图像模糊、有噪声,从而影响图像识别的发展。这时就需要进行图像锐化和边缘检测,加强原始图像的高频部分,锐化和突出图像的边缘细节,提高图像的对比度,使模糊的图像更加清晰。
图像锐化和边缘提取技术可以消除图像中的噪声,提取图像信息中一些用来表征图像的变量,为图像识别提供依据。通常采用灰度差法对图像的边缘和轮廓进行处理并突出显示。本文采用拉普拉斯算子、罗伯特算子、Prewitt算子和Sobel算子进行图像锐化边缘处理实验。本文主要讲解灰度的线性变换。希望基础知识对你有帮助。
这个系列在github。所有源代码:https://github.com/eastmountyxz/ImageProcessing-Python
1.Roberts算子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)。位于中间的delta表示存储目标图像之前添加到像素的可选值,默认值为0borderType表示边框模式。Python实现代码如下:
# -*-编码:utf-8 -*-
导入cv2
将numpy作为np导入
将matplotlib.pyplot作为plt导入
#阅读图像
img=cv2.imread(lena.png )
lenna_img=cv2.cvtColor(img,cv2。COLOR_BGR2RGB)
#对图像进行灰度处理
grayImage=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(灰度图像,cv2。CV_16S,kernelx)
y=cv2.filter2D(灰度图像,cv2。CV_16S,kernely)
#转到uint8
absX=cv2.convertScaleAbs(x)
absY=cv2.convertScaleAbs(y)
Roberts=cv2.addWeighted(absX,0.5,absY,0.5,0)
#用于正常显示中文标签。
PLT . RC params[ font . sans-serif ]=[ sim hei ]
#显示图形
Titles=[u 原始图像,u 罗伯茨运营商]
images=[lenna_img,Roberts]
对于xrange(2)中的I:
plt.subplot(1,2,i 1),plt.imshow(images[i], gray )
plt.title(标题[i])
plt.xticks([]),plt.yticks([])
plt.show()的运行结果如下图所示:
2.Prewitt算子Prewitt是图像边缘检测的微分算子。其原理是利用特定区域像素灰度值产生的差异来实现边缘检测。由于Prewitt算子使用33个模板计算区域内的像素值,而Robert算子使用22个模板,因此Prewitt算子的边缘检测结果在水平和垂直方向都比Robert算子更明显。Prewitt算子适用于识别噪声较多、渐变的图像,其计算公式如下。
在Python中,Prewitt算子的实现过程类似于Roberts算子。通过Numpy定义模板,然后调用OpenCV的filter2D()函数实现图像的卷积运算,最后通过convertScaleAbs()和addWeighted()函数实现边缘提取。代码如下:
# -*-编码:utf-8 -*-
导入cv2
将numpy作为np导入
将matplotlib.pyplot作为plt导入
#阅读图像
img=cv2.imread(lena.png )
lenna_img=cv2.cvtColor(img,cv2。COLOR_BGR2RGB)
#对图像进行灰度处理
grayImage=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(灰度图像,cv2。CV_16S,kernelx)
y=cv2.filter2D(灰度图像,cv2。CV_16S,kernely)
#转到uint8
absX=cv2.convertScaleAbs(x)
absY=cv2.convertScaleAbs(y)
Prewitt=cv2.addWeighted(absX,0.5,absY,0.5,0)
#用于正常显示中文标签。
PLT . RC params[ font . sans-serif ]=[ sim hei ]
#显示图形
Titles=[u 原始图像,uPrewitt运算符]
images=[lenna_img,Prewitt]
对于xrange(2)中的I:
plt.subplot(1,2,i 1),plt.imshow(images[i], gray )
plt.title(标题[i])
plt.xticks([]),plt.yticks([])
plt.show()的输出结果如下图所示。左边是原始图像,右边是Prewitt算子锐化提取的边缘轮廓。渲染图的边缘检测结果无论在水平方向还是垂直方向都比Robert算子更明显。
3.Sobel算子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算子的大小。值必须是正的和奇数标度,以指示标度导数的标度常数。默认情况下,没有缩放系数delta来指示结果存储在目标图像中,添加到结果中的可选增量值borderType指示边框模式。有关更多详细信息,请参考BorderTypes。注意,经过Sobel算子处理后,需要调用convertScaleAbs()函数计算绝对值,将图像转换成8位图进行显示。其算法原型如下:
dst=convertScaleAbs(src[,dst[,alpha[,beta]])
Src表示原始数组dst表示输出数组,8位深度表示缩放因子表示缩放原始数组元素后添加的值。Sobel算子的实现代码如下:
# -*-编码:utf-8 -*-
导入cv2
将numpy作为np导入
将matplotlib.pyplot作为plt导入
#阅读图像
img=cv2.imread(lena.png )
lenna_img=cv2.cvtColor(img,cv2。COLOR_BGR2RGB)
#对图像进行灰度处理
grayImage=cv2.cvtColor(img,cv2。COLOR_BGR2GRAY)
#Sobel算子
X=cv2.sobel (grayimage,cv2.cv _ 16s,1,0) #求x的一阶导数
Y=cv2.sobel (grayimage,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 . RC params[ font . sans-serif ]=[ sim hei ]
#显示图形
Titles=[u 原始图像,u 索贝尔算子]
images=[lenna_img,Sobel]
对于xrange(2)中的I:
plt.subplot(1,2,i 1),plt.imshow(images[i], gray )
plt.title(标题[i])
plt.xticks([]),plt.yticks([])
plt.show()的最终输出结果如下图所示:
4.拉普拉斯算子拉普拉斯算子是N维欧氏空间中的二阶微分算子,常用于图像增强和边缘提取。它通过灰度差计算邻域内的像素,基本过程是:判断图像中心像素的灰度值及其周围其他像素的灰度值,如果中心像素的灰度值较高,则提高中心像素的灰度值;相反,降低中心像素的灰度,从而实现图像锐化操作。在算法执行过程中,拉普拉斯算子计算邻域中心像素在四个方向或八个方向的梯度,然后对梯度求和,判断中心像素的灰度与邻域其他像素的灰度之间的关系。最后,根据梯度运算的结果调整像素的灰度。
拉普拉斯算子分为四邻域和八邻域。四个邻域对于四个方向上的邻域的中心像素是梯度的,八个邻域对于八个方向是梯度的。四邻域模板如公式所示:
通过模板可以发现,当邻域内像素灰度相同时,模板的卷积结果为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时,拉普拉斯()函数用33孔径(四邻域模板)变换。以下代码使用ksize=3的拉普拉斯算子进行图像锐化,其代码如下:
# -*-编码:utf-8 -*-
导入cv2
将numpy作为np导入
将matplotlib.pyplot作为plt导入
#阅读图像
img=cv2.imread(lena.png )
lenna_img=cv2.cvtColor(img,cv2。COLOR_BGR2RGB)
#对图像进行灰度处理
grayImage=cv2.cvtColor(img,cv2。COLOR_BGR2GRAY)
#拉普拉斯算法
dst=cv2。拉普拉斯(灰度图像,cv2。CV_16S,ksize=3)
拉普拉斯=cv2.convertScaleAbs(dst)
#用于正常显示中文标签。
PLT . RC params[ font . sans-serif ]=[ sim hei ]
#显示图形
Titles=[u 原始图像,u 拉普拉斯算子]
images=[lenna_img,拉普拉斯]
对于xrange(2)中的I:
plt.subplot(1,2,i 1),plt.imshow(images[i], gray )
plt.title(标题[i])
plt.xticks([]),plt.yticks([])
plt.show()的最终输出结果如下图所示:
5.总结码边缘检测算法主要是基于图像强度的一阶和二阶导数,但是导数通常对噪声比较敏感,所以需要用滤波器滤除噪声,调用图像增强或阈值算法进行处理,最后进行边缘检测。下面是用高斯滤波器去噪阈值化后边缘检测的过程,比较了四种常见的边缘提取算法。
# -*-编码:utf-8 -*-
导入cv2
将numpy作为np导入
将matplotlib.pyplot作为plt导入
#阅读图像
img=cv2.imread(lena.png )
lenna_img=cv2.cvtColor(img,cv2。COLOR_BGR2RGB)
#对图像进行灰度处理
grayImage=cv2.cvtColor(img,cv2。COLOR_BGR2GRAY)
#高斯滤波
gaussianBlur=cv2。GaussianBlur(灰度图像,(3,3),0)
#阈值处理
ret,binary=cv2 . threshold(Gaussian blur,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(二进制,cv2。CV_16S,kernelx)
y=cv2.filter2D(二进制,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(二进制,cv2。CV_16S,kernelx)
y=cv2.filter2D(二进制,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(二进制,cv2。CV_16S,1,0)
y=cv2。Sobel(二进制,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。拉普拉斯(二进制,cv2。CV_16S,ksize=3)
拉普拉斯=cv2.convertScaleAbs(dst)
#效果图
titles=[源图像,二进制图像,罗伯茨图像,
Prewitt图像, Sobel图像,拉普拉斯图像]
images=[lenna_img,binary,Roberts,Prewitt,Sobel,Laplacian]
对于np.arange(6)中的I:
plt.subplot(2,3,i 1),plt.imshow(images[i], gray )
plt.title(标题[i])
plt.xticks([]),plt.yticks([])
plt.show()的输出结果如图所示。其中,拉普拉斯算子对噪声敏感。由于其算法可能存在双像素边界,所以常用来判断边缘像素是位于图像的亮区还是暗区,而很少用于边缘检测。罗伯特算子对陡峭的低噪声图像,尤其是正负45度边缘较多的图像效果较好,但定位精度较差。Prewitt算子对有灰度的图像边缘提取效果较好,但没有考虑相邻点之间的距离对当前像素的影响。Sobel算子综合考虑了各种因素,对噪声较多的图像处理有较好的效果。
第一时间点击了解华为云鲜技术~
原创作品来自华为云开发者联盟,
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。