目标
- 掌握Canny边缘检测的概念
- 掌握OpenCV函数:cv2.Canny()
理论
Canny边缘检测是一种十分常用的边缘检测算法。
1、Canny算法是一个多级算法,我们将在每一个阶段涉及到这个算法;
2、消除噪声
因为边缘检测容易受到图像中的噪音影响,因此,首先都会使用一个5×5的高斯滤波器去去除噪声。关于图像滤波前面章节已经介绍过来。
3、计算图像梯度
对平滑过后的图像采用Sobel算子分别计算水平方向梯度和垂直方向梯度。然后从这两个结果中,我们可以获得每一个像素的梯度和方向,按照以下公式计算:
梯度方向一般总是与边缘垂直,并且梯度方向可以归纳为垂直、水平和两个对角线方向。
4、去除非边界
在获得梯度和方向后,将要遍历图像上的所有像素点,去除所有可能不在边界上的像素点。实现这个目的的方法为,在每一像素点处,判断当前像素点是否是周围像素点中具有相同方向梯度的最大值。对下面的图片进行检测,
点A是在边缘上(在垂直方向),梯度方向垂直与边缘。点B和点C都在梯度方向上,因此,点A将要和点B和C进行比较,确定点A是否为局部最大值。如果点A是局部最大值,则将保留进行下一步计算,否则将被抑制(设置为0)。
5、滞后阈值发(Hysteresis Thresholding)
这一节将确定在所有边界中哪些是真的边界,哪些不是边界。这一步需要设置两个阈值,滞后阈值1(minVal)和滞后阈值2(maxVal)。如果一个边界的梯度强度大于最大值则认为是边界,如果小于最小值则认为不是边界。那些在最大值与最小值之间的边界则通过是否与确定的边界相连来决定是否保留,与边界相连则保留,否则抛弃。具体情况如下图所示,
边界A大于最大值,因此认为是“确定的边界”。尽管边界C小于最大值,但是它与边界A相连,则认为是有效的边界,我们则获得全部的曲线。但是对于边界B,尽管它大于最小值,但是它没有和任何确定的边界想连接,因此抛弃边界B。因此,为了获得正确的结果,我们需要选择合适的minVal和maxVal。
这一步也去除了小的噪声点,因为假设边界都是很长的线。
通过上面的5个步骤,我们最终可以获得图像中清晰的边界。
使用OpenCV实现Canny边缘检测
在OpenCV中,函数cv2.Canny()完成上面所有的操作。
edges = cv2.Canny( image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]])
第一个参数为输入图像;第二个参数和第三个参数分别为滞后阈值1和滞后阈值2;apertureSize为Sobel卷积核大小,默认值为3;最后一个参数是标识符,指定计算梯度值的公式,如果为真,则采用上面介绍的准确计算公式,否则将采用等效计算公式。
下面的代码将对一副图片进行Canny边缘检测
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('image\lena.jpg',0)
# 设置滞后阈值1为100,滞后阈值2为200
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()
结果如下所示: