本章将了解如下内容:
- Canny边缘检测的概念
- OpenCV函数:
cv2.Canny()
理论基础
Canny边缘检测是一种流行的边缘检测算法,由John F.Canny于1986年开发。 这是一个多阶段的算法,我们将经历其每个阶段的算法。
降噪
由于边缘检测容易受到图像中噪声的影响,第一步是用5x5高斯滤波器去除图像中的噪声。 在前面的章节中已经看到了这一点。
寻找图像的强度梯度
平滑后的图像在水平和垂直方向上用Sobel kernel进行滤波, 以获得水平方向的一阶导数 ($G_x$) 和垂直方向 ($G_y$)。 从这两幅图像中可以找到每个像素的边缘梯度和方向,如下所示:
$$Edge\_Gradient \; (G) = \sqrt{G_x^2 + G_y^2}$$$$Angle \; (\theta) = \tan^{-1} \bigg(\frac{G_y}{G_x}\bigg)$$
渐变方向始终垂直于边。 它被四舍五入到代表垂直、水平和两个对角线方向的四个角度之一。
非极大值抑制
在获得梯度幅度和方向后,对图像进行全扫描,以去除可能不构成边缘的任何不需要的像素。 为此,在每个像素处,检查像素在梯度方向上是否是其邻域中的局部最大值。请查看下图:
点A位于边缘(垂直方向)。渐变方向垂直于边缘。点B和C在梯度方向上。 因此,用点B和C检查点A,看它是否形成局部最大值。 如果是这样,则将其考虑到下一阶段,否则将其抑制(置零)。
简而言之,得到的结果是一个具有“薄边缘”的二值图像。
滞后阈值
这个阶段决定哪些边都是真正的边,哪些不是。为此需要两个阈值, $minVal$ 和 $maxVal$。 强度梯度大于 $maxVal$ 的任何边都肯定是边,低于 $minVal$ 的边肯定是非边,因此被丢弃。 位于这两个阈值之间的那些根据其连接性被分类为边缘或非边缘。 如果它们连接到“确定边缘”像素,则被视为边缘的一部分。 否则,它们也会被丢弃。请参见下图:
边缘A高于$maxVal$,因此被视为“确定边”。 虽然边缘C低于$maxVal$,但它连接到边缘A,因此也被视为有效边,获得了完整的曲线。 但是边缘B,虽然它高于$minVal$并且与边缘C位于同一区域,但它没有连接到任何“确定边”,因此被丢弃。 因此必须根据需要选择$minVal$和$maxVal$以获得正确的结果,这一点非常重要。
此阶段还可以在假设边缘是长线的情况下消除小像素噪声。 所以最终得到的是图像中的强边缘。
OpenCV中的Canny边缘检测
OpenCV将上述所有内容放在一个函数cv2.Canny()
中。接下来看一下如何使用。
第一个参数是输入图像。第二个和第三个参数分别是$minVal$和$maxVal$。
第三个参数是$aperture_size$。它是用于查找图像梯度的Sobel核的大小。
默认值为3。最后一个参数是$L2graduation$,它指定了寻找梯度幅度的方程。
如果为“True”,则使用上述更准确的方程,
否则使用以下函数:$Edge\_Gradient \; (G) = |G_x| + |G_y|$。
默认情况下为“False”。
%matplotlib inline
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('/data/cvdata/messi5.jpg',0)
edges = cv2.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()
cv2.__version__
'4.11.0'
如下图所示:
扩展阅读
- Canny边缘检测器维基百科
- Canny边缘检测教程,Bill Green, 2002
练习
- 编写一个小应用程序来查找Canny边缘检测,其阈值可以使用两个轨迹条进行更改。这样就可以了解阈值的影响。