社区计算机视觉课程文档

神经辐射场 (NeRFs)

Hugging Face's logo
加入 Hugging Face 社区

并获得增强文档体验

开始使用

神经辐射场 (NeRFs)

神经辐射场是将 3D 场景存储在神经网络中的一种方法。这种存储和表示场景的方式通常被称为隐式表示,因为场景参数完全由底层多层感知器 (MLP) 表示。(相比之下,显式表示会将场景参数(如颜色或密度)显式地存储在体素网格中。)这种新颖的场景表示方法在新视角合成任务中取得了非常令人印象深刻的结果,该任务是从训练集中不存在的相机视角插值新视角。此外,与显式表示相比,它允许我们使用更小的内存占用存储大型场景,因为我们只需要存储神经网络的权重,而体素网格的内存大小则按立方项增长。

简史 📖

NeRFs 领域相对较新,Mildenhall 等人于 2020 年发表了第一篇论文。从那时起,发表了大量论文,并取得了快速进展。自 2020 年以来,已经发布了 620 多篇预印本和出版物,GitHub 上有 250 多个存储库。(截至 2023 年 12 月,来自paperswithcode.com 的统计数据)

由于 NeRFs 的第一个公式需要很长的训练时间(在强大的 GPU 上最多需要几天),因此在加快训练和推理方面取得了许多进步。NVIDIA 于 2022 年发布的Instant-ngp是一个重要的飞跃。虽然这种方法中使用的模型架构与现有架构相似,但作者引入了一种使用可训练哈希表的新颖编码方法。由于这种类型的编码,我们可以在不损失重建质量的情况下显著缩小 MLP。这种新颖的方法训练和查询速度更快,同时在质量上与当时的最新方法相当。Mipnerf-360也于 2022 年发布,也值得一提。同样,模型架构与大多数 NeRFs 相同,但作者引入了一种新颖的场景收缩,使我们能够表示在所有方向上都无界的场景,这对现实世界的应用很重要。Zip-NeRF于 2023 年发布,结合了Instant-ngp的编码和Mipnerf-360的场景收缩等最新进展,以处理现实世界的情况,同时将训练时间缩短到不到一个小时。(公平地说,这仍然是在强大的 GPU 上测量的)

由于 NeRFs 领域正在快速发展,我们在最后添加了一个部分,我们将在此部分中介绍最新的研究以及 NeRFs 未来可能的发展方向。

但现在先别谈历史了,让我们深入了解 NeRFs 的本质吧! 🚀🚀

底层方法(原始 NeRF) 📘🔍

NeRFs 背后的基本思想是将场景表示为一个连续函数,该函数将位置xR3 \mathbf{x} \in \mathbb{R}^{3} 和观察方向θR2 \boldsymbol{\theta} \in \mathbb{R}^{2} 映射到颜色cR3 \mathbf{c} \in \mathbb{R}^{3} 和体积密度

σR1 \sigma \in \mathbb{R}^{1}由于神经网络可以作为通用函数逼近器,我们可以用一个简单的多层感知机(MLP)来逼近表示场景的这个连续函数。FΘ:(x,θ)(c,σ) F_{\mathrm{\Theta}} : (\mathbf{x}, \boldsymbol{\theta}) \to (\mathbf{c},\sigma) .

一个简单的NeRF管道可以用下图概括。

nerf_pipeline

图片来自:Mildenhall et al. (2020)

(a) 沿相机光线采样点和视角方向,并将它们传入网络。

(b) 网络输出每个样本的颜色向量和密度值。

(c) 通过体渲染结合网络输出,从3D空间中的离散样本生成2D图像。

(d) 计算损失并通过反向传播更新网络梯度以表示场景。

这个概述非常高层次,为了更好地理解,让我们深入了解体渲染和使用的损失函数的细节。

体渲染

体渲染过程背后的原理在经典的计算机图形管道中已经很好地确立,并且并非源于NeRF。对于NeRF的使用案例来说,重要的是这一步是可微分的,以便允许反向传播。NeRF中最简单的体渲染形式可以表述如下:C(r)=tntfT(t)σ(r(t))c(r(t),d)dt\mathbf{C}(\mathbf{r}) = \int_{t_n}^{t_f}T(t)\sigma(\mathbf{r}(t))\mathbf{c}(\mathbf{r}(t),\mathbf{d})dt

在上式中,C(r) \mathbf{C}(\mathbf{r}) 是相机光线的预期颜色。r(t)=o+td \mathbf{r}(t)=\mathbf{o}+t\mathbf{d} 其中,oR3 \mathbf{o} \in \mathbb{R}^{3} 是相机的原点,dR3 \boldsymbol{d} \in \mathbb{R}^{3} 是作为3D单位向量的视角方向,以及tR+ t \in \mathbb{R}_+ 是沿光线的方向距离。tn t_n tf t_f 分别代表光线的近端和远端边界。T(t) T(t) 表示沿光线累积的透射率。r(t) \mathbf{r}(t) tn t_n t t .

