旷视CV Master基于神经网络的3D重建训练营课程笔记1

May 14,2022   8070 words   29 min


本篇博客是旷视CV Master训练营系列《基于神经网络的3D重建》专题的第一课《NeRF及Plenoxel理论入门》的相关笔记,课程视频可以点此查看

0.课程前言

1.第一课课程大纲与概述

1.1 计算机图形学与计算机视觉

可以看到,计算机视觉的输入是一张图片,我们通过各种各样的方法(比如目前流行的神经网络方法)来了解或者认识这张图片(比如从图片中感知场景的几何信息等)。而计算机图形学的目标是,给定一个已知的场景,通过渲染获得2D图片。

比如,我们有一堆已知相机位姿的图片,那么我们就可以基于这些信息将三维场景重建出来(比如使用经典的sfm方法),也就是下图中的箭头表示的过程。 建好模型的模型如下图所示。 计算机图形学则是有了3D模型或者场景,给定一个相机位姿,基于此位姿渲染出一个2D影像。

1.2 可微渲染(Differentiable Rendering)

上面我们说了由图片到场景是计算机视觉做的事情,由场景到图片是计算机图形学做的事情。其中由场景到图片这个过程被称为渲染。如果这个渲染的过程是可微的会怎么样?答案是我们就可以将从图像到模型和从模型到图像这两个过程关联起来,如下图所示。 首先,我们有一堆已知位姿的图片,我们将其作为真值。然后我们也有一个初始的场景表示(可能是非常糟糕的,但没关系),基于这个场景表示,再结合位姿,就可以渲染出每个位姿对应的二维图片。显然,这个图片和真值相比是非常糟糕的,但没关系。我们计算估计的影像和真值之间的差异,将其作为Loss。然后关键一步来了,利用这个Loss去反向传播、更新我们的场景表示。更新之后,我们再让它渲染出每个位姿对应的影像。这一次相比于第一次渲染,结果肯定好了一些。但还是不够,我们继续计算和真值的差异,然后再反向传播。不断重复上述过程。最终我们可以达到一个效果,那就是场景表示渲染出来的图片和真值几乎没有差异了。从这个角度也就可以认为这个三维场景,已经被我们的场景表示所完全“掌握”了。给定任意一个位姿,场景表示就可以给出一张该位姿所对应的2D影像。 上面我们说了,反向传播这个过程是“关键一步”,而在这一步中,最核心的就是“可微渲染”。如果这个渲染过程不可微,那么我们就没办法基于计算的Loss来更新场景表示,后续的步骤也就无从谈起了。

1.3 有了场景表示后可以做什么

到此为止,假设我们已经训练得到了一个场景表示。我们可以用来干什么呢?首先,一个最直接的想法就是合成新视角上的影像。比如我们利用左、右影像训练了一个很棒的场景表示。我们就可以基于这个场景表示合成新视角下的影像,比如上、下、前、后,如下所示。 除了合成新视角下的影像,另一个有用的地方在于,我们可以直接导出场景的三维模型,如下所示。 当然,我们也可以将得到的场景表示参与到其它的计算机视觉任务中去,如下所示。 比如说我有多个视角的图片,要做目标检测,如果可以先构建出三维场景来,在此基础上再做目标检测,肯定比单张做要效果好。当然,还有iNeRF这种工作。它的目的是,我现在已经有了一个三维场景,又有一个新的图片进来。通过iNeRF,我就可以知道这个图片在哪个位姿拍摄的。

1.4 体积渲染(Volume Rendering)

体积渲染的核心思想是,对于空间中的每一个点,我们都认为它有一定的密度,如上图所示。如何理解“密度”这个概念呢?和不透明度又有什么关系?看下面的图。 比如说,我们有一个透光率为0.6的滤镜(简单理解就是如果入射光线是1,那么经过该滤镜射出时就只有0.6了)。如果这个滤镜的厚度为h,那么我们将透光率除以厚度,得到的就是这个滤镜的密度σ。 所以,如果有某束光线,其光路上的密度如上图所示。根据上面的公式,我们就可以积分得到透过率。可以看到,光线从相机出发,一直在被吸收,透过率不断减少。

