7-26更新:代码在2080ti+0.4.1+cuda10.2环境下Loss依旧无法收敛
7-18更新:代码在torch1.8.0+cu111环境下可以跑通,但是Loss无法收敛,导致model推理的深度不对,可以见章节5.2的图
一、基础解析
- Kitti文件夹的含义
–image0文件(黑白左目)
–image1文件(黑白右目)
–image2文件(彩色左目)
–image3文件(彩色右目)
- [0, -1, 1, ‘s’]代表的含义网络的输入有4张图
0代表读取当前帧;
-1代表前一帧;
+1代表后一帧;
's’代表双目当前帧的另一侧的图片;
每张图对应4个尺度,每个尺度都会数据增强
- 这里的内参K是归一化的,后面使用的时候:
第一行×当前scale的图片width,第二行×当前scale的图片height
也要求广义逆矩阵 K − 1 K^{-1} K−1
最后得到K与inv_K
- 从bin文件中生成真实的GT深度depth_gt
- 双目之间的内部变换假设只有平移stereo_T:
( 1 0 0 ± 0.1 0 1 0 0 0 0 1 0 0 0 0 1 ) \begin{pmatrix} 1 & 0 & 0 & ±0.1 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 &1 \\ \end{pmatrix} ⎝⎜⎜⎛100001000010±0.1001⎠⎟⎟⎞
Notes: 这里的平移不设置为1,而是缩小了10倍,是为了让网络在开始时更容易训练。左目就是-0.1,右目就是0.1
总结:网络的input最终是大小为42的字典,是通过CPU进行运算的。
这里的42 = 4(输入的4张图) × 4(4个尺度) × 2(数据增强) + 4(K矩阵4个尺度) × 2(加上逆矩阵) + 2
二、Depth网络结构(看图)
三、Pose网络结构(看图)
总结:两个网络的outputs最终是大小为10的字典。
这里的10 = 4(Depth网络输出的4个视差尺度) + 3(角轴/平移/变换矩阵) × 2(当前到前一帧,当前到后一帧)
四、Loss计算
4.1 视差转深度图
4个尺度的视差图disp最后都是经过sigmoid的,disp∈(0, 1)。而真实的深度图depth我们想让它限定在(0.1, 100)的范围之内。故设定公式:(对应paper3.3)
d
e
p
t
h
=
1
b
+
a
×
d
i
s
p
=
1
0.01
+
(
10
−
0.01
)
×
d
i
s
p
depth=\frac{1}{b+a×disp}=\frac{1}{0.01+(10-0.01)×disp}
depth=b+a×disp1=0.01+(10−0.01)×disp1
outputs新增当前帧的4个尺度预测的深度图depth(1,192,640)
4.2 深度图转点云(BackprojectDepth类)
两者的详细区别在此
正常顺序是从三维点云投影到像素坐标:
(
u
v
1
)
=
1
Z
(
f
x
0
c
x
0
f
y
c
y
0
0
1
)
(
X
Y
Z
)
=
1
Z
K
(
X
Y
Z
)
\begin{pmatrix} u \\ v\\ 1\\ \end{pmatrix} = \frac{1}{Z} \begin{pmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1\\ \end{pmatrix} \begin{pmatrix} X \\ Y \\ Z \\ \end{pmatrix}= \frac{1}{Z}K\begin{pmatrix} X \\ Y \\ Z \\ \end{pmatrix}
⎝⎛uv1⎠⎞=Z1⎝⎛fx000fy0cxcy1⎠⎞⎝⎛XYZ⎠⎞=Z1K⎝⎛XYZ⎠⎞
转点云就要反过来算:
Z
K
−
1
(
u
v
1
)
=
(
X
Y
Z
)
ZK^{-1} \begin{pmatrix} u \\ v\\ 1\\ \end{pmatrix} = \begin{pmatrix} X \\ Y \\ Z \\ \end{pmatrix}
ZK−1⎝⎛uv1⎠⎞=⎝⎛XYZ⎠⎞
4.3 重投影图片生成(BackprojectDepth类)
outputs新增的一些字典键值对
字典key | 含义 | 大小 | 个数 |
---|---|---|---|
sample | 重投影到当前帧的像素坐标 | (192, 640, 2) | 3×4=12 |
color | 利用F.grid_sample生成的图片 | (3, 192, 640) | 3×4=12 |
color_identity | 记录对应原尺度的输入图片 | (3,192,640) | 3×4=12 |
4.4 Loss计算
代码中的Loss跟paper里面的还是比较一致的,最终的loss公式如下:
L
s
=
μ
L
p
+
λ
L
s
L_s=\mu L_p + \lambda L_s
Ls=μLp+λLs
L
p
L_p
Lp就是光度误差,通过
L
1
L_1
L1损失与SSIM损失计算,与paper一致
L
s
L_s
Ls就是edge-aware smoothness损失,与paper一致
不一样的点在于paper中的公式(5)对应的Automasking Stationary Pixels在代码中对广度误差
L
p
L_p
Lp的改变为:
L
m
i
n
=
μ
p
e
(
I
t
,
I
t
′
→
t
)
+
(
1
−
μ
)
p
e
(
I
t
,
I
t
′
)
L_{min}=\mu\,pe(I_t,I_{t^{'}\rightarrow t}) +(1-\mu) pe(I_t,I_{t^{'}})
Lmin=μpe(It,It′→t)+(1−μ)pe(It,It′)
详细解释在此
代码里精妙的点在trainer.py#L478利用一句话完成了重投影误差的比较、与identity_loss的比较
4.5 整体流程图
五、实验
5.1 时间测试
使用test_simple.py对一张图进行循环:
- 如果仅测试模型的速度 FPS=180左右
features = encoder(input_image)
outputs = depth_encoder(features)
- 数据读取+模型推理+结果resize FPS=75左右
# Load image and preprocess
input_image = pil.open(image_path).convert('RGB')
original_width, original_height = input_image.size
input_image = input_image.resize((feed_width, feed_height), pil.LANCZOS)
input_image = transforms.ToTensor()(input_image).unsqueeze(0)
# PREDICTION
input_image = input_image.to(device)
features = encoder(input_image)
outputs = depth_decoder(features)
disp = outputs[("disp", 0)]
disp_resized = torch.nn.functional.interpolate(
disp, (original_height, original_width), mode="bilinear", align_corners=False)
5.2 精度测试
在自己的KITTI子数据集上(太大了没下完)
abs_rel | sq_rel | rmse | rmse_log | a1 | a2 | a3 | |
---|---|---|---|---|---|---|---|
stereo_640x192(official) | 0.109 | 0.883 | 5.052 | 0.210 | 0.864 | 0.949 | 0.975 |
stereo_1024x320(official) | 0.107 | 0.882 | 4.893 | 0.202 | 0.875 | 0.954 | 0.977 |
my_stereo_640x192 | 0.898 | 14.189 | 19.262 | 2.526 | 0.000 | 0.000 | 0.000 |
举个例子来测试一下