OpenCV——Canny边缘检测算法

问题描述

图像分割是将数字图像细分为多个子区域的过程,在计算机视觉/机器视觉领域被广泛应用。它的目的是简化或改变图像的表示形式,以便更容易理解和分析。常见的图像分割方法包括阈值处理、聚类法、边缘检测和区域生长等。解决图像分割问题通常需要结合领域知识,以提高解决效果。

边缘检测是一种常用的图像分割方法,通过提取图像中不连续部分的特征来实现。目前,常见的边缘检测算子包括差分算子、Roberts算子、Sobel算子、Prewitt算子、Log算子和Canny算子。Canny算子是由John F. Canny于1986年提出的一种边缘检测算子,被认为是目前最完善的边缘检测算法之一。许多常用的图像处理工具(如MATLAB、OpenCV)都内置了Canny算子的API。

Canny边缘检测

Canny边缘检测是一种经典的边缘检测算法,于1986年由John F. Canny提出。它被广泛应用于计算机视觉和图像处理领域,用于检测图像中的边缘信息。

论文信息:

标题:A Computational Approach to Edge Detection

作者:John F. Canny

出版年份:1986年

Canny边缘检测算法的步骤

Canny边缘检测算法的目标是找到图像中的强边缘,并尽量消除噪声和弱边缘。该算法的步骤如下:

  1. 噪声抑制:首先,使用高斯滤波器对图像进行平滑处理,以减少噪声的影响。

  2. 计算梯度:然后,计算图像中每个像素点的梯度强度和方向。这可以通过应用Sobel等滤波器来实现。

  3. 非极大值抑制:接下来,对梯度强度图像进行非极大值抑制,以细化边缘并消除边缘响应。

  4. 双阈值处理:然后,使用双阈值处理来确定边缘的强度。根据设定的阈值,将边缘像素分为强边缘、弱边缘和非边缘像素。

  5. 边缘连接:最后,通过边缘连接算法来连接强边缘像素和与之相邻的弱边缘像素,以形成完整的边缘。

Canny边缘检测算法在图像处理领域广受赞誉,它能够检测到细微的边缘,并且对噪声具有较好的鲁棒性。

1.应用高斯滤波去除图像噪声

高斯滤波器是一种平滑滤波器,通过对图像进行卷积操作,降低像素值之间的差异,从而减少噪声的影响。

高斯滤波器的核(或模板)是一个二维的权重矩阵,其中权重值由高斯函数计算得出。该权重矩阵的大小和标准差(σ)是高斯滤波器的两个关键参数,决定了滤波器的平滑程度。

二维高斯函数可以表示为:

G(x, y) = \frac{1}{2\pi \sigma ^{2}} exp(-\tfrac{x^{2}+y^{2}}{2\sigma ^{2}})

其中,G(x, y)表示二维高斯函数在点(x, y)处的值,σ表示标准差。 

对于图像中的每个像素,应用高斯滤波器可以通过以下卷积操作实现:

I' = G * I

其中,I'表示滤波后的图像,G表示高斯滤波器核,I表示原始图像。

具体而言,对于每个像素点,将高斯滤波器核与以该像素为中心的邻域进行卷积操作,计算邻域内像素值的加权平均值,作为该像素在滤波后图像中的值。通过这种方式,高斯滤波器可以模糊图像并减少噪声的影响。

需要注意的是,高斯滤波器的大小和标准差需要根据具体的应用场景进行选择。较大的滤波器大小和较小的标准差可以提供更强的平滑效果,但可能会导致边缘信息的模糊。相反,较小的滤波器大小和较大的标准差可以保留更多细节,但可能无法有效抑制噪声。

二维高斯滤波器的标准差σ在水平和垂直方向上是相等的,以保持滤波器的各向同性。选择合适的标准差取决于噪声水平和图像特性,较大的标准差可以提供更强的平滑效果,但可能会模糊边缘和细节。

 2. 使用Sobel算子计算像素梯度

Sobel算子是一种常用的离散微分算子,它可以计算图像中每个像素点的梯度强度和方向。在Canny边缘检测中,通常使用Sobel算子来计算图像在水平和垂直方向上的梯度。

对于灰度图像,我们可以通过以下Sobel算子来计算像素的梯度:

水平方向的梯度(Gx):

      -1  0  1
Gx =  -2  0  2
      -1  0  1

垂直方向的梯度(Gy):

      -1 -2 -1
Gy =   0  0  0
       1  2  1