有了上面提到的积分模型有什么用呢?答案是我们就可以用来做渲染。首先,我们可以简化物理模型。在现实世界中,大部分的物体是反射其它光源的,但这里我们假设每个物体自己都发光。基于这个假设,可以想象,对于现实世界中的物体,它不但有密度,还有颜色。所以我们最终渲染图像上一个像素的像素值就应该是个积分,如下图所示。 可以看到,一个像素的总的颜色,它就等于光路上各个点的积分值,每一个积分项表示当前积分还剩的光线乘以该点自己的密度和颜色。当然了,这是一个简化的模型。不符合现实世界的逻辑。在现实世界中,我们观测到的一个点的颜色除了由它自身的固有属性决定之外,还与观测的角度有关,进一步说与光影和反射有关。一个简单的例子比如,我从正面看手机屏幕和从侧面看手机屏幕看到的颜色是不同的,尽管物理上屏幕显示的是一样的内容。因此,我们要对上面的模型进行修正,加入观测角度,如下所示。 当然,还有最后一个问题,上面的模型是连续积分的,不好计算,为了方便计算我们需要进行离散化。简单来说就是沿光路以一定间隔采样,然后求和,如下所示。 在下面的公式中,σ还是表示密度,δ则表示采样间隔,它们俩相乘就表示某一小段光路的透光率。这样我们就可以得到最终的颜色。不仅如此,我们还可以求得最终颜色与各个采样点的颜色ci的偏导。换句话说就是,这个渲染过程是可微的。这样的好处就是,当求出某个Loss之后,就可以求微分反向更新颜色和密度。

2.Neural Radiance Field

如上图所示,NeRF的输入是一系列带位姿的影像,通过前面得到的可微渲染方程可以训练网络。这个网络就可以当作是场景的表达。那么给定一个新的位姿,我们就可以通过这个网络推理出这个视角下的影像。

2.1 Coordinate MLP(2D)

继续往下之前,先来看这样一个例子。我们能不能用神经网络把图像“背”下来。我们的输入是影像上的x、y坐标,输出则是该位置对应的RGB颜色。如果网络能够学到这种位置和颜色之间的映射关系,那么我们就可以拿着这个网络去恢复图像。也就可以说,这个图像被网络“记下来”了。但事实上,直接这样做,最多只能训练到上图中模糊影像的样子。这样的原因是,一般的没有经过特别设计的神经网络,只能学到一些比较低频的信息,所以恢复出的图像看起来是模糊的,没有细节。

2.2 Positional Encoding

为了解决这个问题,我们不再直接学习x、y位置和RGB颜色之间的映射关系,而是先对位置进行编码,将其转换为一系列分量,构成一个向量,让网络去学它和颜色之间的关系。这在一定程度上体现了低频和高频的信息,如上图中的图像所示。通过这样的方式进行训练,网络就能较好的完成“背”图像的任务,如下图所示。 在图中,上面的图就是加了位置编码以后的网络恢复的结果,可以看到已经非常好了,网络学到了低频和高频的信息。作为对比,下面的图是直接训练的结果。

2.3 3D Coordiante MLP

然后,我们再思考三维的问题。根据之前的描述,我们想要寻找的是位姿和图像表达之间的映射,如上图所示。x、y、z表示位置,而θ和Φ表示入射光线的角度(可以理解为从球心到球面的任意一点,采用类似经纬度的方式表达)。r、g、b表示颜色,σ表示某一点的密度。当然这里还有个小问题,如下图所示。 一般而言,某个物体的密度是其固有属性,它不应该受到看它角度的影响,换句话说,从不同角度看同一个物体,它的密度应该是不变的。所以说,它不应该和观测角度θ和Φ相关联。

2.4 The Model

基于以上的分析,我们最终构思的网络结构应该如下图所示。 可以看到,对于位置和姿态我们单独处理。对于姿态我们单独编码,然后让他只对r、g、b有影响。对于位置,还是先做位置编码,然后有两个分支,分别给密度σ和r、g、b。这样姿态是影响不到密度的,但是位置可以同时影响密度和颜色。

2.5 Coarse to Fine

此外,还有一个问题,前面我们提到了,在渲染的时候,通过一定间隔采样来近似连续的积分过程。对于一条光线而言,我们当然可以等间隔采样,如上图所示。但事实上,可能这个光线上的大部分采样点对于渲染都是没用的,真正有用的可能只集中在某一个区域,这样比较浪费。而如果我们只学习某一个小区域,那么其它区域的信息可能又学不到(可以理解为其它区域只是在这个位姿下没用,在其它位姿下可能就有用)。为了解决这个问题,我们可以训练两个网络,coarse网络和fine网络。在coarse网络中,我们采用等间隔采样的方式进行学习。当然,这里也不是完全的等间隔。如果完全等间隔,那么网络只会见到那些距离相机采样间隔整数倍的点。这样就可能带来一个结果,网络对于采样间隔整数倍上的点学习效果很好,但对于除此之外的其它点效果就很差。所以针对这个问题,在实际训练中可以把一个光线分成若干段,每段随机采样一个起点,然后以它为起点再等间隔划分,可以一定程度上避免这个问题。在fine网络中,我们可以先根据coarse网络预测得到一个可能的透光率曲线,然后我们采样有变化的区域,以此进行训练。当然,前面也说了,如果只训练这些点也会有问题。所以解决办法是把根据透光率曲线采样的点和前面粗网络等间隔采样的点放在一起进行训练。这样既可以保证对于变化区域的学习,又可以保证对于其它区域的关注。

2.6 Examples

使用上面提到的这些tricks之后,就可以训练得到相当好的效果,如下图所示。

3.Plenoxels

Plenoxels相比于NeRF可以有更快的训练、推理效率。并且他们认为可以不用神经网络表达空间中点的颜色和密度。

3.1 Voxels

如上图所示,我们可以用体素来表达三维模型,每个体素包含r、g、b颜色和密度。当然根据前面分析得出的结论,我们同样希望,rgb值是会受到入射角影响的。在NeRF中,我们通过对姿态进行编码然后输给神经网络,让它来拟合这种关联关系。 但是在体素中如何表达这种变化呢?这就要引入一个新的概念——球面谐波系数。 这里我们认为,一个定义在求表面的连续函数,可以用上图所示的正交基的线性组合来表示。从上往下表示拟合的阶数。在Plenoxel文章中,用到的是金字塔的前三层,也就是9项,如下图所示。 每个颜色通道都用9项来拟合,三个通道一共是27项。所以对于每个体素,我们不再存RGB的信息,而是存这27个分量的信息。这样的好处就是我们可以根据这27个值来插值出任意位置的颜色。

3.2 Trilinear Interpolation of Color

使用体素来表示三维空间还有个问题,那就是整个空间被划分成规则的格网,类似于我的世界中的像素画风。要想获得每个位置的颜色,就需要进行插值。如上图所示,我们利用三线性插值(使用某点周围8个节点的数值)。最终,对于某个点,我们就可以得到,从任意角度看的颜色。

3.3 Trilinear Interpolation of Density

上面我们利用三线性插值对球面谐波系数进行了插值进而获得了某一点从某个角度观察的颜色。我们还要获得密度这个量。在Plenoxel工作中,并没有限制体素中的密度一定要为正。这样的结果就是可能会有负数密度,但显然是没有意义的,需要过滤掉——对于负密度都设为0。如上图所示,对于某个体素,通过插值可以得到一个相对复杂的非零和零密度的边界。这是一个比较微妙的地方。因为如果只是一块块的体素,最后得到的可能会有方块状的效果。但因为有这种插值曲面的存在,会导致实际效果可能要好得多。

3.4 Voxel Pruning

