NeRF:将场景表示为神经辐射场以用于视图合成

Mildenhall B, Srinivasan P P, Tancik M, et al. Nerf: Representing scenes as neural radiance fields for view synthesis[C]//European conference on computer vision. 2020.

NeRF 在 2020 年欧洲计算机视觉大会 (ECCV) 上作为口头报告的论文发表,仅使用多视角的 RGB 图像数据,通过神经网络建模场景的三维几何形状和颜色信息,就可以实现高度逼真的三维重建和新视图合成,效果令人惊叹。

NeRF 所做的任务就是 新视图合成 (novel view synthesis),即根据若干已知 视角 (viewpoint) 下的图像,合成任意新视角下的图像。传统方法中,通常采用三维重建再渲染的方式实现;而 NeRF 希望不进行显式的三维重建过程,仅根据内外参直接得到新视角渲染的图像。为了实现这一目的,NeRF 使用多层感知机作为一个 3D 场景的隐式表达,通过这样的网络可以直接渲染任意角度任意位置的投影图像 1

目录

  • 一. 研究思路
    • 1. 新的场景表示
    • 2. 新视图的渲染过程
    • 3. 优化工作
  • 二. 基于神经辐射场的场景表示
  • 三. 神经辐射场的体积渲染(新视图的表示)
  • 四. 神经辐射场的优化工作
    • 1. 位置编码
    • 2. 分层抽样
    • 3. 实现细节
  • 五. 实验结果
    • 1. 数据集
    • 2. 对比实验
  • 六. 总结
  • 七. 复现
    • 1. TensorFlow 版
    • 2. Pytorch 版

一. 研究思路

  • 视图合成 (view synthesis) 通过给定一组图像及其各自的摄像机位生成同一场景的新视图。想要从新的视角生成逼真的图像需要正确地处理复杂的几何形状和材料反射特性。
  • 目前的场景表示和生成方法还没有一种能够在相机或视角之间存在较大距离 (camera baseline) 的情况下生成逼真质量的图像。
  • 文章提出了一种新的场景表示方法,可以用梯度下降进行训练以大量生成新视角下的高分辨率图像,并且仍然具有极高的内存效率。

1. 新的场景表示

文章中提出的新的场景表示如下:将一个静态场景表示为一个连续的 5D 函数,函数输出在各个空间点 ( x , y , z ) (x, y, z) (x,y,z) 上向各个方向 ( θ , ϕ ) (θ, ϕ) (θ,ϕ) 发射出来的 辐射 (radiance)体积密度 (density) 。空间点的辐射其实就是 RGB 颜色;体积密度就是不透明度,决定了光线穿过空间点 ( x , y , z ) (x, y, z) (x,y,z) 能够在该点累加多少辐射。

2D 视角方向 ( θ , ϕ ) (θ, ϕ) (θ,ϕ):描述观察点向外观察的方向的参数。 θ θ θ 称为极角,通常用来表示在水平平面上的方向,从正前方开始顺时针旋转的角度; ϕ ϕ ϕ 称为俯仰角,通常用来表示上下方向,也就是从水平线向上或向下的角度。
NeRF:将场景表示为神经辐射场以用于视图合成_第1张图片

差分不透明度 (Differential opacity):这里理解为不透明度 2,指的是在不同的区域或情境中,物体或材料的透明度(即光线穿过的程度)不同,这个术语用来描述不同元素或区域的可见程度差异。例如,某个图像中的某一区域可能具有高差分不透明度,因此在合成图像时会更显眼,而其他区域可能有低差分不透明度,因此相对不太显眼。

2. 新视图的渲染过程

文章中通过优化一个多层感知机 MLP(即没有用到卷积层的深度全连接神经网络)来表示前文提出的 5D 函数,将一个 5 维坐标 ( x , y , z , θ , ϕ ) (x, y, z, θ, ϕ) (x,y,z,θ,ϕ) 映射到对应的体积密度和 RGB 颜色。为了使用 神经辐射场 (NeRF) 从特定视角渲染新图像,过程如下:

  1. 使用相机光线穿过场景,生成一组 3D 样本点;
  2. 将这些 3D 样本点及其对应的 2D 视角方向作为神经网络的输入,生成一组颜色和密度;
  3. 使用经典的立体渲染技术,累加这些颜色和密度,得到 2D 图像。

上述过程显然可微,因此可以使用梯度下降法来优化这个 MLP 模型,即最小化特定视角下的真实图像和模型渲染得到的图像之间的误差。在多个视角下缩小误差可以让模型输出逼近场景的真实分布。整个流程如图所示:

NeRF:将场景表示为神经辐射场以用于视图合成_第2张图片

3. 优化工作

文章中指出,对于复杂的场景,用简单的梯度下降法训练 NeRF 很难得到高分辨率的收敛结果。为此,提出用一个 位置编码 (positional encoding) 对输入 5D 坐标进行变换,使得 MLP 可以表示更高频的函数 3

二. 基于神经辐射场的场景表示

NeRF 将一个连续场景表示为一个 5D 向量值函数 (5D vector-valued function)

  • 输入:3D 空间位置 x = ( x , y , z ) \bold{x} = (x, y, z) x=(x,y,z) 和 2D 视角方向 ( θ , ϕ ) (θ, ϕ) (θ,ϕ)
  • 输出:发射颜色 c = ( r , g , b ) \bold{c} = (r, g, b) c=(r,g,b) 和体积密度 σ σ σ(即前文所述不透明度);

在实际应用中,使用 三维笛卡尔单位向量 (3D Cartesian unit vector) d \bold{d} d 来表示 2D 视角方向 ( θ , ϕ ) (θ, ϕ) (θ,ϕ)。用 MLP 来近似该函数的映射:
F Θ : ( x , d ) → ( c , σ ) F_{\Theta}: (\bold{x}, \bold{d}) \rightarrow (\bold{c}, σ) FΘ:(x,d)(c,σ)
通过优化 MLP 的参数 Θ \Theta Θ 来得到从 5D 坐标到对应发射颜色和体积密度的映射。因为场景表示对各个视角有效,因此有以下假设:

  • 体积密度 σ σ σ 仅与空间位置 x \bold{x} x 有关,与视角 d \bold{d} d 无关;
  • 发射颜色 c \bold{c} c 与空间位置 x \bold{x} x 和视角 d \bold{d} d 有关;

为了符合上述体积密度、发射颜色与空间位置、视角之间的关系,实现网络如下:

  • MLP 网络先用 8 层全连接层处理空间位置 x \bold{x} x(每层使用 ReLU 激活函数,256 通道),得到体积密度 σ σ σ 和一个 256 维的特征向量;
  • 然后将 256 维的特征向量与视角 d \bold{d} d 拼接后再传入下一层全连接层(使用 ReLU 激活函数,128 通道),输出发射颜色 c \bold{c} c
    NeRF:将场景表示为神经辐射场以用于视图合成_第3张图片

上述网络经过训练后即可对场景中的任意粒子的颜色和体积密度进行表示。

这里的粒子就是前文中的空间点 ( x , y , z ) (x, y, z) (x,y,z),可以理解为点云,后面统称为粒子。

三. 神经辐射场的体积渲染(新视图的表示)

上文已经详细阐述了如何根据若干已知视角下的图像表示场景 —— 神经辐射场,下面将介绍如何根据已经得到的神经辐射场表示新视角的图像。

新视角图像指的是特定视角下观察场景所得的 2D 图片,本质就是一个个 RGB 像素点。新视角图像每个像素点的颜色可以使用经典体积渲染原理得到。

神经辐射场中粒子的不透明度 σ σ σ 可以理解为光线穿过 x \bold{x} x 处微元粒子后终止的概率。根据前文提出的场景表示,新视图图像的生成本质上就是从一个指定视角发出指定方向的光线最终穿过或停留的情况,这取决于光线经过的粒子的颜色和不透明度。

NeRF:将场景表示为神经辐射场以用于视图合成_第4张图片

从指定视角 o \bold{o} o 发出的方向为 d \bold{d} d 的光线,在 t t t 时刻能够到达的空间点为:
r ( t ) = o + t d \bold{r}(t)=\bold{o}+t \bold{d} r(t)=o+td
沿 d \bold{d} d 方向在时间范围 ( t n , t ) (t_n, t) (tn,t) 对不透明度 σ σ σ 积分,稍作调整可以得到 t t t 时刻的累计透明度,也就是光线在时间范围 ( t n , t ) (t_n, t) (tn,t) 内没有碰到任何粒子遮挡的概率:
T ( t ) = exp ⁡ ( − ∫ t n t σ ( r ( s ) ) d s ) T(t)=\exp \left(-\int_{t_n}^t \sigma(\mathbf{r}(s)) d s\right) T(t)=exp(tntσ(r(s))ds)

沿 d \bold{d} d 方向在时间范围 ( t n , t f ) (t_n, t_f) (tn,tf) 对粒子积分,可以得到从新视角 r \bold{r} r 处观察场景得到的颜色值:
C ( r ) = ∫ t n t f T ( t ) σ ( r ( t ) ) c ( r ( t ) , d ) d t C(\mathbf{r})=\int_{t_n}^{t_f} T(t) \sigma(\mathbf{r}(t)) \mathbf{c}(\mathbf{r}(t), \mathbf{d}) d t C(r)=tntfT(t)σ(r(t))c(r(t),d)dt

这里的 ( t n , t f ) (t_n, t_f) (tn,tf) 可以理解为光线经过场景的前后两个表面的时刻,在 NeRF 中不需要显式计算,因为整个过程是由神经网络模型来处理的。关于相机参数与坐标的关系可以参考 NeRF代码解读-相机参数与坐标系变换,此处不加赘述。

然而,定积分一般用于渲染离散的场景表示,如体素、网格等,会大大限制生成视图的分辨率。因此,文中采用 分层抽样法 (stratified sampling approach):把 [ t n , t f ] [t_n, t_f] [tn,tf] 分成均匀的小区间,对每个小区间进行均匀采样:
t i ∼ u [ t n + i − 1 N ( t f − t n ) , t n + i N ( t f − t n ) ] t_i \sim \mathcal{u}\left[t_n+\frac{i-1}{N}\left(t_f-t_n\right), t_n+\frac{i}{N}\left(t_f-t_n\right)\right] tiu[tn+Ni1(tftn),tn+Ni(tftn)]

对于采样的样本,采用离散的积分方法进行累加:
C ^ ( r ) = ∑ i = 1 N T i ( 1 − exp ⁡ ( − σ i δ i ) ) c i ,  where  T i = exp ⁡ ( − ∑ j = 1 i − 1 σ j δ j ) \hat{C}(\mathbf{r})=\sum_{i=1}^N T_i\left(1-\exp \left(-\sigma_i \delta_i\right)\right) \mathbf{c}_i, \text { where } T_i=\exp \left(-\sum_{j=1}^{i-1} \sigma_j \delta_j\right) C^(r)=i=1NTi(1exp(σiδi))ci, where Ti=exp(j=1i1σjδj)

其中 δ i = t i + 1 − t i \delta_i=t_{i+1}-t_i δi=ti+1ti 是相邻样本之间的距离。

四. 神经辐射场的优化工作

前两节分别介绍了将场景表示为神经辐射场和从该表示中生成新视图,就是 NeRF 的基本内容。但仅仅这样还不能生成高质量的新视图,为了能够生成高分辨率的新视图,需要进行以下改进:

  • 位置编码 (positional encoding):对输入 ( x , y , z , θ , ϕ ) (x, y, z, θ, ϕ) (x,y,z,θ,ϕ) 进行位置编码,使得 MLP 能够更好地表达高频函数,从而提高图像分辨率;
  • 分层抽样 (hierarchical sampling procedure):把积分区间分成均匀的小区间,对每个小区间均匀采样后采用离散的积分方法进行累加,从而提高新视图的分辨率;

1. 位置编码

尽管神经网络理论上可以逼近任何函数,但实验发现仅用 MLP 构成的 F Θ F_{\Theta} FΘ 处理输入 ( x , y , z , θ , ϕ ) (x, y, z, θ, ϕ) (x,y,z,θ,ϕ) 在表示高频变量的颜色和几何方面表现欠佳。这和 Rahaman 等人在《On the spectral bias of neural networks》中论述的结论相符,即神经网络倾向于学习到频率较低的函数。Rahaman 等人还指出,先用高频函数将输入映射到高维空间后再将其传入网络可以更好地拟合数据中的高频信息。

对神经辐射场的输入也进行高频预处理:将 F Θ F_{\Theta} FΘ 修改成两个函数的组合: F Θ = F Θ ′ ∘ γ F_{\Theta}=F_{\Theta}^{\prime} \circ \gamma FΘ=FΘγ 。其中 F Θ ′ F_{\Theta}^{\prime} FΘ 是学习得到的 MLP 网络, γ \gamma γ 表示从 R \mathbb{R} R 到更高维 R 2 L \mathbb{R}^{2 L} R2L 的编码函数。本文使用的编码函数如下:
γ ( p ) = ( sin ⁡ ( 2 0 π p ) , cos ⁡ ( 2 0 π p ) , ⋯   , sin ⁡ ( 2 L − 1 π p ) , cos ⁡ ( 2 L − 1 π p ) ) \gamma(p)=\left(\sin \left(2^0 \pi p\right), \cos \left(2^0 \pi p\right), \cdots, \sin \left(2^{L-1} \pi p\right), \cos \left(2^{L-1} \pi p\right)\right) γ(p)=(sin(20πp),cos(20πp),,sin(2L1πp),cos(2L1πp))

文中将编码函数 γ ( ⋅ ) \gamma(\cdot) γ() 应用于三维空间位置 x \mathbf{x} x(需要归一化到 [ − 1 , 1 ] [-1,1] [1,1])和三维笛卡尔单位方向向量 d \mathbf{d} d。文中对于 γ ( x ) \gamma(\mathbf{x}) γ(x) 设置 L = 10 L=10 L=10 ,对于 γ ( d ) \gamma(\mathbf{d}) γ(d) 设置 L = 4 L=4 L=4

注意,这里的位置编码与 Transformer 中的位置编码不同,前者是为了把连续的输入坐标映射到更高维的空间从而使得 MLP 能够更好地近似高频的函数,后者是为了维护输入序列中的位置信息以帮助模型更好地理解和处理序列数据。

2. 分层抽样

前文阐述的渲染策略是在每条相机光线上采样 N 个点进而评估神经辐射场,这是相当低效的,因为在空白区域和被遮挡区域都有大量的重复采样。于是作者提出了分层渲染,通过分层按比例分配样本数以提高渲染效率。

这里的按比例分配可以理解为:对颜色贡献大的点附近采样密集,贡献小的点附近采样稀疏。

文中并不是使用单个网络来表示场景,而是同时优化粗细两个网络。

  • 粗 (coarse) 网络:先分层采样 N c N_c Nc 个粒子,使用前文 C ^ ( r ) \hat{C}(\mathbf{r}) C^(r) 的公式评估粗网络中的这些粒子,粒子颜色为:
    C ^ c ( r ) = ∑ i = 1 N c w i c i , w i = T i ( 1 − exp ⁡ ( − σ i δ i ) ) \hat{C}_c(\mathbf{r})=\sum_{i=1}^{N_c} w_i c_i, \quad w_i=T_i\left(1-\exp \left(-\sigma_i \delta_i\right)\right) C^c(r)=i=1Ncwici,wi=Ti(1exp(σiδi))
    对权重做归一化处理:
    w ^ i = w i ∑ j = 1 N c w j \hat{w}_i=\frac{w_i}{\sum_{j=1}^{N_c} w_j} w^i=j=1Ncwjwi
    这里的 w ^ i \hat{w}_i w^i 可以看作是粗网络中沿着光线的分段常数概率密度函数 (piecewise-constant PDF),可以近似为粗网络中该方向上粒子的分布情况。

  • 细 (fine) 网络:使用逆变换采样,根据此前求出来的概率分布再采样 N f N_f Nf 个粒子,仍然使用前文 C ^ ( r ) \hat{C}(\mathbf{r}) C^(r) 的公式评估采样粒子的颜色 C ^ f ( r ) \hat{C}_f(\mathbf{r}) C^f(r)。不同之处是使用了全部的 N c + N f N_c+N_f Nc+Nf 个样本。这样第二次采样可以根据分布采样更多的样本在真正有场景内容的区域,实现了重要性抽样 (Importance Sampling)。

