三维重建-CVPR2021-NeuralRecon: Real-Time Coherent 3D Reconstruction from Monocular Video
论文链接:NeuralRecon: Real-Time Coherent 3D Reconstruction from Monocular Video
代码链接:/zju3dv/NeuralRecon
在了解NeuralRecon之前,需要先了解相机相关的知识,实际上三维重建,最后计算得出的是每个体素的TSDF(Truncated Signed Distance Function)值。
什么是TSDF?
TSDF(Truncated Signed Distance Function,截断的符号距离函数)是一种常用于三维重建的体素表示方法,广泛应用于像 NeuralRecon 这样的三维重建系统。
SDF(Signed Distance Function,符号距离函数): 符号距离函数是一个空间函数,用来表示场景中每个点到最近物体表面的距离,同时包含符号信息:
- 正值:如果空间中的某个点位于物体表面之外,SDF 的值为正,表示点离表面的距离。
- 负值:如果某个点在物体表面内部,SDF 的值为负。
- 零值:如果某个点正好在物体的表面上,则 SDF 的值为 0。
SDF 是一个连续函数,表示物体表面的所有点距离最近表面的距离,并且是带符号的,能区分点在物体内部还是外部。
TSDF(Truncated Signed Distance Function,截断符号距离函数): 由于直接使用 SDF 来表示整个三维空间可能会产生大量不必要的数据(因为大多数点离物体表面较远,距离较大),因此在实际应用中我们会对距离进行截断,得到 TSDF。
- TSDF 会将那些离物体表面较远的点的 SDF 值进行截断,超过某个距离阈值的点直接设为常量。
- 这样做的好处是减少了远离物体表面区域的不必要计算,从而提高计算效率。
如何计算TSDF值?
在计算 TSDF 时,首先通过模型预测获得图片中的深度
ds
(从图片中获取的深度信息),然后根据相机的位姿和内参计算出体素的深度dv
(通过相机拍摄的位姿以及相机的内参计算得来),接着计算体素到真实面的距离d(x) = ds - dv
。若d(x) > 0
,则体素位于真实面前;若d(x) < 0
,则体素位于真实面后。最终,通过进一步处理d(x)
得到 TSDF 值。
NeuralRecon
NeuralRecon 是一种基于深度学习的三维重建方法,主要用于从单目视频或多视图图像生成高质量的三维场景。NeuralRecon利用TSDF的计算方法,将三维空间中的体素投影映射到二维图像中,找到体素需要提取图片中的具体位置,采用TSDF方式提取特征,构成所有的体素。
NeuralRecon模型架构图
主要步骤:
- 输入:彩色图像和相机位姿信息作为输入,提供三维场景的基本信息。
- 初始低分辨率重建:从低分辨率的体素网格开始,通过图像的多视角信息推断初步体素特征。
- ConvGRU 更新特征:使用 ConvGRU 递归更新每层体素特征,保留前一层的有用信息。
- MLP 估计 TSDF:多层感知机(MLP)用于估计每个体素的 TSDF 值,表示体素与表面的距离。
- 逐层细化:每一层增加体素分辨率,并通过上采样和特征融合逐层细化体素特征。
- 输出:生成高分辨率的 TSDF,表示最终的 3D 场景结构。
Fragment Posed Images(分段的带位姿的图像输入)
NeuralRecon 的输入是视频中的图像序列以及相机的位姿信息。具体的输入包括:
彩色图像:单目视频中的每一帧图像。
相机位姿:包括相机的内参和外参(位姿信息),这些信息帮助网络确定每个图像在三维空间中的位置。
这些图像会被分成多个 batch,每个 batch 表示一个 fragment(片段)。在每个片段中,网络从多视角的图像推断深度图并估计局部 TSDF(Truncated Signed Distance Function)。然后,通过将多个局部 TSDF 融合,逐步构建出完整的 3D 场景。
Coarse-To-Fine Reconstruction(从粗到细的三维重建)
NeuralRecon 使用了 coarse-to-fine(粗到细)的三维重建策略,逐步提升 TSDF 的分辨率,生成更细致的 3D 场景。其流程如下:
(1) 初始粗分辨率体素网格
首先,网络从低分辨率的体素网格开始重建。在这一层,每个体素的特征会通过图像序列中的多视角图像推断出来。多张图像的特征向量会被相加求平均,以此确定该体素的初步特征向量。
(2) ConvGRU 更新机制
在每一层的 TSDF 估计中,NeuralRecon 使用了 ConvGRU 机制来递归更新体素特征。ConvGRU 的作用是将前一层的体素特征与当前层的信息进行融合,保留有用的信息,并逐层改进。这一机制确保了不同层次之间的特征一致性和逐步细化的过程。
(3) MLP 和 TSDF 估计
特征通过 3D CNN 处理后,进入多层感知机(MLP)模块来估计 TSDF 值。在这一步,网络推断出每个体素的 TSDF 值,表示体素距离真实表面的距离。
(4) 逐层增加分辨率
第一层完成后,接下来的层次中,网络会逐渐增加体素的分辨率。这意味着体素的数量会增加,从而提取出更多的细节。在每一层,ConvGRU 机制会确保上一层的特征信息在新的分辨率下被细化并保留。为了对齐不同分辨率的体素网格,上一层生成的 TSDF 会通过上采样操作。
def forward(self, features, inputs, outputs):
'''
:param features: list: 每个图像的特征列表,例如 list[0] : 图像0的金字塔特征 : [(B, C0, H, W), (B, C1, H/2, W/2), (B, C2, H/2, W/2)]
:param inputs: 来自数据加载器的元数据
:param outputs: {} 空字典,用于存储输出
:return: outputs: dict: {
'coords': (Tensor), 体素的坐标 (number of voxels, 4) (4 : batch 索引, x, y, z)
'tsdf': (Tensor), 体素的 TSDF 值 (number of voxels, 1)
}
:return: loss_dict: dict: {
'tsdf_occ_loss_X': (Tensor), 多层次损失
}
'''
bs = features[0][0].shape[0] # 批次大小
pre_feat = None # 前一阶段的特征初始化为 None
pre_coords = None # 前一阶段的坐标初始化为 None
loss_dict = {
} # 损失字典初始化为空
# ----从粗到细的过程----
for i in range(self.cfg.N_LAYER): # 遍历网络的每一层
interval = 2 ** (self.n_scales - i) # 当前层体素网格的间隔
scale = self.n_scales - i # 当前尺度
if i == 0:
# ----生成新的坐标----
coords = generate_grid(self.cfg.N_VOX, interval)[0] # 生成初始的网格坐标
up_coords = []
for b in range(bs):
up_coords.append(torch.cat([torch.ones(1, coords.shape[-1]).to(coords.device) * b, coords]))
up_coords = torch.cat(up_coords, dim=1).permute(1, 0).contiguous() # 将批次索引加入坐标
else:
# ----上采样坐标----
up_feat, up_coords = self.upsample(pre_feat, pre_coords, interval) # 上采样前一层的特征和坐标
# ----反向投影(将坐标投影到3D空间)----
feats = torch.stack(