亲宝软件园·资讯

展开

Python图像处理之边缘检测原理详解

夏天是冰红茶 人气:0

原理

边缘检测是图像处理和计算机视觉当中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点,图像的边缘检测可以大幅度的减少数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性,它们绝大多数可以分为两类:基于搜索和基于零穿越。

基于搜索:通过寻找图像一阶导数中max来检测边界,然后利用计算结果估计边缘的局部方向,通常采用梯度的方向,并在此方向找到局部梯度模的最大值,代表的算法是Sobel算子和Scharr算子。

基于零穿越:通过寻找图像二阶导数零穿越来寻找边界,代表算法是Laplacian算子。 

Sobel检测算子

Sobel边缘检测算法比较简单,实际应用中效率要比Canny边缘检测算法效率要高,但边缘检测效果不如Canny准确,但很多常会它依旧是首选,它是高斯平滑与微分操作的结合体,所以其抗燥声能力很强,用途较多,尤其是对效率要求较高,而对细纹理不是很在意的情况下。

方法

 那么我们看到上图最后一个式子,在某一个领域内,某一方向上,前一个像素与后一个像素的差,除以(x+1)-(x-1)=2,就得到当前的一个一阶导数,这就是Sobel算子的一个思想。

这里假设要处理的对象为I,在两个方向求导:

如下,模板为3;

最后,统计极大值所在的位置,即为图像的边缘

注:当内核大小为3时,以上Sobel内核可能会产生比较明显的误差,为解决这一个问题,我们使用Scharr函数,但该函数仅作用于大小为3的内核,该函数的运行速度与Sobel算子一样快,但结果更加精准,其计算方法为下图所示:

应用

 Opencv当中的API为:

sobel_x_or_y=Sobel(src, ddepth, dx, dy, dst=None, ksize=None, scale=None, delta=None, borderType=None)
 
"""
src:传入图像
ddepth:图像的深度
dx、dy=指求导的阶数,0表示这个方向上没有求导,取值为0、1
ksize=是Sobel算子的大小,即卷积核的大小,为奇数(1,3,5,7等),默认是3
scale:缩放导数的比例常数,默认没有
borderType:函数的边界模式,默认是cv2.BORDER_DEFAULT
"""

Sobel函数求导后会有负数,还有大于255的值,原图像为uint8,8位无符号数,所以Sobel建立的图像位数不够,会有截断。因此,要用16位有符号的数据类型,即cv2.CV_16S(放到深度中)。处理完图像后,再使用cv2.convertScaleAbs()函数将其再转为uint8格式,否则无法显示图像。

Sobel算子是两个方向计算的,最后要用cv2.addWeighted()函数将其组合起来

Scale_abs=cv2.convertScaleAbs(x)
result=cv2.addWeighted(scr1,alpha,scr2,beta)  #混合x,y方向
import cv2
import numpy as np
from matplotlib import pyplot as plt
 
img=cv2.imread("Images/1-1.jpg",0)
#Sobel:x,y方向
x=cv2.Sobel(img,cv2.CV_16S,1,0)
y=cv2.Sobel(img,cv2.CV_16S,0,1)
#数据转化
Scale_absx=cv2.convertScaleAbs(x)
Scale_absy=cv2.convertScaleAbs(y)
#混合图像
result=cv2.addWeighted(Scale_absx,0.5,Scale_absy,0.5,0)
 
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('yuantu')
plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(result,cmap=plt.cm.gray),plt.title('Sobel')
plt.xticks([]),plt.yticks([])
plt.show()

而Scharr,就是将Sobel算子当中的变成ksize=-1。

x=cv2.Sobel(img,cv2.CV_16S,1,0,ksize=-1)
y=cv2.Sobel(img,cv2.CV_16S,0,1,ksize=-1)

可以看到,Scharr检测的细节更加多一些。 

Laplacian算子

laplacian是利用二阶导数来检测边缘。因为图像是“二维”,需要在两个方向上求导,如下所示

API:

laplacian=cv2.Laplacian(src, ddepth, dst=None, ksize=None, scale=None, delta=None, borderType=None)
 
"""
src:图像
Ddepth:图像的深度,-1表示采用的是原图像相同的深度,目标图像的深度必须要大于等于原图像的深度;
ksize:卷积核的大小,必须为奇数
"""
import cv2
import numpy as np
from matplotlib import pyplot as plt
 
img=cv2.imread("Images/1-1.jpg",0)
 
result=cv2.Laplacian(img,cv2.CV_16S)
Scale_abs=cv2.convertScaleAbs(result)
 
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('yuantu')
plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(result,cmap=plt.cm.gray),plt.title('laplacian')
plt.xticks([]),plt.yticks([])
plt.show()

算子比较

加载全部内容

相关教程
猜你喜欢
用户评论