NeRF:类神经网路在View Synthesis的热门新方向

项目主页:https://www.matthewtancik.com/nerf

论文地址: https://arxiv.org/abs/2003.08934  
code:https://github.com/bmild/nerf https://github.com/yenchenlin/nerf-pytorch
ECCV 2020 (Oral Presentation, Best Paper Honorable Mention)

NeRF:用深度学习完成3D渲染任务的蹿红

文章难度:★★★★☆
阅读建议: 这篇文章尽量深入浅出地介绍 NeRF,这个近期 deep learning的热门新宠儿。 NeRF因为牵扯到许多 rendering与机率的设计,所以直接啃起来是挺硬的。这篇文章一开始会先简单地介绍 NeRF以及 Volume Rendering的概念,而后才会一步一步地解释 NeRF的运作细节与训练方法。最后也会简单带过几个接续 NeRF研究的优秀方法。
推荐背景知识: deep learning, multilayer perceptron (MLP), volume rendering, ray tracing, light field, view synthesis, neural rendering, quadrature, stratified sampling.

NeRF 是 2020 年 ECCV 上获得最佳论文荣誉提名的工作,其影响力是十分巨大的。NeRF 将隐式表达推上了一个新的高度,仅用 2D 的 posed images 作为监督,即可表示复杂的三维场景,在新视角合成这一任务上的表现是非常 impressive 的。

问题1:3D渲染是要干什么?

用照相机拍照是一个现实世界的物理过程,主要是光学过程,拍照对象是现实世界中真实的万事万物,形成照片的机制主要就是:光经过镜头,到达传感器,被记录下来。

渲染就是用计算机模拟这一过程,模拟“拍照”的对象是已存在的某种三维场景表示(3D representation of the scene),模拟生成照片的机制是图形学研究人员精心设计的算法。

关键前提:渲染的前提是某种三维场景表示已经存在。渲染一词本身不包办生成三维场景表示。不过,渲染的确与三维场景表示的形式息息相关;因此研究渲染的工作通常包含对三维场景表示的探讨。

  • 问题2:3D渲染是图形学问题,那么原先大家是用什么传统图形学方法实现3D渲染的呢?

主要有两种算法:光栅化(rasterization),光线追踪(ray tracing);都是对照相机拍照的光学过程进行数学物理建模来实现的

Quick Overview: Neural Radiance Fields (NeRF)

NeRF的核心精神是将物体与场景的信息,编码 (encode) 进 MLP (multi-layer perceptron)中。然后使用 computer graphics(计算机图形学)中的 volume rendering(立体渲染)将 MLP中的信息投影出来。

在训练的部分, NeRF 所需要的是某个物体与场景的 multi-view多视角影像,利用这些影像来训练NeRF,或者更直接地说,将信息 encode进 NeRF。之后就可以对这个物体与场景 render出连续、不存在于原始信息的视角

NeRF整体的结果非常令人惊艳,特别是在金属或有光泽物体的结果,视觉的真实性真的非常高。可以参考以下的图片,或者直接到官方的页面看影片会更有感觉。

不过如果对于没有 rendering相关知识的人,要直接理解NeRF可能会有点障碍。因此,接下来会先简单带一些基础的 volume rendering与 ray tracing观念,再回头去看 NeRF到底是怎么做的。

Volume Rendering

所谓的 volume rendering (立体渲染) 指的是将 discretely sampled 3D data投影到 2D的技术。像是以下这张图片,我们已知一个物体的离散3D信息,对于目前的视角,就可以动态地渲染出这个视角看到的画面。

使用shear warp algorithm做volume rendering的例子。(资料来源)

Ray Tracing

而在 rendering上其中一个实作方法是 ray tracing。 这个方法可以用眼睛看物体来解释。我们眼睛之所以看的到物体,是因为有光源打到了物体,然后反射进我们的眼睛。而Ray tracing的想法其实就是反推这个射线 (ray),从相机中心发出射线,与物体相交就根据规则反射、折射或吸收,直到遇到光源或者走太远停住。

Ray tracing的方法示意图 (资料来源)

总之, ray tracing就是借由这个概念算出在 image平面上的 2D投影该长什么样子。实际上这边的名词蛮复杂的 (至少对非图学出身的笔者来说),像是 ray tracing、 ray casting、 ray marching什么的。为了避免混淆视听,这边就先不解释过多了。

接下来,就开始正式介绍 NeRF的细节。

Neural Radiance Fields (NeRF)-神经辐射场

NeRF网路的输入是一组 5D的参数,包含一组 3D的 location X = (x, y, z)跟一组 2D的 view direction (θ, φ),实事上这个 view direction表示为 3D Cartesian unit vector(笛卡尔单位矢量),称为 d。而NeRF网路的输出则是一组 emitted color (如RPG的c=(r, g, b)) 与 volume density σ

以下图来解释会更加清楚。这个 3D location指的是在 3D空间中的任何一个点,比如说怪手的尖端,而 view direction则是说我要从哪个视角看。NeRF网路的输出则是这个 3D点实际 (或者说推估) 的颜色,以及推估的 volume density(体积密度σ:可以简单理解为不透明度)。

有了RGB和体密度3D物体表示,就可以使用 Ray Tracing 等3D渲染方法来渲染出任意视角的2D投影。(Volume rendering)

NeRF的运作概念。(资料来源)

Radiance Fields,或者说映射 gθ ,能对三维场景进行隐式表示(implicit scene representation)。在上一节,我们说过某种三维场景表示正是渲染的前提。实现渲染也是 作者提出Radiance Fields这一新型三维场景表示方法 的目的所在。

NEeRF输入数据准备(将一系列的2d图片转成网络所需的输入):

多视角2D图像准备

想要利用NeRF拟合自己拍摄的3D场景,首先需要准备多张从不同角度拍摄的同场景静态2D图像,以沙发为例。

沙发多视图

相机内外参估计

不过仅仅有2D多视图还不够,在重建阶段,我们还需要各个点的位置 x={x,y,z} 和方位 d={θ,ϕ} 作为输入进行重建。为了获得这些数据,NeRF中采用了传统方法COLMAP进行参数估计。还是以沙发为例,通过COLMAP可以得到场景的稀疏重建结果,其输出文件包括相机内参,相机外参和3D点的信息,然后进一步利用LLFF开源代码中的imgs2poses文件将内外参整合到一个文件poses_boudns.npy中,该文件记录了相机的内参,包括图片分辨率(图片高与宽度)、焦距,共3个维度、外参(包括相机坐标到世界坐标转换的平移矩阵 t 与旋转矩阵 R,其中旋转矩阵为 R∈R3×3 的矩阵,共9个维度,平移矩阵为 t∈R3×1 的矩阵,3个维度,因此该文件中的数据维度为 N×17 (另有两个维度为光线的始发深度与终止深度,通过COLMAP输出的3D点位置计算得到),其中N为图片样本数。

ps: 这里建议将nerf-pytorch中加载poses_boudns.npy数据中的代码与imgs2poses中保存poses_boudns.npy的代码对着看一遍,就可以理解加载时为什么要搞一些奇怪的维度变换等操作了。

DCOLMAP重建结果

Raw数据到MPL的输入

输入生成与坐标系变换

在理解NeRF的过程中涉及到一个很重要的问题,即原文中提到的MLP的输入 x={x,y,z} 和 d={θ,ϕ} 是什么,其与相机内外参有什么关系?要搞懂这个问题,需要先理解坐标系变换

总的来说,坐标变换的目的在于将不同视角下视角特定的坐标系投影到一个统一的世界坐标中进行三维重建,见get_rays_np函数。首先,我们在像素坐标下进行网格点采样,得到特定分辨率图像的各个像素点坐标

i, j=np.meshgrid(np.arange(W, dtype=np.float32), np.arange(H, dtype=np.float32), indexing='xy')

然后,我们进行像素坐标到相机坐标的变换,要理解这个变换需要清楚两个点。

dirs=np.stack([(i-K[0][2])/K[0][0], -(j-K[1][2])/K[1][1], -np.ones_like(i)], -1)

首先像素坐标到相机坐标的变换属于投影变换的逆变换,即2D点到3D的变化,即我们需要根据像素点的坐标 (u,v) 计算该点在相机坐标系下的三维的坐标 (X,Y,Z)。

像素坐标到相机坐标

这时帅气的小伙伴就发现了,为什么相机坐标的 Y 和 Z 前面有负号?那是因为COLMAP采用的是opencv定义的相机坐标系统,其中x轴向右,y轴向下,z轴向内;而Nerf pytorch采用的是OpenGL定义的相机坐标系统,其中x轴向右,y轴向上,z轴向外。因此需要在y与z轴进行相反数转换。

最后,我们进行相机坐标到世界坐标的转换得到光线始发点与方向向量,这里需要了解世界坐标到相机坐标的变换过程,本质上为矩阵相乘,要进行反向变化只需要对变化矩阵求逆即可(COLMAP原输出的是世界坐标到相机坐标的旋转矩阵,在LLFF的imgs2poses.py中已经进行了求逆操作)。

rays_d=np.sum(dirs[..., np.newaxis, :] *c2w[:3,:3], -1)
rays_o=np.broadcast_to(c2w[:3,-1], np.shape(rays_d))

首先因为相机坐标与世界坐标的坐标原点不一致,要解决这个问题只需要将相机坐标进行平移变化即可进行对齐,实现平移的方式在是世界坐标系下减去一个平移矩阵,即相机外参中的平移矩阵 t ,对应得到代码中的rays_o。

平移以对齐坐标系的原点

此外,由于相机坐标与世界坐标的各个轴的朝向也是不同的,因此需要进一步通过旋转来对齐坐标轴的朝向,实现旋转的方式是在平移对齐坐标原点之后的基础上进行旋转,即与相机外参中的旋转矩阵 R 做矩阵乘法,对应得到代码中的rays_d。

旋转以对齐坐标轴的朝向

另附:NeRF的代码中其实还存在另外一个坐标系统:Normalized device coordinates(ndc)坐标系,一般对于一些forward facing的场景(景深很大)会选择进一步把世界坐标转换为ndc坐标,因为ndc坐标系中会将光线的边界限制在0-1之间以减少在景深比较大的背景下产生的负面影响。但是需要注意ndc坐标系不能和spherify pose(球状多视图采样时)同时使用,这点nerf-pytorch中并没有设置两者的互斥关系,而nerf-pl则明确设置了。

输入的进一步预处理

介绍完了将像素坐标转换到世界坐标形成光线始发点与方向向量之后,接下来便是文章中使用的一些trick,关于如何进一步处理光线起点与光线方向形成真正的MLP的输入值。

首先由于体素渲染需要沿着光线进行积分,而积分在计算机中是以离散的乘积和进行计算的,那么这里就涉及到在光线上进行点的采样。NeRF在光线的点采样过程中的进行了一些设计。首先为了避免大量的点采样导致的计算量的激增,NeRF设计了coarse to fine的采样策略。在coarse采样阶段,采用了带有扰动的均匀采样方法。第一步在光线的边界之间进行深度空间的均匀采样:

z_steps = torch.linspace(0, 1, N_samples, device=rays.device)
z_vals = near * (1-z_steps) + far * z_steps

然后在规定了下界与上界的范围内将采样点进行扰动:

z_vals_mid = 0.5 * (z_vals[: ,:-1] + z_vals[: ,1:])
upper = torch.cat([z_vals_mid, z_vals[: ,-1:]], -1)
lower = torch.cat([z_vals[: ,:1], z_vals_mid], -1)
perturb_rand = perturb * torch.rand(z_vals.shape, device=rays.device)
z_vals = lower + (upper - lower) * perturb_rand

最终coarse采样阶段在每条光线上采样了64个样本点。然后基于coarse采样得到的结果进一步指导fine的点采样,即在对最终颜色贡献更大(权重更大)的点附近进行更加密集的点采样。

然后是类似Transformer中的位置编码,将低维的坐标点与方向映射到高维空间以提升网络捕捉高频信息的能力,以nerf-pl中的代码nerf.py为例,embedding函数将3维的位置编码映射为63维,将3维的方向编码映射为27维。

处理好的样本点通过embedding函数进行低维到高维的映射之后得到的结果才会最终作为MLP的输入,MLP最终预测每个样本点的颜色值与不透明度。具体的网络结构图如下,NeRF为了建模view-specific的像素值,即不同的视角下看同一个点的颜色是有区别的,在网络的倒数第二层进一步把表示光线方向的高维编码输入以预测最终的像素值,而为了增强空间位置的condition,在网络的第四层又将表示位置的高维编码再一次输入到了网络中。

Nerf中MLP结构图

NeRF的训练

训练NeRF的输入数据是:从不同位置拍摄同一场景的图片,拍摄这些图片的相机位姿、相机内参,以及场景的范围。若图像数据集缺少相机参数真值,作者便使用经典SfM重建解决方案COLMAP估计了需要的参数,当作真值使用。

在训练使用NeRF渲染新图片的过程中,

  • 先将这些位置输入MLP以产生volume density和RGB颜色值;
  • 取不同的位置,使用体积渲染技术将这些值合成为一张完整的图像;
  • 因为体积渲染函数是可微的,所以可以通过最小化上一步渲染合成的、真实图像之间的差来训练优化NeRF场景表示。

这样的一个NeRF训练完成后,就得到一个以多层感知机的权重表示的模型。一个模型只含有该场景的信息,不具有生成别的场景的图片的能力。

除此之外,NeRF还有两个优化的trick:

  • 位置编码(positional encoding),类似于傅里叶变换,将低维输入映射到高维空间,提升网络捕捉高频信息的能力;
  • 体积渲染的分层采样(hierarchical volume sampling),通过更高效的采样策略减小估算积分式的计算开销,加快训练速度。

从MLP的输出到2D图像—体素渲染

在得到MLP对于某条光线中采样点的颜色与不透明度的预测值之后,便可以通过体素渲染进行该光线对应的像素颜色值的计算。这里的代码可以参考nerf-pl中的inference函数,每一步代码都有比较详细的说明。在训练阶段就将沿着特定光线预测的像素值与对应像素点的像素真值做L2损失函数以更新网络;在测试阶段就计算相机视锥中所有光线得到所有像素的像素值以组成完整的2D图像。

最后附上一些讲解关于相机内外参解释以及坐标系变换的资料:www.cs.cmu.edu/~16385/s17/Slides/11.1_Camera_matrix.pdf

ndc坐标系说明:github.com/bmild/nerf/files/4451808/ndc_derivation.pdf

Nerf-pytorch:https://github.com/yenchenlin/nerf-pytorch​

Nerf-pl:https://github.com/kwea123/nerf_pl​

Emitted Color & Volume Density

事实上 volume rendering的方法百百种,论文中使用的也只是其中一种依靠 emitted color与 volume density的方法。在论文中所谓的 volume density(密度)可以解释为这条射线会停在这个 3D位置的机率,而 emitted color指的是由某个视角看出去这个 3D位置的颜色

依照这样的设定, volume density在设计上应该只与 3D location有关,而 emitted color则是与 location与 view direction有关。因此 NeRF网路 (MLP) 会先吃进 3D location,通过 8层 FC layer,输出 volume density与 volume density以及 256维的 latent feature。而后与 view direction concatenate,在通过 FC layer得到 emitted color。

Vanilla NeRF

NeRF 所要做的 task 是 Novel View Synthesis,一般翻译为新视角合成任务,定义是:在已知视角下对场景进行一系列的捕获 (包括拍摄到的图像,以及每张图像对应的内外参),合成新视角下的图像。传统方法使用 IBR (Image Based Rendering) 方法的较多,也有一些使用深度学习和 IBR 结合的方法,这些我们不过多介绍。

NeRF 想做这样一件事,不需要中间三维重建的过程,仅根据位姿内参和图像,直接合成新视角下的图像。为此 NeRF 引入了辐射场的概念,这在图形学中是非常重要的概念,在此我们给出渲染方程的定义:

那么辐射和颜色是什么关系呢?我们不希望把这个文章讲复杂,简单讲就是,光就是电磁辐射,或者说是振荡的电磁场,光又有波长和频率,二者乘积为光速,光的颜色是由频率决定的,大多数光是不可见的,人眼可见的光谱称为可见光谱,对应的频率就是我们认为的颜色:

Positional Encoding

其实在 2018 年 Bengio 等人就发现 deep networks 更倾向于学习低频的函数,而可以想象的是,实际场景的神经辐射场基本上都是高频的,为此作者提出了 Positional Encoding(注意这里的 Positional Encoding 和 Transformer 中的 Positional Encoding 很像,但是解决问题是不一样的):

这里 p,L 都是标量, L 是超参数,对原始的坐标以及视角方向的每一维度都做相同的操作,然后再输入到网络中,事实上在官方的代码实现中,输入的是 (γ(p),p) ,也就是说原始信息也保留了下来。下图是一个关于 Positional Encoding 以及视角方向相关的 ablation study:

关于 Positional Encoding 以及 SIREN 等工作我们会在之后专门选一个章节进行讲解。读者暂时仅需保留一个印象即可。

Hierarchical volume sampling

使用体渲染积分就会遇到以下几个问题,虽然可以离散的近似计算积分,但是面临的问题就是如何采样。采样点过多开销过大,采样点过少近似误差有太大。直观的一个想法是,最好尽可能的避免在空缺部分以及被遮挡了的部分进行过多的采样,因为这些部分对最好的颜色贡献是很少的,基于这一想法 NeRF 提出分层采样训练的方式,如下图所示:

使用两个网络同时进行训练 (后称 coarse 和 fine 网络), coarse 网络输入的点是通过对光线均匀采样得到的,根据 coarse 网络预测的体密度值,对光线的分布进行估计,然后根据估计出的分布进行第二次重要性采样,然后再把所有的采样点 (Nc+Nf) 一起输入到 fine 网络进行预测。关于 loss 函数我们同样作为一个开放性的内容,不在此多做讲解了。

后NeRF时代

GIRAFFE:composition方向的代表作

2021CVPR的最佳论文奖得主GIRAFFE是NeRF、GRAF工作的延申。

在NeRF之后,有人提出了GRAF(Generative Radiance Fields),关键点在于引入了GAN来实现Neural Radiance Fields(神经辐射场);并使用conditional GAN实现对渲染内容的可控性。

在GRAF之后,GIRAFFE实现了composition。在NeRF、GRAF中,一个Neural Radiance Fields表示一个场景,one model per scene。而在GIRAFFE中,一个Neural Radiance Fields只表示一个物体,one object per scene(背景也算一个物体)。这样做的妙处在于可以随意组合不同场景的物体,可以改变同一场景中不同物体间的相对位置,渲染生成更多训练数据中没有的全新图像。

GIRAFFE实现composition

如图所示,GIRAFFE可以平移、旋转场景中的物体,还可以在场景中增添原本没有的新物体。

另外,GIRAFFE还可以改变物体的形状和外观,因为网络中加入了形状编码、外观编码变量(shape codes zsi , appearance codes zai )。

其他最新相关工作

2021年CVPR还有许多相关的精彩工作发表。例如,提升网络的泛化性:

  • pixelNeRF:将每个像素的特征向量而非像素本身作为输入,允许网络在不同场景的多视图图像上进行训练,学习场景先验,然后测试时直接接收一个或几个视图为输入合成新视图。
  • IBRNet:学习一个适用于多种场景的通用视图插值函数,从而不用为每个新的场景都新学习一个模型才能渲染;且网络结构上用了另一个时髦的东西 Transformer。
  • MVSNeRF:训练一个具有泛化性能的先验网络,在推理的时候只用3张输入图片就重建一个新的场景。

针对动态场景的NeRF:

  • Nerfies:多使用了一个多层感知机来拟合形变的SE(3) field,从而建模帧间场景形变。
  • D-NeRF:多使用了一个多层感知机来拟合场景形变的displacement。
  • Neural Scene Flow Fields:多提出了一个scene flow fields来描述时序的场景形变。

其他创新点:

  • PhySG:用球状高斯函数模拟BRDF(高级着色的上古神器)和环境光照,针对更复杂的光照环境,能处理非朗伯表面的反射。
  • NeX:用MPI(Multi-Plane Image )代替NeRF的RGBσ作为网络的输出。

5 不止是NeRF:Neural Rendering

Neural Radiance Fields的外面是Neural Rendering;换句话说,NeRF(Neural Radiance Fields)是Neural Rendering方向的子集。

在针对这个更宽泛的概念的综述State of the Art on Neural Rendering中,Neural Rendering的主要研究方向被分为5类,NeRF在其中应属于第2类“Novel View Synthesis”(不过这篇综述早于NeRF发表,表中没有NeRF条目)。

Neural Rendering的5类主要研究方向

表中彩色字母缩写的含义:

在这篇综述中,Neural Rendering被定义为:

Deep image or video generation approaches that enable explicit or implicit control of scene properties such as illumination, camera parameters, pose, geometry, appearance, and semantic structure.

Neural Rendering包含所有使用神经网络生成可控(且photo-realistic)的新图片的方法。“可控”指人可以显式或隐式地控制生成新图片的属性,常见的属性包括:光照,相机内参,相机位姿(外参),几何关系,外观,语义分割结构。在这个大框架下,NeRF是一种比较受欢迎的可控相机位姿的Neural Rendering算法。但Neural Rendering这个方向不止于此。

在目前的Neural Rendering方向,最火的子方向就是“Novel View Synthesis”,这与NeRF的强势蹿红密不可分;第二火的子方向是“Semantic Photo Synthesis”,这主要归功于语义分割以及相关的GAN领域的成熟度。“Semantic Photo Synthesis”方向也是成果颇丰,例如2019年CVPR的Semantic Image Synthesis with Spatially-Adaptive Normalization,其效果图如下。

Semantic Image Synthesis

最后我们给出一些 NeRF 的 paper list 以及代码整理:

综述论文

可以说是官方综述,作者列表是目前在Neural Rendering领域最活跃的一群人。两篇分别是2021、2020年的SIGGRAPH、CVPR讲座用到的综述,很全面很有条理,值得每位从业者一读!

SIGGRAPH 2021 Course: Advances in Neural Rendering

CVPR 2020 Tutorial: State of the Art on Neural Rendering

范围限定为可微渲染方法的综述:

Differentiable Rendering: A Survey

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注