另外,在实际渲染的时候,大部分的体素可能都是没什么贡献的。有两种情况。一种是没有密度的体素,另一种是物体很内部的体素。第二种如何理解呢?我们从外部看一个物体是看不到它的内部的,尽管它的内部也是有密度的。所以这种体素尽管它有密度,但对最终渲染的贡献也不大。

3.5 Total Variation

最后还需要注意的一点是,对于Plenoxel,由于每个体素之间是没有什么关联的,那么在网络训练的早期阶段有可能学到“奇奇怪怪”的东西。比如上面的这只猫,网络可能学到的并不是这只猫,而是一团团离散的体素,只是恰好从某个角度观察,这些离散的体素刚好构成了一只猫。 这样说可能不容易理解。但我们可以举一个更直观的例子。如果你玩过《原神》,在今年海灯节的时候,有一个叫做“灯中妙影”的活动。在这个活动中,我们需要不断转动一堆零散的零部件,最终拼成一个图形,如下图所示。 对于我们的网络也是一样的,如果不加约束,它一开始可能学到的就是这样一堆“零散”的零部件,只是恰好在这个角度观察是某个物体。为了避免这种情况的出现,在Plenoxel中增加了相关约束。 具体而言,如果是一个有意义的实体,那么一般而言,相邻的体素会比较接近,而如果是零散的体素团,相邻的体素就不那么接近。所以,我们可以对于每个体素,我们都计算其和周围体素之间的“距离”。然后把所有的距离加起来,以此为Loss,希望这个值最小。这里的距离是打引号的,具体怎么算呢?就是上面图的公式,说白了就是计算相邻体素球面谐波系数和密度之间的差异,我们希望这种差异越小越好。

当然,你可能会发现,这种策略主要是用于网络训练的初始化阶段的,避免网络“跑偏”。在后期训练阶段就不太需要这种策略了。

3.6 Coarse to Fine

一个常见的网络训练流程如上图所示。首先我们会训练一个粗模型(通常是较低分辨率的),为了避免网络训坏掉,我们会加入一个正则项作为约束。然后我们会根据训练的粗模型进行剪枝。然后通过插值获得高分辨率的模型。最后,用高分辨率模型做精华,得到最终结果。

4.关于NeRF的一些误区与思考

NeRF的核心是场景的隐式表示,给定一组影像,网络去学习这个三维场景,然后当指定任意一个位姿的时候,就输出它对应的图片。这其实和传统的SfM的流程是一样的,没什么新鲜的。唯一不同的是SfM构建的是场景的显式表达,而NeRF是通过神经网络进行了表达。现在很多NeRF的宣传都是一张静态图片搭配小幅度移动视角的动图,比如下面这样。 但这样宣传有些误导性。下面简单描述。

4.1 NeRF并非只利用单张影像

第一点,NeRF不是由单张影像得到的。上面这样宣传,很容易让人以为是单张图片得到了可以小幅移动视角的三维场景。这是不对的。如果从单张影像生成三维场景,这就是一个玄学问题了。NeRF并不是只利用单张影像估计三维场景。

我的个人看法是利用单张影像永远也无法恢复出准确的三维场景。这是一个病态问题。就如同单张影像深度估计一样,很多时候都是玄学问题。对于单张影像深度估计,我认为这个问题的核心在于两点:一是这是一个“无中生有”的信息缺失补全问题。如果说可以利用其它模态的数据作为参考,也许是有物理意义的,但仅靠单张图像是站不住脚的。二是深度本身和图像中的纹理、语义等没有明显的关联关系。对于这种没有明显对应关系的两个变量,深度学习能力再强,也很难学到真正有价值的东西。一个简单的例子就是,比如我有一面白墙,拍了一张照片,然后我在墙上挂了一幅山水画,又拍了一张,最后,我关灯了,墙变黑了,我又拍了一张。一共有三张照片。从常识都知道,这三张照片的深度不会有变化(如果考虑画的厚度,反映在深度图上会是一个规则的突出矩形)。但事实上,目前应该鲜有网络能真正准确估计——如果它学到的是和图片位置相关的知识,那么白墙从上到下的深度就不同;如果它学到的是灰度相关的知识,那么它估计的白墙和黑墙的深度就不同;如果它学到的是纹理的知识,那么在山水画上,它就会估计出不同的深度(但显然它只是一幅画而已,我们作为人是不会把它真的有深度的)。

