介绍
Canny边缘检测是一种多阶段的算法,广泛应用于计算机视觉和图像处理领域,用于识别数字图像中的边缘。该方法由John F. Canny在1986年提出,并因其良好的性能和鲁棒性而成为标准的边缘检测技术之一。OpenCV提供了内置函数cv2.Canny()
来执行Canny边缘检测。下面将详细介绍Canny边缘检测的工作原理及其在OpenCV中的应用。
Canny 边缘检测的步骤
Canny边缘检测主要包括以下几个关键步骤:
噪声去除:由于实际图像中通常包含噪声,这些噪声会影响后续的梯度计算,因此首先需要使用高斯滤波器对输入图像进行平滑处理,以减少高频噪声的影响。
计算梯度强度和方向:通过Sobel算子或其他类似的方法,在水平和垂直方向上分别计算图像的梯度,从而得到每个像素点处的梯度强度(即梯度幅度)以及梯度的方向。
非极大值抑制(Non-Maximum Suppression, NMS):这一步骤旨在细化边缘,只保留局部最大值的像素点作为候选边缘点。具体来说,对于每一个像素点,如果它的梯度强度不是其所在方向上的局部最大值,则将其设置为0(即非边缘点)。
双阈值(Double Thresholding):设定两个阈值——高阈值和低阈值。任何大于高阈值的像素都被认为是强边缘点;而介于高低阈值之间的像素则被标记为弱边缘点,等待进一步验证。
边缘追踪(Edge Tracking by Hysteresis):对于那些被标记为弱边缘点的像素,只有当它们与强边缘点相连时才会被认为是真正的边缘点。这个过程称为滞后效应(hysteresis),它可以有效地连接断开的边缘段落,同时避免孤立的噪声点被误判为边缘。
总结
Canny边缘检测以其精确性和可靠性成为了一种经典的边缘检测算法,适用于各种不同的应用场景。通过合理配置参数并结合适当的预处理手段,如高斯模糊,我们可以获得高质量的边缘信息,为进一步的图像分析任务打下坚实的基础。此外,OpenCV提供的高效实现使得Canny边缘检测易于集成到更复杂的计算机视觉系统中。
理论
Canny 边缘检测是一种流行的边缘检测算法。它是由 John F. Canny 在 1986 年提出。
这是一个多阶段算法,我们将介绍算法的每一个步骤。
降噪
由于边缘检测易受图像中的噪声影响,因此第一步是使用 5x5 高斯滤波器去除图像中的噪声。我们在前面的章节中已经介绍到了这一点。
寻找图像的强度梯度
然后在水平和垂直方向上用 Sobel 内核对平滑后的图像进行滤波,以获得水平方向(编辑)和垂直方向(编辑)的一阶导数。从这两个图像中,我们可以找到每个像素的边缘梯度和方向
渐变方向始终垂直于边缘。它被四舍五入到表示垂直,水平和两个对角线方向的四个角度中的一个。
非最大抑制
在获得梯度幅度和方向之后,完成图像的全扫描以去除可能不构成边缘的任何不需要的像素。为此,在每个像素处,检查像素是否是其在梯度方向上的邻域中的局部最大值。检查下图:
A 点位于边缘(垂直方向)。渐变方向与边缘垂直。 B 点和 C 点处于梯度方向。因此,用点 B 和 C 检查点 A,看它是否形成局部最大值。如果是这样,则考虑下一阶段,否则,它被抑制(归零)。
简而言之,您得到的结果是具有“细边”的二进制图像。
滞后阈值
这个阶段决定哪些边缘都是边缘,哪些边缘不是边缘。为此,我们需要两个阈值,minVal 和 maxVal。强度梯度大于 maxVal 的任何边缘肯定是边缘,而 minVal 以下的边缘肯定是非边缘,因此被丢弃。位于这两个阈值之间的人是基于其连通性的分类边缘或非边缘。如果它们连接到“可靠边缘”像素,则它们被视为边缘的一部分。否则,他们也被丢弃。见下图:
边缘 A 高于 maxVal,因此被视为“确定边缘”。虽然边 C 低于 maxVal,但它连接到边 A,因此也被视为有效边,我们得到完整的曲线。但是边缘 B 虽然高于 minVal 并且与边缘 C 的区域相同,但它没有连接到任何“可靠边缘”,因此被丢弃。因此,我们必须相应地选择 minVal 和 maxVal 才能获得正确的结果。
在假设边是长线的情况下,该阶段也消除了小像素噪声。
所以我们最终得到的是图像中的强边缘。
OpenCV 中的 Canny 边缘检测
OpenCV 将以上所有内容放在单个函数中, cv.Canny() 。我们将看到如何使用它。第一个参数是我们的输入图像。第二个和第三个参数分别是我们的 minVal 和 maxVal。第三个参数是 aperture_size。它是用于查找图像渐变的 Sobel 内核的大小。默认情况下,它是 3.最后一个参数是 L2gradient,它指定用于查找梯度幅度的等式。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg',0)
edges = cv.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()
结果如图所示: