96 lines
6.6 KiB
Markdown
96 lines
6.6 KiB
Markdown
# NeRF
|
||
|
||
最原始的 NeRF 在 2020 年被提出,这还是一个比较新的领域。NeRF 的主要目标是在密集采样的图片中对这个图片中的物体进行三维重建。
|
||
|
||
NeRF 想做这样一件事,不需要中间三维重建的过程,仅根据位姿内参和图像,直接合成新视角下的图像。
|
||
|
||
在生成建模前,我们需要对被建模物体进行密集的采样,如下图是一个示例的训练集,它含有 100 张图片以及保存了每一张图片相机参数(表示拍摄位置,拍摄角度,焦距的矩阵)的 json 文件。
|
||
|
||

|
||
|
||
你可以看到,这 100 张图片是对一个乐高推土机的多角度拍摄结果。我们需要的是一个可<strong>以获取这个推土机在任意角度下拍摄的图片</strong>的模型。如图所示:
|
||
|
||

|
||
|
||
现在来看 NeRF 网络:
|
||
|
||
在 NeRF 中,我们把空间<strong>认为是一个个的小方块叠成的空间</strong>(可以理解为 MC)每一个方块有以下属性:
|
||
|
||
- 3 个位置坐标(x,y,z)
|
||
- 透明度$\sigma$
|
||
- 注意:因为每个角度观察的颜色并不相同(光线原因),颜色属于一个会根据观察角度变化的隐藏属性。
|
||
|
||
# 用 NeRF 如何建模?(思路部分)
|
||
|
||
## 得到模型
|
||
|
||
我们需要的是每个视角下的图片,可以理解为从一个视角发射<strong>光线</strong>,<u>一根光线对应一个像素点</u>。这些光线穿透路径上的所有方块,把这些方块上的属性信息以某种方式累计,就能得到这个像素的颜色。这是 一个已有的公式,只要我们获得每个小方块的颜色信息和不透明度,我们就能知道这个角度下的视图。(这个我们后面介绍)
|
||
|
||
现在的难点在于:我们不知道<strong>每个小方块的颜色信息</strong>(因为颜色会随着观察角度变化)。众所周知,算法解决不了的问题就扔给神经网络试试啦~
|
||
|
||
<strong>为了获取根据角度变化而变化的颜色信息,我们选择了神经网络。</strong>
|
||
|
||
<strong>这个网络的输入是:</strong>
|
||
|
||
- 小方块的位置坐标(x,y,z)
|
||
- 观察角度(以二维坐标表示两个偏转角)
|
||
|
||
<strong>这个网络的输出是:</strong>
|
||
|
||
- 对应的小方块的 RGB 信息
|
||
- 不透明度
|
||
|
||

|
||
|
||
在这里,作者选择了最简单的 MLP,因此,<strong>这是一个输入为 5 维,输出为 4 维向量</strong>($R,G,B,\sigma$)的简单网络,值得注意的是,不透明度与观察角度无关,这里在网络中进行了特殊处理,让这个值与后两维无关。
|
||
|
||
<strong>现在我们能够输入坐标和视角信息得到小方块的颜色和不透明度,我们就可以对光线穿过的小方块进行计算了。</strong>
|
||
|
||
## 进行渲染
|
||
|
||
(渲染就是得到三维建模的某视角下图片)
|
||
|
||
得到每条光线上的方块信息后,我们对其进行计算(这里开始介绍上面略过的公式)
|
||
|
||
这个公式对光线上的所有小方块的颜色进行加权求和,权重是关于不透明度$\sigma$的一个函数$T(\sigma)$,不透明度在[0,1]之间,越不透明这个值越大。也就是越不透明,占的颜色比重越高,比如空气的$\sigma$就接近于 0,乐高本身就接近 1。而求和的结果就是这个光线对应像素的颜色。
|
||
|
||
这里展开说一下$T(\sigma)$,我们把不透明度理解为光线在这个小方块被阻止的概率,越不透明,越容易阻挡光线,而光线一旦被阻挡,就不用计算后面的小方块颜色了。因此,我们的$T(\sigma)$就表示<strong>光线能够行进到这个小方块的概率</strong>,也就是这点之前所有小方块的$(1-\sigma)$的乘积。
|
||
|
||
这段要仔细看和推导,第一遍不容易直接懂。顺带一提,我们的<strong>小方块</strong>学名叫<strong>体素</strong>,<del>为了显得我们更专业一点以后就叫它体素罢</del>
|
||
|
||

|
||
|
||
上面所说的公式具体如下:t 是我们的$\sigma$,$t_f,t_n$分别是离发射点最远的体素和最近的体素。这个公式求得是像素的颜色。
|
||
|
||

|
||
|
||
思路总体如上,这里放一张找来的渲染过程示意图(<del>不知道为什么有点包浆</del>)
|
||
|
||

|
||
|
||
# 算法细节部分
|
||
|
||
上述只是 NeRF 的思路,也是后续改进工作的核心,具体实现使用了很多 trick,下面进行列举介绍:
|
||
|
||
## 采样方法
|
||
|
||
因为上述计算使用了积分,而计算机只能处理非连续的信息,因此我们先把光线分为数个小段,在每个小段中使用蒙特卡罗采样(就是随机抽),得到积分的近似值。
|
||
|
||
我们也可以配合另一种更巧妙的方法实现采样,就在下面啦。
|
||
|
||
## 粗网络和精细网络
|
||
|
||
如上述采样方法所说,因为是均匀采样,我们对空气和实体的体素关注度都是差不多的,但是这并无必要,空气上采样点少了也不会怎么样,而实体上如果采样点增多,图片就会更加清晰,因此我们设计了以下算法来进行优化。
|
||
|
||
我们使用了两个网络:粗网络和精细网络。
|
||
|
||
粗网络就是上述采样方法用的普通网络,而<strong>粗网络输出的不透明度值会被作为一个概率分布函数</strong>,精细网络根据这个概率分布在光线上进行采样,不透明度越大的点,它的邻域被采样的概率越大,也就实现了我们要求的在实体上多采样,空气中少采样。最后精细网络输出作为结果,因此粗网络可以只求不透明度,无视颜色信息。
|
||
|
||

|
||
|
||
## 位置编码
|
||
|
||
学过 cv 的大家想必对这个东西耳熟能详了吧~,这里的位置编码是对输入的两个位置和一个方向进行的(体素位置,相机位置和方向),使用的是类似 transformer 的三角函数类编码如下。位置编码存在的意义是放大原本的 5 维输入对网络的影响程度,把原本的 5D 输入变为 90 维向量;并且加入了与其他体素的相对位置信息。
|
||
|
||

|