离散化后,上式可以计算为以下求和:C^(r)=i=1NTi(1exp(σiδi))ci, 其中 Ti=exp(j=1i1σjδj)\boldsymbol{\hat{C}}(\mathbf{r})=\sum_{i=1}^{N}T_i (1-\exp(-\sigma_i \delta_i)) \mathbf{c}_i\,, \textrm{ where }T_i=\exp \bigg(-\sum_{j=1}^{i-1} \sigma_j \delta_j \bigg)

下面,您可以看到一条离散化相机光线的示意图,以便更好地理解上述变量。

ray_image

损失函数公式

由于离散化体渲染方程是完全可微的,因此可以使用渲染像素上的重构损失来训练底层神经网络的权重。许多 NeRF 方法使用像素级误差项,可以写成如下形式:Lrecon(C^,C)=C^C2\mathcal{L}_{\rm recon}(\boldsymbol{\hat{C}},\boldsymbol{C^*}) = \left\|\boldsymbol{\hat{C}}-\boldsymbol{C^*}\right\|^2

其中C^ \boldsymbol{\hat{C}} 是渲染的像素颜色,并且

C \boldsymbol{C}^* 是地面实况像素颜色。

其他说明

在单个章节中详细描述整个NeRF管道非常困难。以上解释对于理解基本概念非常重要,并且在每个NeRF模型中都相似,即使不完全相同。但是,要获得性能良好的模型,还需要一些额外的技巧。

首先,需要对输入信号进行编码,以捕捉颜色和几何形状中的高频变化。在将输入传递到神经网络之前对输入进行编码的做法并非NeRF领域的独有,在其他机器学习领域(例如自然语言处理(NLP))中也被广泛采用。一个非常简单的编码方法是将输入映射到更高维的空间,使我们能够捕捉场景参数中的高频变化,如下所示

import torch
import mediapy as media
import numpy as np


def positional_encoding(in_tensor, num_frequencies, min_freq_exp, max_freq_exp):
    """Function for positional encoding."""
    # Scale input tensor to [0, 2 * pi]
    scaled_in_tensor = 2 * np.pi * in_tensor
    # Generate frequency spectrum
    freqs = 2 ** torch.linspace(
        min_freq_exp, max_freq_exp, num_frequencies, device=in_tensor.device
    )
    # Generate encodings
    scaled_inputs = scaled_in_tensor.unsqueeze(-1) * freqs
    encoded_inputs = torch.cat(
        [torch.sin(scaled_inputs), torch.cos(scaled_inputs)], dim=-1
    )
    return encoded_inputs.view(*in_tensor.shape[:-1], -1)


def visualize_grid(grid, encoded_images, resolution):
    """Helper Function to visualize grid."""
    # Split the grid into separate channels for x and y
    x_channel, y_channel = grid[..., 0], grid[..., 1]
    # Show the original grid
    print("Input Values:")
    media.show_images([x_channel, y_channel], cmap="plasma", border=True)
    # Show the encoded grid
    print("Encoded Values:")
    num_channels_to_visualize = min(
        8, encoded_images.shape[-1]
    )  # Visualize up to 8 channels
    encoded_images_to_show = encoded_images.view(resolution, resolution, -1).permute(
        2, 0, 1
    )[:num_channels_to_visualize]
    media.show_images(encoded_images_to_show, vmin=-1, vmax=1, cmap="plasma", border=True)


# Parameters similar to your NeRFEncoding example
num_frequencies = 4
min_freq_exp = 0
max_freq_exp = 6
resolution = 128

# Generate a 2D grid of points in the range [0, 1]
x_samples = torch.linspace(0, 1, resolution)
y_samples = torch.linspace(0, 1, resolution)
grid = torch.stack(
    torch.meshgrid(x_samples, y_samples), dim=-1
)  # [resolution, resolution, 2]

# Apply positional encoding
encoded_grid = positional_encoding(grid, num_frequencies, min_freq_exp, max_freq_exp)

# Visualize result
visualize_grid(grid, encoded_grid, resolution)

输出应该类似于下图

encoding

第二个值得一提的技巧是,大多数方法都使用智能方法来对空间中的点进行采样。从本质上讲,我们希望避免在场景为空的区域进行采样。有多种方法可以将样本集中在对最终图像贡献最大的区域,但最突出的一种方法是使用第二个网络,通常称为提议网络,这样就不会浪费计算资源。如果您对这种提议网络的内部工作原理和优化感兴趣,可以随意查阅Mipnerf-360的出版物,该网络首次在其中提出。

训练你自己的NeRF

为了在训练你的第一个NeRF时获得完整的体验,我建议你看看来自nerfstudio团队的优秀的Google Colab笔记本。在那里,你可以上传你选择的场景的图像并训练一个NeRF。例如,你可以拟合一个模型来表示你的客厅。🎉🎉

该领域的最新进展

该领域正在快速发展,新出版物的数量几乎呈爆炸式增长。在训练和渲染速度方面,VR-NeRFSMERF显示出非常有希望的结果。我们相信,我们很快就能在边缘设备上实时流式传输真实世界的场景,这将是迈向现实元宇宙的一大飞跃。然而,NeRF领域的研 究不仅专注于训练和推理速度,还涵盖了各种方向,例如生成式NeRF、姿态估计、可变形NeRF、组合性等等。如果您对NeRF出版物的精选列表感兴趣,请查看Awesome-NeRF

< > 在GitHub上更新