下面是使用Sobel算子计算像素梯度的具体步骤:

  1. 将图像转换为灰度图像(如果不是灰度图像)。

  2. 对图像应用水平方向的Sobel算子(Gx)和垂直方向的Sobel算子(Gy)。

  3. 计算每个像素的梯度强度和方向:

    G = sqrt(Gx^2 + Gy^2)       (梯度强度)
    θ = arctan(Gy / Gx)         (梯度方向)
    

    其中,G表示梯度强度,θ表示梯度方向。

这样,我们就可以得到图像中每个像素的梯度强度和方向。这些梯度信息将在Canny边缘检测算法的后续步骤中使用,以检测和连接边缘。

 3. 非极大值像素梯度抑制

在该步骤中,我们需要检查每个像素点的梯度方向上的相邻像素,并保留梯度强度最大的像素,将其他像素抑制为零。

为了更好地描述非极大值抑制的过程,我们将假设图像的梯度方向离散化为四个主要方向:0°(水平方向)、45°(对角线方向)、90°(垂直方向)和135°(对角线方向)。对于每个像素点,我们比较其梯度强度与沿着梯度方向的两个相邻像素的梯度强度。

如果像素点的梯度方向是0°(水平方向),我们比较其梯度强度与其左右两个相邻像素的梯度强度。

假设当前像素点为(x,y),其梯度方向为0°,梯度强度为G(x, y)。我们比较G(x, y)与两个相邻像素的梯度强度:G(x-1, y)和G(x+1, y)。

  • 如果G(x, y)是三个值中最大的,我们保留该像素值,否则将其抑制为零。

具体而言,我们使用线性插值来判断是否保留像素值。如果G(x, y)介于G(x-1, y)和G(x+1, y)之间,那么我们通过线性插值计算相应的权重w:

w = \frac{ |G(x, y) - G(x-1, y)|}{ |G(x+1, y) - G(x-1, y)|}

最终,我们保留像素值的条件是: 

G(x, y) >= w * G(x+1, y) + (1 - w) * G(x-1, y) 

根据具体的图像数据和梯度方向,上述过程将分别应用于其他方向(45°、90°和135°)。

4. 阈值滞后处理

完成上述步骤后,图像内的强边缘已经在当前获取的边缘图像内。但是,一些虚边缘可能也在边缘图像内。这些虚边缘可能是真实图像产生的,也可能是由于噪声所产生的。对于后者,必须将其剔除。

在这一步骤中,我们使用双阈值处理来确定边缘的强度。

  • 首先,根据设定的高阈值和低阈值,将梯度强度图像的像素分为三类:强边缘、弱边缘和非边缘。
  • 强边缘像素的梯度强度高于高阈值,
  • 非边缘像素的梯度强度低于低阈值,
  • 弱边缘像素的梯度强度在两个阈值之间。

5. 边缘连接

在最后一步中,通过边缘连接算法来连接强边缘像素和与之相邻的弱边缘像素,以形成完整的边缘。通常,如果一个弱边缘像素与至少一个强边缘像素相邻,那么它将被认为是边缘的一部分。这个过程可以通过递归或迭代实现。

程序流程图

图片链接: https://pic4.zhimg.com/80/v2-7400b8669ef4c750aeff27ed699ba7c7_720w.webp

Canny 函数及使用

OpenCV 提供了函数 cv2.Canny()来实现 Canny 边缘检测,其语法形式如下:

edges = cv.Canny( image, threshold1, threshold2[, apertureSize[, L2gradient]])

 其中:

  •  edges 为计算得到的边缘图像。
  • image 为 8 位输入图像。
  • threshold1 表示处理过程中的第一个阈值。
  • threshold2 表示处理过程中的第二个阈值。
  • apertureSize 表示 Sobel 算子的孔径大小。
  • L2gradient 为计算图像梯度幅度(gradient magnitude)的标识。其默认值为 False。如果为 True,则使用更精确的 L2 范数进行计算(即两个方向的导数的平方和再开方),否则使用 L1 范数(直接将两个方向导数的绝对值相加)。


示例:
使用函数 cv2.Canny()获取图像的边缘,并尝试使用不同大小的 threshold1 和threshold2。

import cv2
o=cv2.imread("lena.bmp",cv2.IMREAD_GRAYSCALE)
r1=cv2.Canny(o,128,200)
r2=cv2.Canny(o,32,128)
cv2.imshow("original",o)
cv2.imshow("result1",r1)
cv2.imshow("result2",r2)
cv2.waitKey()
cv2.destroyAllWindows()

运行结果: 

从程序运行结果可知,当函数 cv2.Canny()的参数 threshold1 和 threshold2 的值较小时,能够捕获更多的边缘信息。 

猜你喜欢

转载自blog.csdn.net/weixin_45897172/article/details/131030659