Canny边缘检测的概念
OpenCV函数用于:cv2.Canny()
步骤:
- 高斯模糊 - GaussianBlur
- 灰度转换 - cvtColor
- 计算梯度 – Sobel/Scharr
- 非最大信号抑制
- 高低阈值输出二值图像
Canny边缘检测:
是一种流行的边缘检测算法。它是由约翰·F·坎尼于1986年开发的。这是一个多阶段的算法。
1、降噪
由于边缘检测对图像中的噪声很敏感,第一步是用5x5高斯滤波器去除图像中的噪声。
2、灰度转换 - cvtColor
3、图像强度梯度的求取
然后在水平方向和垂直方向用Sobel核对平滑后的图像进行滤波,得到水平方向的一阶导数(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)
梯度方向总是垂直于边缘。它是圆形的四个角之一,代表垂直,水平和两个对角线方向。
4、非极大抑制
在获得梯度幅值和方向后,对图像进行全面扫描,以消除可能不构成边缘的任何不需要的像素。为此,在每个像素上,检查像素是否是其邻域内沿梯度方向的局部最大值。检查下面的图像:
Non-Maximum Suppression
A点在边缘(垂直方向)。梯度方向是正常的边缘。点B和C呈梯度方向。因此,用点B和点C检查点A,看看它是否形成局部最大值。如果是,则考虑到下一阶段,否则就会被抑制(将为零)。
简而言之,你得到的结果是一幅带有“薄边”的二值图像。
5、迟滞阈值
这个阶段决定了哪些是边缘,哪些是真正的边,哪些不是边。为此,我们需要两个阈值,MinVal和MaxVal。任何强度梯度大于MaxVal肯定是边,而小于MinVal肯定是非边缘的,所以被丢弃了。处于这两个阈值之间的人根据其连通性分为边缘或非边缘。如果它们连接到“确定边缘”像素,则它们被视为边缘的一部分。否则,它们也会被丢弃。见下图:
Hysteresis Thresholding
边缘A在MaxVal,因此被认为是“确定边缘”。虽然边缘C在MaxVal下面,但是它与边A相连,因此也被认为是有效边,我们得到了这条全曲线。但是边缘B,虽然它在上面MinVal并且与边缘C的区域相同,它没有连接到任何“确定边”,因此被丢弃。所以我们必须相应地选择MinVal和MaxVal,得到正确的结果。
这一阶段也消除了小像素噪声的假设,边缘是长线。所以我们最终得到的是图像中的强边。
OpenCV中的Canny边缘检测
OpenCV将上述所有功能都放在一个函数中,cv2.Canny()。我们会看看如何使用它。第一个参数是我们的输入图像。第二个和第三个参数分别是我们的MinVal和MaxVal。第三个参数是aperture_size。它是用于查找图像梯度的Sobel核的大小。默认情况下是3。最后一个参数是L2gradient ,它指定了求梯度幅值的公式。如果是True,它使用上面提到的更精确的方程,否则它使用这个函数:Edge_Gradient ; (G) = |G_x| + |G_y|。默认情况下,它是False
代码实现:
import cv2 as cv
import numpy as np
#边缘检测
def edge_demo(image):
#高斯模糊:降噪
blurred = cv.GaussianBlur(image, (3, 3), 0)
#灰度转换
gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)
# X Gradient,深度cv.CV_16SC1 整型
xgrad = cv.Sobel(gray, cv.CV_16SC1, 1, 0)
# Y Gradient
ygrad = cv.Sobel(gray, cv.CV_16SC1, 0, 1)
#edge
#edge_output = cv.Canny(xgrad, ygrad, 50, 150)
#50, 150 低阈值 高阈值
edge_output = cv.Canny(gray, 50, 150)
cv.imshow("Canny Edge", edge_output)
#bitwise_and:变成彩色
dst = cv.bitwise_and(image, image, mask=edge_output)
cv.imshow("Color Edge", dst)
print("--------- Python OpenCV Tutorial ---------")
src = cv.imread("D:/vcprojects/images/topstar.png")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
edge_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
结果: