Opencv Canny边缘检测
边缘检测的目的是找到灰度值的突变
步骤:
- 使用高斯滤波,以平滑图像、滤除噪声
- 计算图像中每个像素点的梯度强度和方向
- 应用非极大值预测,以消除边缘检测的杂散响应
- 应用双阈值检测来确定真实的和潜在的边缘
- 通过抑制孤立的弱边缘最终完成边缘检测
5.1 高斯滤波器
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
H
H
H为3×3高斯滤波器,经过归一化处理。
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
]
=
sum
(
[
a
×
h
11
b
×
h
12
c
×
h
13
d
×
h
21
e
×
h
22
f
×
h
23
g
×
h
31
h
×
h
32
i
×
h
33
]
)
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} = \text{sum} \left( \begin{bmatrix}a \times h_{11} & b \times h_{12} & c \times h_{13} \\d \times h_{21} & e \times h_{22} & f \times h_{23} \\g \times h_{31} & h \times h_{32} & i \times h_{33}\end{bmatrix} \right)
e=H∗A=
h11h21h31h12h22h32h13h23h33
∗
adgbehcfi
=sum
a×h11d×h21g×h31b×h12e×h22h×h32c×h13f×h23i×h33
A
A
A为图像区域,与高斯滤波器对应元素相乘后得到
e
e
e为滤除噪声后的结果矩阵。
5.2 梯度和方向
S x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] S y = [ 1 2 1 0 0 0 − 1 − 2 − 1 ] S_x = \begin{bmatrix}-1 & 0 & 1 \\-2 & 0 & 2 \\-1 & 0 & 1\end{bmatrix}S_y = \begin{bmatrix}1 & 2 & 1 \\0 & 0 & 0 \\-1 & -2 & -1\end{bmatrix} Sx= −1−2−1000121 Sy= 10−120−210−1
这是Sobel算子的两个卷积核
S
x
S_{x}
Sx和
S
y
S_{y}
Sy。
G
x
=
S
x
∗
A
=
[
−
1
0
1
−
2
0
2
−
1
0
1
]
∗
[
a
b
c
d
e
f
g
h
i
]
=
sum
(
[
−
a
0
c
−
2
d
0
2
f
−
g
0
i
]
)
G
y
=
S
y
∗
A
=
[
1
2
1
0
0
0
−
1
−
2
−
1
]
∗
[
a
b
c
d
e
f
g
h
i
]
=
sum
(
[
a
2
b
c
0
0
0
−
g
−
2
h
−
i
]
)
G_x = S_x * A = \begin{bmatrix}-1 & 0 & 1 \\-2 & 0 & 2 \\-1 & 0 & 1\end{bmatrix} * \begin{bmatrix}a & b & c \\d & e & f \\g & h & i\end{bmatrix} = \text{sum} \left( \begin{bmatrix}-a & 0 & c \\-2d & 0 & 2f \\-g & 0 & i\end{bmatrix} \right) \\G_y = S_y * A = \begin{bmatrix}1 & 2 & 1 \\0 & 0 & 0 \\-1 & -2 & -1\end{bmatrix} * \begin{bmatrix}a & b & c \\d & e & f \\g & h & i\end{bmatrix} = \text{sum} \left( \begin{bmatrix}a & 2b & c \\0 & 0 & 0 \\-g & -2h & -i\end{bmatrix} \right)
Gx=Sx∗A=
−1−2−1000121
∗
adgbehcfi
=sum
−a−2d−g000c2fi
Gy=Sy∗A=
10−120−210−1
∗
adgbehcfi
=sum
a0−g2b0−2hc0−i
计算x和y方向上的梯度,再计算总梯度和方向
G
=
G
x
2
+
G
y
2
θ
=
arctan
(
G
y
G
x
)
G = \sqrt{G_x^2 + G_y^2}\\\theta = \arctan\left(\frac{G_y}{G_x}\right)
G=Gx2+Gy2θ=arctan(GxGy)
5.3 非极大值抑制
非极大值抑制目的是确保检测到的边缘是细的、连续的,并且尽可能地精确。这一步骤非极大值抑制能够有效地抑制非边缘点,保留真正的边缘点。
非极大值抑制有两种方法:
线性插值法
图中ABCDE每个点代表一个像素点,E点的梯度为蓝色的线,梯度方向指向左上角,边缘与梯度方向垂直。想确定这条梯度线上的边缘,就要比较E点的梯度和f、g点的梯度,而f、g点为亚像素点,所以f点需要通过A点与B点的梯度值求得。
f点的梯度值 = M(A)*w1+M(B)*w2,其中M表示梯度幅值,w1可等于L(Af)/L(AB),同理w2也是如此
同理,可求得g点的梯度值,从而比较三个点的梯度值,确定哪点最大该点与梯度值的垂直方向就是边缘。
梯度方向离散化
我们可以把一个像素的梯度方向离散化为八个方向,这样就只需计算前后即可,不需要插值
将每个像素的梯度方向分解为八个方向。假设梯度方向是45°,如果点 A 的梯度幅值大于其相邻像素点的梯度幅值,则保留点 A;否则,抑制点 A。
5.4 双阈值检测
梯度幅值大于高阈值的像素被标记为强边缘像素,而梯度幅值小于低阈值的像素被标记为非边缘像素。梯度幅值介于两者之间的像素被标记为弱边缘像素。
强边缘像素通常是确定的边缘,而弱边缘像素是否属于边缘则需要进一步判断。通常,低阈值设置为高阈值的一半。
代码实现Canny边缘检测:
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
# 传入图片数据和两个阈值
v1 = cv2.Canny(img, 80, 150)
v2 = cv2.Canny(img, 50, 100)
res = np.hstack((v1, v2))
cv_show(res, 'res')
两个阈值越大,边缘的精确度就会提高,检测到的边缘就会减少,一些细节会丢失,也有可能会漏检真实的边缘。