In Defense of Classical Image Processing: Fast Depth Completion on the CPU
论文链接: https://arxiv.org/abs/1802.00036
代码地址: https://github.com/kujason/ip_basic
一、 Problem Statement
如果将目前的激光雷达传感器数据投影到图像上的话,只能获得稀疏的深度图。这样会限制依赖于深度图的算法,比如3D目标检测等等。但深度补全的算法目前大部分都依赖于深度学习,会有以下几个缺点:
- 需要GPU,难以在嵌入式平台使用
- 深度学习没有很好的解释,可能会有次优化的问题
二、 Direction
而本文提出的方法只通过基础的图像处理操作,就能使得稀疏的雷达深度数据变为稠密的深度图,且能在CPU i7-7700K上达到90Hz。
三、 Method
先来看一下深度补全的问题定义:
给定了一个图像
I
∈
R
M
×
N
I \in \R^{M \times N}
I∈RM×N, 和一个稀疏的深度图
D
s
p
a
r
s
e
∈
R
M
×
N
D_{sparse} \in \R^{M \times N}
Dsparse∈RM×N, 找出一个
f
^
\hat{f}
f^,使其逼近于(近似于)真值函数
f
:
R
M
×
N
×
R
M
×
N
→
R
M
×
N
f:\R^{M \times N } \times \R^{M \times N} \rightarrow \R^{M \times N }
f:RM×N×RM×N→RM×N。其中
f
(
I
,
D
s
p
a
r
s
e
)
=
D
d
e
n
s
e
f(I, D_{sparse}) = D_{dense}
f(I,Dsparse)=Ddense。所以问题数学定义如下:
min
∣
∣
f
^
(
I
,
D
s
p
a
r
s
e
−
f
(
I
,
D
s
p
a
r
s
e
)
∣
∣
F
2
=
0
\min||\hat{f}(I, D_{sparse} - f(I,D_{sparse})||^2_{F} = 0
min∣∣f^(I,Dsparse−f(I,Dsparse)∣∣F2=0
用图像表示就是下面这样:
因此,作者就把寻找上面的
f
^
\hat{f}
f^用一系列的图像处理方法表示。整体的方法如下:
利用原始的点云图,通过以下8个步骤,生成稠密的深度图:
- Depth Inversion
- Custom Kernel Dilation
- Small Hole Closure
- Small Hole Fill
- Extension to Top of Frame
- Large Hole Fill
- Median and Gaussian Blur
- Depth Inversion
1. 深度逆变换
主流的稀疏处理机制是使用OPENCV中的morphological transformation operations(形态变换)操作,能够用较大的像素值来取代较小的像素值。比如KITTI数据集上,近距离的像素接近0m,而最远的是80m。而空像素也是0m。如果直接使用OPENCV的操作,会有一个问题,就是在原深度图上使用膨胀操作会导致较远的距离取代了较小的距离,从而丢失较近对象的边缘信息。 为了解决这个问题,作者将有效的像素深度(non-empty),作以下转换:
D
i
n
v
e
r
t
e
d
=
100.0
−
D
i
n
p
u
t
D_{inverted} = 100.0 - D_{input}
Dinverted=100.0−Dinput
这可以在有效像素值和空像素值之间创建20米的缓冲区(也就是最远和空的像素距离)。这种逆变换允许算法在应用膨胀操作时保留更接近的边缘。20米缓冲区用于偏移有效深度,以便在后续操作中屏蔽无效像素。
2. 填充有效像素附近的空像素
第二步就是将最接近有效像素的空像素进行填充,因为这些像素最有可能与有效深度共享接近的深度值。
作者使用了上面的定制膨胀核。
3. 填充小的空洞
通过上面膨胀步骤后,需要空洞仍然会存在,这些是没有深度值的。附近扩展深度的小块可以连接起来形成物体的边缘。使用5×5全核的形态学闭合操作来闭合深度图中的小孔。此操作使用binary kernel,保留对象边缘。
4. 填充中等大小的空洞
前面两个膨胀操作,仍然会有一些中等大小的空洞没有补上。为了解决这些,首先会计算空像素的掩膜,然后使用7x7的全核来进行膨胀操作。此操作只会填充空像素,同时保持先前计算的有效像素不变。
5. 扩充深度图的顶部
为了考虑树、杆子和建筑物等延伸到激光雷达点顶部以上的高大对象,将沿每列的顶部值外推到图像顶部,从而提供更密集的深度贴图输出。
6. 填充大的空洞
这一步是考虑如何填充大的空洞。因为这些区域是没有点的,也不使用图像数据,因此这些像素的深度值是从附近的值推断出来的。使用一个31x31的全核膨胀操作来填补剩下的空像素。
7. 中值和高斯模糊
上面的步骤过后,会得到一个稠密的深度图。然而,深度图中的异常值(outliers) 是这些膨胀操作的副产品。为了移除这些异常值,使用5x5的中值模糊。这个操作可以移除异常值的同时保持局部边界。最后使用5x5的高斯模糊来获得更加平滑的局部平面和目标边界。
8. 深度逆变换
第一步我们使用了深度逆变换,现在要变换回去。
D
o
u
t
p
u
t
=
100.0
−
D
i
n
v
e
r
t
e
d
D_{output} = 100.0 - D_{inverted}
Doutput=100.0−Dinverted
四、效果
和其他方法的一些比较:
自己不同版本的效果:
bilateral blur 版本保留了对象的局部结构,建议用于实际应用。
五、 Conclusion
整篇文章就是用膨胀核进行空洞补全,不同大小的空洞用不同大小的核,最后使用模糊算法进行平滑。也就是作者关键是设计了膨胀操作的Kernel大小和形状。size太大,填充的深度值大于实际其作用区域;太小,膨胀不足以使边缘通过后期的闭运算连接起来。该算法的效果在KITTI depth completion benchmark中排名第一(当时),比用深度学习的方法还好。速度能达到实时。