一、Canny边缘检测的过程
- 使用高斯滤波器,进行图像平滑操作,滤除噪声
具体图像平滑操作,可以参考下面链接
https://blog.csdn.net/qq_43279579/article/details/120766454 - 计算图像中每个像素点的梯度张度和方向
- 应用非极大值抑制,消除边缘检测带来的杂散响应
- 应用双阈值检测,来确定真实的和潜在的边缘
二、Canny边缘检测过程的详细介绍
(一)平滑滤除噪声
- 高斯滤波器
H = [ 0.0924 0.1192 0.0924 0.1192 0.1538 0.1192 0.0924 0.1192 0.0924 ] H= \begin{bmatrix} 0.0924 & 0.1192 & 0.0924 \\ 0.1192 & 0.1538 & 0.1192 \\ 0.0924 & 0.1192 & 0.0924 \\ \end{bmatrix} H=⎣⎡0.09240.11920.09240.11920.15380.11920.09240.11920.0924⎦⎤ - 滤波后的结果
e = H ∗ A = [ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] ∗ [ a b c d e f g h i ] e=H*A= \begin{bmatrix} h_{11} & h_{12} & h_{13} \\ h_{21} & h_{22} & h_{23} \\ h_{31} & h_{32} & h_{33} \\ \end{bmatrix} *\begin{bmatrix} a & b & c \\ d & e & f \\ g & h & i \\ \end{bmatrix} e=H∗A=⎣⎡h11h21h31h12h22h32h13h23h33⎦⎤∗⎣⎡adgbehcfi⎦⎤
通过对应项乘积求和,得到最中心像素点的值。通过上面方式,求解每个像素点,从而获得滤波去噪的图像。
(二)梯度和方向
- 梯度的求法利用Sobel算子
具体过程可以参考下面链接
https://blog.csdn.net/qq_43279579/article/details/120785076 - 方向求解
θ = arctan ( G y / G x ) \theta=\arctan(G_y/G_x) θ=arctan(Gy/Gx)
其中,Gy表示垂直方向的梯度,Gx表示水平方向的梯度。
(三)非极大值抑制
- 线性插值法
C表示需要判断是否为极大值的像素点,G1,G2,G3,G4分别为G邻近的像素点,QN表示C的梯度方向,从N到Q。求解G1,G2,G3,G4的梯度值分别为M(Gi)(i=1,2,3,4)。
w = G 1 Q / G 1 G 2 w=G_1Q/G_1G_2 w=G1Q/G1G2
Q点的梯度
M ( Q ) = w ∗ M ( G 1 ) + ( 1 − w ) ∗ M ( G 2 ) M(Q)=w*M(G_1)+(1-w)*M(G_2) M(Q)=w∗M(G1)+(1−w)∗M(G2)
同理可以求解N点的梯度。
判断C点梯度是否是其中最大的梯度点,如果是最大的点表示,C是其中的极大值点。如果不是最大梯度,表示C不是边缘点,应该抑制它。 - 简化方法
一个像素点包含8个邻近的像素点,把一个像素点的梯度分散为8个方向,这样就不用计算插值。就只需要判断该点的梯度方向上邻近的点是否小于目标像素点。
(四)双阈值
- 阈值说明
maxval表示最大梯度阈值,minval表示最小梯度阈值。
当梯度值>maxval,则处理为边界。
当minval<梯度值<maxval,则连接边界就保留,否则舍弃。
当梯度值<minval,舍弃。
对于C点来说,它与A连接,所以保留,而对于B来说,它未与边界的点连接,所以舍弃。
三、Canny的实现
-
opencv的Canny函数
def Canny(image: Any, threshold1: Any, threshold2: Any, edges: Any = None, apertureSize: Any = None, L2gradient: Any = None)
部分参数说明
threshold1:最小阈值
threshold2:最大阈值 -
实际实现举例
import cv2 import numpy as np def showImg(img, name): cv2.imshow(name, img) cv2.waitKey(10000) cv2.destroyAllWindows() lena = cv2.imread("Lena.png", cv2.IMREAD_GRAYSCALE) # Canny的边缘检测 canny1 = cv2.Canny(lena, 80, 150) canny2 = cv2.Canny(lena, 50, 100) result = np.hstack((lena, canny1, canny2)) showImg(result, 'result')
通过结果对比,可以发现双阈值指定的越小,边界就体现的越明细
。