【OpenCV 学习笔记】第十章: Canny边缘检测

第十章: Canny边缘检测

canny边缘检测是一种一阶微分算子检测算法,但为什么还要单独拿出来讲呢,因为它几乎是边缘检测算子中最优秀的边缘检测算子,你很难找到一种边缘检测算子能显著地比Canny算子做的更好。

Canny提出了边缘检测算子优劣评判的三条标准:
1、较高的检测率。边缘检测算子应该只对边缘进行响应,检测算子不漏检任何边缘,也不应该将非边缘标记为边缘。
2、精确定位。检测到的边缘与实际边缘之间的距离要尽可能的小。
3、明确的响应。对每一条边缘只有一次响应,只得到一个点。

Canny边缘检测之所以优秀是因为它在一阶微分算子的基础上,增加了非最大值抑制和双阈值两项改进。
利用非极大值抑制不仅可以有效地抑制多响应边缘,而且还可以提高边缘的定位精度;利用双阈值可以有效减少边缘的漏检率。

  • Canny边缘检测的步骤:
    1、去噪。噪声会影响边缘检测的准确性,因此首先要将噪声过滤掉。
    2、计算梯度大小和方向。
    3、非极大值抑制。就是适当的让边缘'变瘦'。
    4、确定边缘。使用双阈值法确定最终的边缘信息。

一、Canny边缘检测原理讲解

1、用高斯滤波器除去图像噪声
为了避免检测到错误的边缘信息,我们就要先对图像进行去噪,因为噪声也集中于高频信号,很容易被识别为伪边缘。去噪就是平滑图像中的一些纹理较弱的非边缘的噪声区域,这样我们就可以得到更准确的边缘,降低了伪边缘的识别。

说明:我们一般用高斯滤波器,但是高斯核并不是固定的,核的大小也不是固定的。这个要自己根据自己图像的特点进行选择。
高斯核的大小和尺寸对边缘检测的效果具有很重要的作用。 当高斯核的尺寸一定,那么数值越大的核,对噪声的平滑效果就越小,就达不到去除伪边缘的效果。当高斯核的数值大致相当,那么核的尺寸越大,对噪声的平滑效果就越大,但同时也模糊了一些弱的边缘,容易弱边缘检测不到。所以要根据自己图像的噪声特点进行选核。一般情况下,我们都是用5x5的高斯核做为一个trade off。

2、计算梯度幅度和方向
这里我们计算的梯度是一阶的,就是做一次差分即可。但是,在计算梯度的时候我们不仅要考虑梯度值(幅度)的大小,我们还要考虑梯度的方向(就是这个梯度值是哪个方向的),比如 当我们计算某个像素点a的梯度值的时候:
当我们用的是a像素点的正左边像素点值-a像素点值的时候,这时a像素点的梯度方向就是:左-右,表示为:<--;
当我们用的是a像素点的正右边像素点值-a像素点值的时候,这时a像素点的梯度方向就是:右-左,表示为:-->;
当我们用的是a像素点的正上方像素点值-a像素点值的时候,这时a像素点的梯度方向就是:上-下,表示为:向下的箭头;
当我们用的是a像素点的正下方像素点值-a像素点值的时候,这时a像素点的梯度方向就是:下-上,表示为:向上的箭头;
同理,一个像素点的梯度方向还有左上、右上、左下、右下。所以一个像素点a的梯度值的方向有8个方向!

经典的Canny算法是用四个梯度算子来分别计算水平,垂直和对角线方向的梯度。但是通常我们都不用四个梯度算子来分别计算四个方向。因为常用的边缘差分算子,比如Rober,Prewitt,Sobel等都能计算这些方向的差分,我们以sobel算子为例介绍如何计算梯度值和梯度方向:
x方向和y方向的Sobel算子分别为:

可以看出:Sx表示x方向的Sobel算子,用于检测y方向的边缘,它同时计算了左右方向的梯度和左上右下、左下右上方向的梯度;
     Sy表示y方向的Sobel算子,用于检测x方向的边缘, 它同时计算了上下方向的梯度和左上右下、左下右上方向的梯度;(边缘方向和梯度方向垂直)。

我们用x方向的sobel算子和原图进行卷积运算,就得到一阶导数值Gx,用y方向的sobel算子和原图进行卷积运算,就得到一阶导数值Gy。然后我们根据Gx、Gy就可以确定这个像素点的梯度大小G和方向θ,公式是:

 其中,G就是这个像素点的梯度大小,θ是这个像素点的梯度方向,arctan为反正切函数。梯度角度θ范围从弧度-π到π,然后把它近似到四个方向,分别代表水平,垂直和两个对角线方向(0°,45°,90°,135°)。
这样我们就求出了每个像素点的梯度值和梯度方向,如下图。(边缘方向和梯度方向垂直)!!!

扫描二维码关注公众号,回复: 14643806 查看本文章

3、非极大值抑制
遍历一阶梯度图像中的每个像素点(这个图像里面的每个像素点都是不仅有值的大小还有方向,就是我们上一步求出了梯度图),判断当前像素点是否是周围像素点中具有相同梯度方向的最大值,如果该点是这个方向上的最大值,则保留该点;如果不是,则该点归零,就是抑制该点。

  • 为什么要这样处理?
    因为对图像进行梯度计算后,仅仅基于梯度值提取的边缘仍然很模糊。对于标准3,对边缘有且应当只有一个准确的响应的标准来说,非极大值抑制可以帮助我们将局部最大值之外的所有梯度值抑制为0,这样就可以剔除掉一大部分的点,将有多个像素宽的边缘变成一个单像素宽的边缘,即将“胖边缘”变成“瘦边缘”。

  • 具体算法是什么?
    对梯度图像中每个像素进行非极大值抑制的算法是:
    1)将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较。
    2)如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制。
    通常为了更加精确的计算,在跨越梯度方向的两个相邻像素之间使用线性插值来得到要比较的像素梯度,例如:

     将梯度分为8个方向,分别为E、NE、N、NW、W、SW、S、SE,其中0代表0-45度,1代表45-90度,2代表-90--45度,3代表-45-0度。像素点P的梯度方向为θ,则像素点P1和P2的梯度
    线性插值为:
     

 因此,非极大值抑制的伪代码描写如下:
if  Gp >= Gp1  and  Gp >= Gp2:
   Gp may be an edge
else:
   Gp should be suppressed
需要注意的是,如何标志方向并不重要,重要的是梯度方向的计算要和梯度算子的选取保持一致。

4、双阈值检测
在进行了非极大值抑制之后,剩余的梯度像素已经可以较准确地表示图像中的实际边缘了。但是,还可能仍然存在一些由于噪声和颜色变化引起的一些伪边缘存在。为了解决这些杂散的伪边缘响应,必须要对梯度像素再过滤一遍。
这次我们过滤的手段是,用高低阈值来过滤。就是人为设置一个高阈值一个低阈值,保留高于高阈值的边缘像素,抑制低于低阈值的边缘像素。
如果边缘像素的值高于高阈值,将其标记为强边缘像素;如果边缘像素值小于高阈值但大于低阈值,则将其标记为弱边缘像素;如果边缘像素值小于低阈值,则会被抑制。
双阈值检测的伪代码描写如下:
if  Gp >= HighThreshold :
   Gp is an strong edge
else if Gp >= LowThreshold:
   Gp is an weak edge
else:
   Gp should be suppressed

说明:阈值的选择取决于给定输入图像的内容。

5、进一步确认弱边缘像素的去留 (抑制孤立点)
上面第4步我们代码已经写死:去除了一些低阈值像素,同时把其他像素区分了成了强边缘像素和弱边缘像素。
下面我们再通过弱边缘像素是否和强边缘相连这条规则,确定是要保留这个弱边缘像素还是抛弃这个边缘像素。用专业一点的话讲就是:抑制孤立点。
因为通常情况下,由真实边缘引起的弱边缘像素点是与强边缘相连接的,而由噪声响应的弱边缘是不和强边缘相连的。

 

抑制孤立边缘点的伪代码:
if  Gp == LowThreshold and Gp connected to a strong edge pixel :
   Gp is a strong edge
else:
   Gp should be suppressed

二、canny边缘检测API

cv2.Canny(img, threshold1, threshold2)

#例10.1 组合不同的参数观察canny检测的效果
import cv2
import numpy as np
import matplotlib.pyplot as plt

lena = cv2.imread(r'C:\Users\25584\Desktop\lena.bmp', 0)

lena_canny_1 = cv2.Canny(lena, 128, 200)   
lena_canny_2 = cv2.Canny(lena, 32, 128) 

fig, axes = plt.subplots(1,3, figsize=(10,6), dpi=100)
axes[0].imshow(lena, cmap='gray')
axes[1].imshow(lena_canny_1, cmap='gray')
axes[2].imshow(lena_canny_2, cmap='gray')
plt.show()

猜你喜欢

转载自blog.csdn.net/friday1203/article/details/125067415