Edge Detection
简单介绍
edge detection (边缘检测) 要检测出图像中的突然变化或者说是不连续情况。这样好处是:
- 从边缘中可以获得图像的语义信息和形状信息;
- 边缘信息比图像像素信息更紧凑;
当然边缘肯定也会丢失相应的纹理信息,但是在某些不关心纹理的任务上比较合适,如果说最近比较火的无人车的车道检测。
图像中边缘产生有几种原因如下:

边缘检测输入是单通道图像(灰度图、RGB每个通道或者HSV的每个通道都可以),输出是和原图大小一致的二值化的边缘检测结果,每个像素点边缘检测结果取值 { 0 , 1 } \{0, 1\} {0,1},分别表示该像素点是不是边缘点。同时检测的边缘宽度需要尽可能小。
有限差分滤波器
边缘是图像中像素强度快速变化的地方,所以为了要评估的是变化值,需要对像素强度求一阶导数,一阶导数极大值和极小值就是要找的边缘,如下图所示:

对于连续函数
f
(
x
,
y
)
f(x, y)
f(x,y) 来说,
x
x
x 的一阶偏导数公式为:
∂
f
(
x
,
y
)
∂
x
=
lim
ε
→
0
f
(
x
+
ε
,
y
)
−
f
(
x
,
y
)
ε
\frac { \partial f ( x , y ) } { \partial x } = \lim _ { \varepsilon \rightarrow 0 } \frac { f ( x + \varepsilon , y ) - f ( x , y ) } { \varepsilon }
∂x∂f(x,y)=ε→0limεf(x+ε,y)−f(x,y)
但对于图像这种离散函数
f
(
x
,
y
)
f(x, y)
f(x,y) 来说,使用有限差分来估计
x
x
x 的一阶偏导数,公式为:
∂
f
(
x
,
y
)
∂
x
≈
f
(
x
+
1
,
y
)
−
f
(
x
,
y
)
1
\frac { \partial f ( x , y ) } { \partial x } \approx \frac { f ( x + 1 , y ) - f ( x , y ) } { 1 }
∂x∂f(x,y)≈1f(x+1,y)−f(x,y)
其中
f
(
x
,
y
)
f(x, y)
f(x,y) 表示该点的像素值,一般是灰度值。
需要在整张图上做相应的操作,可以转化成滤波器和图像做卷积,如下图所示:

x x x 方向的滤波器为 [ − 1 , 1 ] [-1, 1] [−1,1], y y y 方向的滤波器为 [ − 1 , 1 ] T [-1, 1]^T [−1,1]T。
后面为了优化效果和鲁棒性,提出了一些改进版本的有限差分滤波器,也就是常说的边缘检测算子。
Prewitt 算子
M x = [ − 1 0 1 − 1 0 1 − 1 0 1 ] ;    M y = [ 1 1 1 0 0 0 − 1 − 1 − 1 ] M _ { x } = \left [ \begin{array} { c c c } { - 1 } & { 0 } & { 1 } \\ { - 1 } & { 0 } & { 1 } \\ { - 1 } & { 0 } & { 1 } \end{array} \right] ; \; M _ { y } = \left[ \begin{array} { c c c } { 1 } & { 1 } & { 1 } \\ { 0 } & { 0 } & { 0 } \\ { - 1 }& { - 1 } & { - 1 } \end{array} \right] Mx=⎣⎡−1−1−1000111⎦⎤;My=⎣⎡10−110−110−1⎦⎤
Sobel 算子
M x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] ;    M y = [ 1 2 1 0 0 0 − 1 − 2 − 1 ] M _ { x } = \left[ \begin{array} { c c c } { - 1 } & { 0 } & { 1 } \\ { - 2 } & { 0 } & { 2 } \\ { - 1 } & { 0 } & { 1 } \end{array} \right] ; \; M _ { y } = \left[ \begin{array} { c c c } { 1 } & { 2 } & { 1 } \\ { 0 } & { 0 } & { 0 } \\ { - 1 }& { - 2 } & { - 1 } \end{array} \right] Mx=⎣⎡−1−2−1000121⎦⎤;My=⎣⎡10−120−210−1⎦⎤
Roberts 算子
M x = [ 0 1 − 1 0 ] ;    M y = [ 1 0 0 − 1 ] M _ { x } = \left[ \begin{array} { c c } { 0 } & { 1 } \\ { - 1 } & { 0 } \end{array} \right] ; \; M _ { y } = \left[ \begin{array} { c c} { 1 } & { 0 } \\ { 0 } & { - 1 } \end{array} \right] Mx=[0−110];My=[100−1]
方向和强度
图像梯度公式为:
∇
f
=
[
∂
f
∂
x
,
∂
f
∂
y
]
\nabla f = \left[ \frac { \partial f } { \partial x } , \frac { \partial f } { \partial y } \right]
∇f=[∂x∂f,∂y∂f]
那不同的方向梯度如下图所示:

上面红色箭头表示梯度方向,是图像像素变化最快的方向,也就是和边缘垂直的方向,这样边缘方向通过图像梯度也确定了。梯度方向角度为:
θ
=
tan
−
1
(
∂
f
∂
y
/
∂
f
∂
x
)
(
1.1
)
\theta = \tan ^ { - 1 } \left( \frac { \partial f } { \partial y } / \frac { \partial f } { \partial x } \right) \qquad {(1.1)}
θ=tan−1(∂y∂f/∂x∂f)(1.1)
同理边缘强度可以由梯度幅度确定:
∥
∇
f
∥
=
(
∂
f
∂
x
)
2
+
(
∂
f
∂
y
)
2
(
1.2
)
\| \nabla f \| = \sqrt { \left( \frac { \partial f } { \partial x } \right) ^ { 2 } + \left( \frac { \partial f } { \partial y } \right) ^ { 2 } } \qquad {(1.2)}
∥∇f∥=(∂x∂f)2+(∂y∂f)2(1.2)
噪声平滑
图像边缘检测基本流程没问题了,但是在实际使用过程中会受到噪声的影响,导致检测出来很多错误的边缘,如下图所示:

上图只考虑图像的一行或者一列来进行求偏导,从偏导值来看没法确定边缘的位置,或者说每个像素都是边缘。
解决这个问题就是需要把那些微小的像素值的变化也就是噪声平滑掉,所以在求偏导之前先进行图像的平滑,那么效果如下图所示:

这样平滑后偏导的峰值就是要检测的边缘。
由卷积导数定理得到,参考:
d
d
x
(
f
∗
g
)
=
f
∗
d
d
x
g
\frac { d } { d x } ( f * g ) = f * \frac { d } { d x } g
dxd(f∗g)=f∗dxdg
所以操作减少一个,如下图所示:

这样得到的峰值和上面相同,但是实际情况下(Canny)还是使用第一种方案,先对图像进行高斯模糊,在利用算子求图像梯度,确定方向和强度,后面精确定位边缘以及稀疏化都会用到这些值。
经过上面讨论确定高斯模糊是边缘检测必须的一个操作,但是高斯核大小如何确定,直观来看,高斯核尺寸越大,对噪声越不敏感,鲁棒性越强,但是会使图像变得更加模糊,边缘精确位置误差会增大,如下图所示:

经验来说,一般设置高斯模核大小为 3。
边缘检测器
这样整个流程就明确了,给定图像 I I I,根据应用需求选定一个边缘检测算子(有限差分滤波器),得到 x , y x, y x,y 方向上的一阶偏导图,利用公式 ( 1.1 ) (1.1) (1.1) 和 ( 1.2 ) (1.2) (1.2) 得到每个像素点对应的边缘方向和强度值。
但是从上面的结果到最后得到精确到二值化边缘检测结果还需要非极大值抑制、双阈值检测以及抑制某些孤立点等,这个其实就是经典的 Canny 边缘检测算法的主要过程。有篇博客讲的非常好,这里就不再重复了,具体链接:https://www.cnblogs.com/techyan1990/p/7291771.html。