1. 原理
边缘检测是一种流行的边缘检测算法。它是由 John F. Canny 开发的.
- 这是一个多阶段算法,我们将经历多个阶段。
- 降噪:由于边缘检测容易受到图像中噪声的影响,第一步是使用 5x5 高斯滤波器去除图像中的噪声。我们已经在前面的章节中看到了这一点。
- 寻找图像的强度梯度:平滑的图像然后在水平和垂直方向使用 Sobel 核进行滤波,以获得水平方向 (Gx) 和垂直方向 (Gy) 的一阶导数。从这两张图像中,我们可以找到每个像素的边缘梯度和方向,如下所示:
E d g e G r a d i e n t ( G ) = G x 2 + G y 2 Edge_Gradient(G)=\sqrt{G^2_x+G^2_y} EdgeGradient(G)=Gx2+Gy2
A n g l e ( θ ) = t a n − 1 ( G y G x ) Angle(θ)=tan^{-1}(\frac{Gy}{Gx}) Angle(θ)=tan−1(GxGy) - 非最大抑制:在获得梯度大小和方向后,对图像进行全扫描以去除可能不构成边缘的任何不需要的像素。为此,在每个像素处,检查像素是否在梯度方向的邻域中是局部最大值。检查下面的图像:
点 A 在边缘上(在垂直方向)。渐变方向垂直于边缘。 B点和C点在梯度方向。所以点 A 与点 B 和 C 一起检查,看它是否形成局部最大值。如果是,则考虑进入下一阶段,否则,将其抑制(置零)。
简而言之,得到的结果是具有“细边缘”的二值图像。 - 滞后阈值 :这个阶段决定哪些边是真正的边,哪些不是。为此,我们需要两个阈值 minVal 和 maxVal。任何强度梯度大于 maxVal 的边缘肯定是边缘,而那些低于 minVal 的肯定是非边缘,因此被丢弃。位于这两个阈值之间的那些根据它们的连通性被分类为边缘或非边缘。如果它们连接到“确定边缘”像素,则它们被认为是边缘的一部分。否则,它们也会被丢弃。见下图:
边 A 高于 maxVal,因此被视为“确定边”。尽管边 C 低于 maxVal,但它连接到边 A,因此它也被视为有效边,我们得到了完整的曲线。但是边 B,虽然它高于 minVal 并且与边 C 处于同一区域,但它没有连接到任何“确定边”,因此将其丢弃。因此,我们必须相应地选择 minVal 和 maxVal 以获得正确的结果,这一点非常重要。
在假设边缘是长线的情况下,此阶段还去除了小像素噪声。
所以我们最终得到的是图像中的强边缘
2. OpenCV 中的 Canny 边缘检测
python中用法:
edges = cv.Canny( image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]] )
edges = cv.Canny( dx, dy, threshold1, threshold2[, edges[, L2gradient]] )
这是一个重载的成员函数,为了方便而提供。它与上述函数的不同之处仅在于它接受的参数。
使用具有自定义图像梯度的 Canny 算法查找图像中的边缘。
OpenCV 将上述所有内容放在单个函数 cv.Canny() 中。我们将看到如何使用它。第一个参数是我们的输入图像。第二个和第三个参数分别是我们的 minVal 和 maxVal。第三个参数是aperture_size。它是用于查找图像梯度的 Sobel 核的大小。默认情况下它是 3。最后一个参数是 L2gradient,它指定了寻找梯度幅度的方程。如果为 True,则使用上述公式更准确,否则使用此函数: E d g e _ G r a d i e n t ( G ) = ∣ G x ∣ + ∣ G y ∣ Edge\_Gradient(G)=|G_x|+|G_y| Edge_Gradient(G)=∣Gx∣+∣Gy∣。默认情况下,它是 False。
参数 | 描述 |
---|---|
dx | 输入图像的 16 位 x 导数(CV_16SC1 或 CV_16SC3)。 |
dst | 输入图像的 16 位 y 导数(与 dx 类型相同) |
edges | 输出边缘图;单通道 8 位图像,与图像大小相同 |
threshold1 | 滞后过程的第一个阈值。 |
threshold2 | 滞后过程的第二个阈值。 |
L2gradient | 一个标志,指示是否应该使用更准确的 L 2 n o r m = ( d I / d x ) 2 + ( d I / d y ) 2 L2 norm=\sqrt{(dI/dx)^2+(dI/dy)^2} L2norm=(dI/dx)2+(dI/dy)2来计算图像梯度幅度( L2gradient=true ),或者是否默认 ( L2gradient=false )。 |
img = cv.imread("../../file/photos/messi5.jpg", 0)
edges = cv.Canny(img, 100, 200)
cv.imwrite("edges.jpg", edges)
plt.subplot(121), plt.imshow(img, 'gray'), plt.title("Oringal")
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(edges, 'gray'), plt.title("edges")
plt.xticks([]), plt.yticks([])
plt.show()
结果如下: