# Canny 边缘检测

## 目标

在本章中，我们将了解

*   Canny 边缘检测的概念
*   OpenCV 的功能： **[cv.Canny（）](../../dd/d1a/group__imgproc__feature.html#ga04723e007ed888ddf11d9ba04e2232de "Finds edges in an image using the Canny algorithm  . ")**

## 理论

Canny 边缘检测是一种流行的边缘检测算法。它是由 John F. Canny 在 1986 年提出。

1.  这是一个多阶段算法，我们将介绍算法的每一个步骤。

2.  **降噪**

    由于边缘检测易受图像中的噪声影响，因此第一步是使用 5x5 高斯滤波器去除图像中的噪声。我们在前面的章节中已经介绍到了这一点。

3.  **寻找图像的强度梯度**

    然后在水平和垂直方向上用 Sobel 内核对平滑后的图像进行滤波，以获得水平方向（<a href="https://www.codecogs.com/eqnedit.php?latex=G_y" target="_blank"><img src="https://latex.codecogs.com/png.latex?G_x" title="G_x" /></a>）和垂直方向（<a href="https://www.codecogs.com/eqnedit.php?latex=G_y" target="_blank"><img src="https://latex.codecogs.com/png.latex?G_y" title="G_y" /></a>）的一阶导数。从这两个图像中，我们可以找到每个像素的边缘梯度和方向，如下所示：

    $$
    Edge\_Gradient \; (G) = \sqrt{G_x^2 + G_y^2} \\ Angle \; (\theta) = \tan^{-1} \bigg(\frac{G_y}{G_x}\bigg)
    $$
    

    渐变方向始终垂直于边缘。它被四舍五入到表示垂直，水平和两个对角线方向的四个角度中的一个。

4.  **非最大抑制**

    在获得梯度幅度和方向之后，完成图像的全扫描以去除可能不构成边缘的任何不需要的像素。为此，在每个像素处，检查像素是否是其在梯度方向上的邻域中的局部最大值。检查下图：

    ![nms.jpg](img/Image_canny_nms.jpg)

    A 点位于边缘（垂直方向）。渐变方向与边缘垂直。 B 点和 C 点处于梯度方向。因此，用点 B 和 C 检查点 A，看它是否形成局部最大值。如果是这样，则考虑下一阶段，否则，它被抑制（归零）。

    简而言之，您得到的结果是具有“细边”的二进制图像。

5.  **滞后阈值**

    这个阶段决定哪些边缘都是边缘，哪些边缘不是边缘。为此，我们需要两个阈值，minVal 和 maxVal。强度梯度大于 maxVal 的任何边缘肯定是边缘，而 minVal 以下的边缘肯定是非边缘，因此被丢弃。位于这两个阈值之间的人是基于其连通性的分类边缘或非边缘。如果它们连接到“可靠边缘”像素，则它们被视为边缘的一部分。否则，他们也被丢弃。见下图：

    ![hysteresis.jpg](img/Image_canny_hysteresis.jpg)

    边缘 A 高于 maxVal，因此被视为“确定边缘”。虽然边 C 低于 maxVal，但它连接到边 A，因此也被视为有效边，我们得到完整的曲线。但是边缘 B 虽然高于 minVal 并且与边缘 C 的区域相同，但它没有连接到任何“可靠边缘”，因此被丢弃。因此，我们必须相应地选择 minVal 和 maxVal 才能获得正确的结果。

    在假设边是长线的情况下，该阶段也消除了小像素噪声。

所以我们最终得到的是图像中的强边缘。

## OpenCV 中的 Canny 边缘检测

OpenCV 将以上所有内容放在单个函数中， **[cv.Canny（）](../../dd/d1a/group__imgproc__feature.html#ga04723e007ed888ddf11d9ba04e2232de "Finds edges in an image using the Canny algorithm  . ")** 。我们将看到如何使用它。第一个参数是我们的输入图像。第二个和第三个参数分别是我们的 minVal 和 maxVal。第三个参数是 aperture_size。它是用于查找图像渐变的 Sobel 内核的大小。默认情况下，它是 3.最后一个参数是 L2gradient，它指定用于查找梯度幅度的等式。如果它是 True，它使用上面提到的更准确的等式，否则它使用这个函数：
$$
Edge\_Gradient \; (G) = |G_x| + |G_y|
$$
默认情况下，它为 False。

```python
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()
```

结果如图所示：

![canny1.jpg](img/Image_canny_2.jpg)

## 其他资源

1.  [维基百科](http://en.wikipedia.org/wiki/Canny_edge_detector)的 Canny 边缘探测器
2.  [Canny 边缘检测教程](http://dasl.unlv.edu/daslDrexel/alumni/bGreen/www.pages.drexel.edu/_weg22/can_tut.html)，作者 Bill Green，2002 年。

## 练习

1.  编写一个小应用程序来查找 Canny 边缘检测，其阈值可以使用两个轨道栏进行更改。这样，您就可以了解阈值的影响。