NeRF:将场景表示为神经辐射场以用于视图合成_第5张图片
如图所示,白色点为第一次均匀采样的粒子,红色点为第二次按概率分布采样的粒子,概率高的地方粒子密集,概率低的地方粒子稀疏。

3. 实现细节

为每个新视角优化一个单独的神经网络,使用一组场景的 RGB 图像及其对应机位和相机内参,和场景边界。训练过程中使用小批量随机梯度下降,损失函数为生成图像颜色和真实图像颜色之间的总平方误差:
L = ∑ r ∈ R [ ∥ C ^ c ( r ) − C ( r ) ∥ 2 2 + ∥ C ^ f ( r ) − C ( r ) ∥ 2 2 ] \mathcal{L}=\sum_{\mathbf{r} \in \mathcal{R}}\left[\left\|\hat{C}_c(\mathbf{r})-C(\mathbf{r})\right\|_2^2+\left\|\hat{C}_f(\mathbf{r})-C(\mathbf{r})\right\|_2^2\right] L=rR[ C^c(r)C(r) 22+ C^f(r)C(r) 22]
其中, R \mathcal{R} R 为一个 batch 中所有采样的光线集合, C ( r ) C(\mathbf{r}) C(r) 为真实的 RGB 颜色, C ^ c ( r ) \hat{C}_c(\mathbf{r}) C^c(r) 为粗网络预测的 RGB 颜色, C ^ f ( r ) \hat{C}_f(\mathbf{r}) C^f(r) 为细网络预测的 RGB 颜色。

五. 实验结果

实验表明,NeRF 优于之前的工作。实验细节见 https://www.matthewtancik.com/nerf,代码见 https://github.com/bmild/nerf。

1. 数据集

  • Diffuse Synthetic 360°:没有官方名称,来源于论文《Deepvoxels: Learning persistent 3D feature embeddings》。该数据集由 4 个具有简单几何形状的兰伯特物体组成,每个物体均为上半球视角采样得到的 512 × 512 像素的图像。

    兰伯特物体 (Lambertian objects):一类在光学和计算机图形学中常用的理想化表面模型,具有以下特征:

    1. 均匀漫反射:兰伯特物体反射光线的方式是均匀的漫反射,这意味着它们以相同的强度将入射光反射到所有方向,而不会产生镜面反射。
    2. 不依赖观察角度:不论从哪个角度观察兰伯特物体,其表面都会以相同的亮度反射光线,前提是入射光线均匀地照射到表面。

    兰伯特物体模型通常用于渲染和计算机图形学中,因为它是一个相对简单的理想模型,易于计算和模拟。然而,在现实世界中,几乎没有真正的兰伯特物体,因为大多数物体表面都会产生一定程度的镜面反射和光泽。兰伯特物体模型作为一种理想化的起点,可用于研究光照和渲染技术,以及在计算机图形学中创建逼真的虚拟场景。

  • Realistic Synthetic 360°:作者 Mildenhall 等人采样得到的数据集,也被称为 NeRF-Synthetic。该数据集由 8 个具有复杂几何形状的真实的非兰伯特物体组成。其中 6 个物体从上半球视角采样,2 个物体 360° 采样,都为 800 × 800 像素的图像。

  • Real ForwardFacing:包含 8 个场景,其中 5 个场景来自 Mildenhall 等人 2019 年发布的论文《Local light field fusion: Practical view synthesis with prescriptive sampling guidelines》,3 个场景为本次采样得到。所有图像均为手机拍摄,1008 × 756 像素。

如果想使用自己的数据集进行训练,需要创建 llff 格式的数据集。一般都是用 COLMAP 软件处理照片信息中的相机内外参得到视角数据,详见 nerf训练自己的数据,过程记录十分详细、【NeRF数据集】基于COLMAP制作自己的LLFF格式数据集。

2. 对比实验

定量结果见下表:
NeRF:将场景表示为神经辐射场以用于视图合成_第6张图片

定性结果如图:
NeRF:将场景表示为神经辐射场以用于视图合成_第7张图片
NeRF:将场景表示为神经辐射场以用于视图合成_第8张图片

六. 总结

NeRF 最大的创新点就是通过隐式表达绕过了人工表示三维场景的方法,能够从更高维度学习到场景的三维信息。

NeRF 的缺点是速度非常慢,这一点在后续工作中有所改进 4。此外,NeRF 的可解释性和隐式表达的能力,依然需要探索。

七. 复现

作者 Mildenhall 等人在发布论文时是使用 Tensorflow 框架实现的 NeRF,因此本人在学习时先复现了 Tensorflow 版的 NeRF。考虑到目前的实际应用中一般都使用 Pytorch 进行实现,因此又复现了 Pytorch 版本的 NeRF。

1. TensorFlow 版

  • 平台:AutoDL
  • 显卡:RTX 3090 24G
  • 镜像:TensorFlow 1.15.5、Python 3.8(ubuntu18.04)、Cuda 11.4
  • 源码:https://github.com/bmild/nerf

实验记录

  1. 源码的 README 文档中第一条指令 conda env create -f environment.yml 在安装 tensorflow 时耗时太久,遂改用自己创建的新环境(因为镜像中已有 tensorflow,所以会快很多),安装相关依赖包;
  2. bash download_example_data.sh 下载数据太慢,复制 download_example_data.sh 内网址下载至本地后上传并解压;
  3. 安装 imageio 后仍执行失败,在 Issues 中找到答案:https://github.com/bmild/nerf/issues/190#issuecomment-1590868746;
  4. 以上更改完成后执行 python run_nerf.py --config config_fern.txt 进行实验;

实验结果

(显卡被占用开不了机 … 因为自己配了环境又不想迁移,所以等资源释放了再把实验补上)

NeRF:将场景表示为神经辐射场以用于视图合成_第9张图片

训练了 10 个小时只跑了 40w 轮迭代,因经费有限,提前终止。得到的蕨类植物渲染结果如下:

2. Pytorch 版

  • 平台:AutoDL
  • 显卡:RTX 3090 24G
  • 镜像:PyTorch 1.11.0、Python 3.8(ubuntu20.04)、Cuda 11.3
  • 源码:https://github.com/yenchenlin/nerf-pytorch

实验记录

  1. 最初选用 PyTorch 1.5.1、Python 3.8(ubuntu18.04)、Cuda 10.1 版本镜像,出现 Pytorch 版本和 CUDA 不兼容问题,后修改镜像为 PyTorch 1.11.0、Python 3.8(ubuntu20.04)、Cuda 11.3 版本即可;
  2. 由于时间和经费关系,只运行了乐高的样例;

实验结果

NeRF:将场景表示为神经辐射场以用于视图合成_第10张图片

经过 4 个半小时的训练,得到了低分辨率的乐高渲染结果:


  1. 论文笔记:NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis ↩︎

  2. 【论文泛读】NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis ↩︎

  3. 文献翻译阅读-NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis ↩︎

  4. NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis ↩︎

你可能感兴趣的:(3D视觉,NeRF,神经辐射场,3D视觉,视图渲染)