论文详解——GeoNet:Unsupervised Learning of Dense Depth, Optical Flow and Camera Pose
前言 :
商汤科技在CVPR2018上发表了一篇名为《GeoNet:无监督深度、光流及相机姿态联合学习框架》的论文,并开发了一种能够实现深度、光流及相机姿态联合学习的无监督框架GeoNet。该研究成果不仅超过了现有无监督方法的表现,并达到了与最佳有监督方法相当的效果。
相关工作:
理解视频中的三维场景几何信息是视觉感知领域中的一项核心任务,在这一领域包含了众多的经典计算机视觉问题,如深度恢复、流估计以及Visual Odometry(Visual Odometry)。这些技术广泛应用于多个工业领域中包括自动驾驶系统、交互式协作机器人以及定位与导航系统等
基于 运动恢复结构 (SfM:Structure from Motion)的传统方法通常采用集成策略来处理相关任务。其主要目标是同时重建场景的三维结构及其相机运动轨迹。然而,该方法本质上严重依赖于高质量的低层次特征匹配技术,在处理异常数据点以及无纹理区域时表现出较差的效果。
为了克服这一限制,在研究中将深度学习模型应用于每一个低层子问题,并带来了部分效果。这种技术的优势源自于大量数据的支持,在此过程中能够实现从低层到高层的意义。(具体而言)这种技术能够帮助我们从基础层次逐步理解并提取出更高层次的信息与关系。相较于传统方法,在ill-posed区域依然展现出较好的性能表现。然而,在实际应用中往往面临较高的挑战:例如需要大量的标注数据支持有监督学习(也就是必须要有高质量标注的数据),而这通常会使用昂贵且精确度较高的激光雷达以及差分GPS设备来进行采集与处理;此外这些设备所带来的成本非常高昂。此外传统的深度学习模型大多专注于单一特定任务(如深度估计、光流计算以及相机姿态估计等),并未充分考虑不同任务之间的几何约束与内在联系。
关键点:
该文所提出的无监督学习框架GeoNet基于视频数据联合学习单目深度估计、光流场预测以及相机运动估计。其理论支撑来自于** 3D场景几何的本质特性和内在规律** 。具体而言,这意味着** 3D场景都是由静态背景和动态目标共同构成的** 。在许多自然场景中,通常是由** 刚性静态表面** 组成的(如道路、房屋、树木等),这些表面在连续帧之间的2D投影图像完全由深度结构以及相机运动所决定。与此同时,在这些静态背景下还包含着动态的对象存在(如行人、车辆等),它们的动力学行为是由相机运动以及自身的运动共同决定的。通过光流场的变化来模拟由于相机运动引起的图像变化是一种有效的实现方式。
主要贡献有两点:
采用了分而治之策略,在分别研究刚性流体的运动学特性以及物体的运动学特性时。在每个阶段用视图合成(view synthesis)被用来作为引导与监督学习的关键指标。
该文引入了自适应几何一致性损失项,并基于正反向一致性验证过程实现对遮挡和异常值的自动过滤。
网络结构:
其网络架构由两部分构成:一个是刚性模块重构器(RBR),另一个是非刚性模块定位器(NBR)。前者负责提取物体的刚性和运动信息;后者则用于获取物体运动信息并完成位姿估计。在无监督框架下完成整个流程,并通过分析图像外观特征来进行约束。
具体的设计方案及其对应的性能指标,在线测试系统提供了详细的实验结果,并将完整的代码实现过程附在了GitHub仓库中(代码仓库链接:https://github.com/ai-root/DPT-Research)

Stage 1 —— Rigid Structure Reconstructor
在第一部分Rigid Structure Reconstructor中包含两个关键组件:DepthNet用于预测深度图 PoseNet负责估计相机姿态 然后将它们结合起来生成刚性流
DepthNet采用了编码器+解码器的结构设计,在编码器部分基于ResNet50构建了残差块结构,在解码器部分通过反卷积层实现了特征重建,并在多尺度特征之间建立了直接连接通道以促进跨尺度信息传递。该网络架构能够有效融合全局上下文信息与局部细节特征。实验数据集基于已知相机内参捕捉的时间连续视频帧序列中,I(t)表示目标参考帧,I(s)则代表其他源帧。该网络采用深度估计方法生成不同分辨率下的深度图(原尺寸、1/2、1/4、1/8倍)—— D(t)
PoseNet由8个卷积层构成,在输出层之前设置了全局平均池化层。这些卷积层之间采用了批归一化和ReLUs激活函数,并且详细提取了来自不同分辨率的相机位置与朝向信息。值得注意的是,在公式中T(t-s)表示从目标帧到源帧的变换矩阵(4×4),这一变换矩阵可通过将6DoF转换得到!
**** 有了深度和相机姿态,则可以计算出刚性流:

由 ** _通过目标帧到源帧的刚性流...** (具体解析详见《Unsupervised Learning of Depth and Ego-Motion from Video》,下图出自该论文。)
**_

_**
基于刚性变换的部分形变损失由视觉相似度表征, 即 目标帧的原图I(t)和通过刚性流与源帧 warp 得到的合成视图 ~ **I(rig;s)_ 之间的视觉相似度误差.
**_

_**
除此之外,还引入了Disparity Smoothness Loss,

具体的计算过程包括:根据论文中的公式(3)进行推导。

Stage 2 —— Non-rigid Motion Localizer
第二部分Non-rigid Motion Localizer... 用于跟踪动态目标;传统的光流方法能够直接模拟无约束运动,在现有深度学习模型中被广泛采用;通过ResFlowNet来学习残留的部分非刚性位移;其中物体相对于世界平面的位置变化导致了这些位移;将这些非刚性位移信息与原有的刚性变换结果相结合;在第一阶段之后按照级联的方式连接到这一模块中去;针对任意两帧图像来说... 预测对应的剩余变形场_(res)f(t-s)_ ,通过这种计算方式得到的结果就是最终的整体变形场_(full)f(t-s)=(rig)f(t-s)+(res)f(t-s)_ 。
通过适当修改将第一阶段的监督范围适当扩展至当前阶段,在预测流 (full)f(t-s) 完成后对任意一对目标帧与源帧之间进行图像变形处理,并使用 (full)I~ 替代 (rig)I~ ,从而获得 full flow 的 warping loss 。类似地,在2D光流场中也实现了平滑损失的扩展。
GeoMetric Consistency Enforcement
GeoNet的每个阶段都基于合成视图与原图之间的差异量作为监督方法。这种方法假设了光度一致性的存在。然而,在实际情况下,遮挡区域以及非Lambertian表面并不满足这一假设条件。为了解决这一问题,在不改变网络结构的前提下采用了一种前向-后向一致性检验的方法。然而这种约束以及warping loss不应应用于遮挡区域

其中, (delta)f 是目标帧 I(t) 在像素 p(t) 处前后一致性检验计算得到的 full flow 的微分。 [ ] 是 Iverson bracket,[P] 等于1(如果条件P为真,否则等于0)。 delta(pt ) 表示条件:

前向与后向流动中的差异较大的像素点被视作潜在的离群点。这些区域违背了 photo consistency 和 geometric consistency 的假设,并且主要依赖于平滑损失函数来进行处理。 full flow 的 warp loss 和 geometric consistency 都是各自依据像素权重进行计算的结果。
最终,整个网络的损失函数为:

训练与测试细节:
**github地址:点击打开链接
**
- Data Preparation
基于深度和流任务的研究中使用了KITTI原始数据集这一重要数据源,在位姿估计领域所应用的数据集源自KITTI位姿估计数据集
准备完成后,在进行预处理步骤时需确保明确区分训练数据集与验证数据集
## Depth Task
## dataset:KITTI raw
python data/prepare_train_data.py --dataset_dir=../KITTI/kitti_raw/ --dataset_name=kitti_raw_eigen --dump_root=data_preprocessing/dump_data_depth/ --seq_length=3 --img_height=128 --img_width=416 --num_threads=16 --remove_static
## Pose Task
## dataset:KITTI odometry
python data/prepare_train_data.py --dataset_dir=../KITTI/kitti_odometry/dataset/ --dataset_name=kitti_odom --dump_root=data_preprocessing//dump_data_pose/ --seq_length=5 --img_height=128 --img_width=416 --num_threads=16
## Flow Task
## dataset:KITTI raw
python data/prepare_train_data.py --dataset_dir=../KITTI/kitti_raw/ --dataset_name=kitti_raw_stereo --dump_root=data_preprocessing/dump_data_flow/ --seq_length=3 --img_height=128 --img_width=416 --num_threads=16
dump_data_depth文件夹中的文件结构如下图:



在train.txt和val.txt文件中分别存储了训练数据集与验证数据集的图片路径及其相关信息。每个文件夹内的内容如图所示,在*.txt文件中的*_cam字段包含一个长度为9的一维向量,用于表示相机内参数.而每张训练图像则由原始数据集中连续的三帧图像拼接而成,并被 fed 到网络进行处理
dump_data_pose的文件结构如下图所示:



其中train.txt和val.txt分别存储了训练集和验证集图片的序号(范围:00-08)及其文件名信息。同样地,在每个文件夹中(对应于序列号为'xx'的情况),都有一个长度为9的一维向量被记录下来——这是相机内参参数的数据表示。每张图片都是从原始数据集中提取连续5帧图像拼接而成,并输入给网络模型进行处理。以'xx'序列为例,在原始数据集中该序列共有'xxxx'-xxxx帧图像数据(具体起始与结束帧数可能因场景而异)。因此得到的结果数量为(最大值 - 最小值 + 1)= xx张图像。(但是实际处理后得到的是'_cam.txt'中的数据?具体情况有待进一步确认,请参考代码文档)
dump_data_flow的文件结构如下图所示:



train.txt和val.txt分别存储了训练数据集与验证数据集中的图像路径及其相关信息。具体结构如图所示,在*_cam.txt文件中,存储了一个长度为9的一维向量,该向量记录了相机内参参数。每张图片都是从原始数据集中选取连续三帧图像进行拼接处理得到的。深度估计(depth)任务使用的数据集是KITTI raw中的eigen子集,而流估计(flow)任务则采用了KITTI raw中的stereo子集作为训练数据。
- Training
mode 设置为train_rigid时,是用来训练DepthNet和PoseNet。
## Train DepthNet
python geonet_main.py --mode=train_rigid --dataset_dir=data_preprocessing/dump_data_depth/ --checkpoint_dir=checkpoint/checkpoint_depth/ --learning_rate=0.0002 --seq_length=3 --batch_size=4 --max_steps=350000
## Train PoseNet
python geonet_main.py --mode=train_rigid --dataset_dir=data_preprocessing/dump_data_pose/ --checkpoint_dir=checkpoint/checkpoint_pose/ --learning_rate=0.0002 --seq_length=5 --batch_size=4 --max_steps=350000
mode 设置为train_flow时,是用来训练DirFlowNet或者ResFlowNet。
## Train ResFlowNet
## 训练ResFlowNet时,--init_ckpt_file需要指向train_rigid模式下有相同训练数据的预训练的模型,即model-depth
python geonet_main.py --mode=train_flow --dataset_dir=../data_preprocessing/dump_data_depth/ --init_ckpt_file=../models_self/depth/model-345000 --checkpoint_dir=models/flow_residual/ --learning_rate=0.0002 --seq_length=3 --flownet_type=residual --max_steps=100001
## Train DirFlowNet
python geonet_main.py --mode=train_flow --dataset_dir=../data_preprocessing/dump_data_flow/ --checkpoint_dir=models_self/flow_direct/ --learning_rate=0.0002 --seq_length=3 --flownet_type=direct --max_steps=400001