当然你可能会反驳我,那现在有些单张影像深度估计做的很好的工作啊?难道没有意义吗?我的回答是,在某些特定场景下可能有意义。比如某个深度估计网络,它利用KITTI数据进行训练。由于KITTI数据都是采用同一套传感器在相似场景下拍摄的,它拍摄的所有影像都有相似的内容和深度分布,比如靠近图像下方的都是路面,图像上方中间的一般为天空等。深度网络确实可能学到这些与位置有关的信息(天空纹理较少、路面纹理较多、天空比路面深度更深等),进而给出深度估计。那么当输入一个和KITTI类似的影像的时候,它也可以估计出一个还不错的深度图。但是如果换了一个配置,比如输入EuRoC的数据,尽管它本身的内容、深度分布和KITTI相差很大,但网络还是按照KITTI的那一套估计深度,结果就显而易见了,所以泛化性较差。因此,在我看来这种数据驱动的方法还是没能从根本上解决这个问题,难以给出令人信服的解释。场景的几何结构是其固有属性,与外界的观测无关,不管你怎么观测,它都不会改变。但深度估计的网络恰恰就是依靠这种不可靠的“观测”学到的。利用缺失的信息是没有办法真正恢复三维场景的,至少说很困难。

4.2 NeRF并非只能合成特定视角影像

第二点,NeRF是三维场景的隐式表达,也就是说其包含了完整的三维场景。基于训练好的NeRF,我们有能力合成该场景任意视角的影像,比如360旋转的视角,而非仅仅只是可以小幅度移动视角的模型。所以,个人觉得最正确的NeRF表现形式应该是一堆图片+360旋转视角的动图。

4.3 没有图片位姿可以训练NeRF吗

对于一堆原本没有位姿的图片,可以训练NeRF吗?答案是否定的。NeRF的输入必须是影像+位姿。但是,我们可以“曲线救国”——我们可以先用COLMAP等传统的SfM软件对场景进行重建,重建好以后,当然就可以获得每张图片的位姿。事实上,不一定需要COLMAP,只要能输出影像间的相对位姿都可以,所以对于序列影像我们甚至可以拿SLAM来获得位姿。我们拿着这些位姿,配上图像,就可以输入NeRF进行训练了。

4.4 NeRF不能无中生有

NeRF还有一个值得注意的地方,就是NeRF作为某个特定场景的隐式表达,我们就可以把它理解为是一个三维模型。所以说,它只能合成出这个场景的不同视角影像,没有办法合成出新的场景的影像。另外,如果拿一张其它场景的影像,它也没有办法去匹配找到正确的位姿(因为就完全不在这个三维模型里)。

4.5 NeRF的应用场景

除了上面提到的合成新视角影像、辅助计算机视觉任务等,从某种程度上来说,NeRF可以用于逆向工程。对于一个没有精确三维模型的零部件,我们拍一堆图像(已知位姿),然后训练一个场景表示,最终把这个场景表示导出成三维模型。当然你可能会问,如果说你都拍了一堆图像(已知位姿),为什么不用传统的视觉方法重建场景呢?这就是一个典型的SfM问题啊。关于这个问题我也没有答案。如果你有想法欢迎在评论区讨论。是不是NeRF建出的模型更稠密、精度更高呢?

4.6 NeRF与SLAM的结合

NeRF与SLAM从某些角度上来看具有一定的相似性。可能在地图存储、位姿估计方面有一定的结合点。比如现在的SLAM地图都是显式的存储三维点云,但如果可以用NeRF隐式地存储,也许会有一些新的应用。

本文作者原创,未经许可不得转载,谢谢配合

返回顶部