基础概念
什么是边缘?
边缘是图像强度函数快速变化的地方,边缘检测的目的就是找到图像中亮度变化剧烈的像素点构成的集合,表现出来往往是轮廓。如果图像中边缘能够精确的测量和定位,那么,就意味着实际的物体能够被定位和测量,包括物体的面积、物体的直径、物体的形状等就能被测量
如何检测边缘?
为了检测边缘,我们需要检测图像中的不连续性,可以使用导数来检测不连续性。导数也会受到噪声的影响,因此建议在求导数之前先对图像进行平滑处理。然后可以使用遮罩使用卷积来检测边缘。
Sobel边缘检测的原理
Sobel算子是离散微分算子,用来计算图像灰度的近似梯度,分别用横向及纵向矩阵,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值梯度越大越有可能是边缘。
Soble算子的功能集合了高斯平滑和微分求导,又被称为一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到的是图像在X方法与Y方向梯度图像。
缺点:比较敏感,容易受影响,要通过高斯模糊(平滑)来降噪。
水平变化: 将 I 与一个奇数大小的内核 Gx进行卷积。比如,当内核大小为3时, Gx的计算结果为:
垂直变化: 将 I 与一个奇数大小的内核 Gy进行卷积。比如,当内核大小为3时, Gy的计算结果为:
在图像的每一像素上,结合以上两个结果求出近似梯度:
有时也用下面更简单公式代替,计算速度快:(最终图像梯度)
如果梯度G大于某一阀值 则认为该点(x,y)为边缘点。
然后可用以下公式计算梯度方向:
Canny边缘检测的原理
边缘检测的一般标准包括:
1、以低的错误率检测边缘,也即意味着需要尽可能准确的捕获图像中尽可能多的边缘。
2、检测到的边缘应精确定位在真实边缘的中心。
3、图像中给定的边缘应只被标记一次,并且在可能的情况下,图像的噪声不应产生假的边缘。
为了满足这些要求,Canny使用了变分法。Canny检测器中的最优函数使用四个指数项的和来描述,它可以由高斯函数的一阶导数来近似。
完成一个Canny边缘检测算法可以分为以下四步:
目的 | |
1.高斯滤波 | 去噪声降低错误率 |
2.计算梯度幅值和方向 | 估计每一点处的边缘强度与方向 |
3.非极大值抑制 | 对Sobel、Prewitt等算子的结果进一步细化 |
4应用双阈值检测 | 确定真实的和可能的边缘。 |
1、高斯滤波
边缘检测结果容易受到图像噪声的影响,图片中一些噪声会大大影像边缘检测。因此为了使图像平滑,可以用高斯滤波器内核与图像进行卷积。
选择高斯核的大小会影响检测器的性能。 尺寸越大,检测器对噪声的灵敏度越低。 此外,随着高斯滤波器核大小的增加,用于检测边缘的定位误差将略有增加。一般5x5是一个比较不错的trade off。
2、计算梯度强度和方向
进行高斯滤波后,图像中的边缘可以指向各个方向,接下来使用四个算子来检测图像中的水平、垂直和对角边缘。边缘检测的算子返回水平和垂直方向的一阶导数值,由此便可以确定像素点的梯度和方向。
3、非极大值抑制(NMS)
在每一点上,邻域中心与沿着其对应的梯度方向的两个像素相比,若中心像素为最大值,则保留,否则中心置0,这样可以抑制非极大值,保留局部梯度最大的点,以得到细化的边缘。
对图像进行梯度计算后,仅仅基于梯度值提取的边缘仍然很模糊。对边缘有且应当只有一个准确的响应。而非极大值抑制则可以帮助将局部最大值之外的所有梯度值抑制为0。非极大值抑制是一种边缘稀疏技术,非极大值抑制的作用在于“瘦”边。直观上地看,对第二步得到的图片,边缘由粗变细了。
4.、用双阈值算法检测和连接边缘
双阈值法非常简单,我们假设两类边缘:经过非极大值抑制之后的边缘点中,梯度值超过TH的称为强边缘,梯度值小于TH大于TL的称为弱边缘,梯度小于TL的不是边缘。
可以肯定的是,强边缘必然是边缘点,因此必须将T1设置的足够高,以要求像素点的梯度值足够大(变化足够剧烈),而弱边缘可能是边缘,也可能是噪声,如何判断呢?当弱边缘的周围8邻域有强边缘点存在时,就将该弱边缘点变成强边缘点,以此来实现对强边缘的补充。实际中人们发现T1:T2=2:1的比例效果比较好,其中T1可以人为指定,也可以设计算法来自适应的指定,比如定义梯度直方图的前30%的分界线为T1。检查8邻域的方法叫边缘滞后跟踪,连接边缘的办法还有区域生长法等等。
基于OpenCV的API代码
def img_edge(img):
"""
task06 图像边缘检测
"""
# 方法1:sobel边缘检测
sobelx = cv2.Sobel(
img, # 原图像
cv2.CV_64F, # 表示64位浮点数
1, # 对X方向的导数
0, # 对Y方向的导数
ksize=5 # Sobel算子(卷积核)的大小
)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)
# 方法2:canny边缘检测
img1 = cv2.GaussianBlur(img,(3,3),0) # 高斯滤波
canny = cv2.Canny(
img1, # 原图像
50,
150 # 较大的阈值2用于检测图像中明显的边缘
)
print("shape1 : ",sobelx.shape)
print("shape2 : ",sobely.shape)
print("shape3 : ",canny.shape)
cv2.imshow('sobelx', sobelx)
cv2.imshow('sobely', sobely)
cv2.imshow('canny', canny)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == "__main__":
img = cv2.imread('./xiabang.jpg', cv2.IMREAD_UNCHANGED)
print('Original Dimensions : ',img.shape)
img_edge(img)