社区计算机视觉课程文档

神经辐射场 (NeRFs)

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始入门

神经辐射场 (NeRFs)

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

简短历史 📖

NeRF 领域相对年轻,Mildenhall 等人的首次出版物出现在 2020 年。自那时以来,大量的论文被发表,并且取得了快速的进展。自 2020 年以来,已经发布了超过 620 篇预印本和出版物,GitHub 上有超过 250 个存储库。(截至 2023 年 12 月,数据来自 paperswithcode.com

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

由于 NeRF 领域发展迅速,我们在最后添加了一个章节,我们将在其中预告最新的研究和 NeRF 可能的未来方向。

现在历史就讲到这里,让我们深入了解 NeRF 的内在原理吧!🚀🚀

底层方法(Vanilla NeRF)📘🔍

NeRF 背后的基本思想是将场景表示为一个连续函数,该函数将位置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 等人 (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, where 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}^* 是真实(Ground Truth)像素颜色。

补充说明

在一个章节中详细描述整个 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

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

训练您自己的 NeRF

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

当前领域进展

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

< > 在 GitHub 上更新