对NeRF的开源代码的解读请见博文 原始NeRF代码学习记录。
NeRF 做得这么好的原因有:
根据计算机图形学的知识,三维物体的shape+texture,camera,图像这三种信息,知道两个就能推出第三个。
CVPR 2022 6 hour Tutorial on Neural Fields in Computer Vision
如下图,蓝色的一坨是 三维物体,然后两个黑色的平面(与z轴垂直)把物体 “夹住”,
这两个平面与 z 轴的交点的 z 轴坐标值分别为 near、far。
任意一条相机光线 ray,其在 z 轴上的投影虚线对应光线ray位于 把物体包围住的一个box里的部分(计算机图形学的一个概念)。
在对 ray上的点进行采样时,先于 z 轴上 在[near, far] 的范围采样,然后映射到 ray 上对应的部分。
t_vals = torch.linspace(0., 1., steps=N_samples)
if not lindisp:
z_vals = near * (1.-t_vals) + far * (t_vals)
......
......
......
pts = rays_o[...,None,:] + rays_d[...,None,:] * z_vals[...,:,None] # [N_rays, N_samples, 3]
论文 supplementary 中提到的 转换到 NDC space 的 insights 来自这篇博文。
将坐标系变换到 NDC空间(Normalized Device Coordinate, [ − 1 , 1 ] 3 [-1,1]^3 [−1,1]3)并不是对所有的情况,只有对那些 forward-facing的场景才会施加,对 36 0 。 360^。 360。场景,是不会施加的。(NDC only works when the whole scene lies behind a “near” plane perpendicular to the camera axis. So yes, it will only work for a half-hemisphere scene with less than 180 degree field of view, and certainly not for 360 degree scenes. Issue47)
有两个网络:coarse,fine,采样的时候首先均匀采样 N c N_c Nc个点,输入coarse网络,根据 volume rendering 的公式,inverse-sample 出 N f N_f Nf 个点,但是输入 fine 模型的不是只有 N f N_f Nf 个点,而是 N c + N f N_c+N_f Nc+Nf 个点。这么看, fine 模型的表现多少依赖于 coarse 模型。因为 fine 模型的采样点依赖于 coarse 模型。
作者认为 NeRF 不是用来做3维重建的,它真正想做的是视图重建
其实NeRF三维重建的效果不是太好
NeRF以像素为单位进行训练,像素的训练都是独立的。
更新一个本人产生的疑惑及其答案。
问题:sampling points 的信息(RGB, σ \sigma σ)是怎么得到的呢?
这个问题的产生受到了论文中下面表达式的影响
F Θ ( x , d ) = ( C , σ ) \mathbf{F}_{\Theta}(\mathbf{x},\mathbf{d})=(\mathbf{C},\sigma) FΘ(x,d)=(C,σ)
下意识觉得 MLP 是对 输入的多张图像进行处理。
答案:NeRF 的MLP恰恰是以 sampling points 为输入,输出对它们预测的信息。然后 volume rendering,得到对应的图像。
本文介绍论文 NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis( ECCV 2020)里面主要的 3 个部分:Volume Rendering,Positional Encoding 和 Hierarchical Volume Sampling。相信本文详细的介绍会让你对 NeRF 的论文有比较深入细致的了解。本文主要参照 vtuber AI葵的视频讲解,讲解1链接和讲解2链接。
在这里首先要说明的是 NeRF 论文中的公式(3),它是从论文中公式(1)的定积分通过矩形法求定积分推导而来。
先抛出几个符号: σ , α , δ \sigma ,\qquad \alpha,\qquad \delta σ,α,δ
还有一个重要概念:transmittance。
它们的含义如下:
由相机 o 和某个成像点 C 两点确定的射线如图 1 所示。以相机为原点 o \mathbf{o} o,射线方向为坐标轴方向建立坐标轴。则坐标轴上任意一点坐标可表示为 o + t d \mathbf{o}+t\mathbf{d} o+td。其中 t t t 为该点到原点距离, d \mathbf{d} d为单位方向向量。near,far 实际上是两个垂直于射线,平行于成像平面的平面。在这里,也用near、far表示那两个平面与射线的交点。在射线上采样的范围是从 near 点到 far 点。理论上,如果对于相关的 scene 我们一无所知,near 点应该被设在在原点(相机处),far 点则在 无穷远。但是实际上如果我们要处理的是 synthetic dataset,则会根据已知的物体在 scene 中的范围调整 near 和 far。因为这样可以减少计算量。
将 near 到 far 的范围 n 等分,在第 i i i 个等分小区间内均匀随机采样,得到采样点点 C i \mathbf{C}_i Ci。注意,用大写的 C i \mathbf{C}_i Ci 代表第 i i i个采样点。而小写的 c i \mathbf{c}_i ci 代表它的RGB颜色向量。
首先, α \alpha α 是由 σ \sigma σ 推导得到的一个量
α = 1 − e − σ δ (1) \alpha=1-e^{-\sigma\delta}\tag{1} α=1−e−σδ(1)
现在,先给出成像点 C 的颜色公式:
C ^ = c 1 α 1 + c 2 α 2 ( 1 − α 1 ) + c 3 α 3 ( 1 − α 1 ) ( 1 − α 2 ) + . . . + c n α n ( 1 − α 1 ) ( 1 − α 2 ) . . . ( 1 − α n − 1 ) (2) \begin{aligned} \hat{\mathbf{C}} =&\mathbf{c}_1 \alpha_1 \\ & + \mathbf{c}_2 \alpha_2 (1-\alpha_1 ) \\ & + \mathbf{c}_3 \alpha_3 (1-\alpha_1 ) (1-\alpha_2 ) \\ &+ ... \\ &+ \mathbf{c}_n \alpha_n (1-\alpha_1 ) (1-\alpha_2 )...(1-\alpha_{n-1}) \tag{2} \end{aligned} C^=c1α1+c2α2(1−α1)+c3α3(1−α1)(1−α2)+...+cnαn(1−α1)(1−α2)...(1−αn−1)(2)
其中, α \alpha α 的定义见上文公式(1)。 C i C_i Ci对应的 t r a n s m i t t a n c e i transmittance_i transmittancei 为
( 1 − α 1 ) ( 1 − α 2 ) . . . ( 1 − α i − 1 ) (1-\alpha_1 ) (1-\alpha_2 )...(1-\alpha_{i-1}) (1−α1)(1−α2)...(1−αi−1)
t r a n s m i t t a n c e i transmittance_i transmittancei 可以理解为除去当前点 C i C_i Ci 前面的 点 C 1 , C 2 , . . . , C i − 1 C_1,C_2,...,C_{i-1} C1,C2,...,Ci−1对 C 的影响之后, C i C_i Ci能对 C产生的最大影响系数。然后, α i \alpha_i αi 乘上 t r a n s m i t t a n c e i transmittance_i transmittancei的乘积才是最终的 C i C_i Ci 对C 颜色的权重,记作 w e i g h t i weight_i weighti。
不同相机射线的采样点不同,对应的 transmittance 不同,这就解释了为什么Scene 中同一个三维点,从不同的方向去看颜色可能是不同的。
等式(2)就是 NeRF 论文里面的等式(3):
C ^ ( r ) = ∑ i = 1 N T i ( 1 − e − σ i δ i ) c i , T i = e − ∑ j = 1 i − 1 σ j δ j (3) \hat{C}(\mathbf{r})= \sum_{i=1}^NT_{i}(1-e^{-\sigma_i\delta_i})\mathbf{c}_i, \qquad T_{i}=e^{-\sum_{j=1}^{i-1}\sigma_j\delta_j} \tag{3} C^(r)=i=1∑NTi(1−e−σiδi)ci,Ti=e−∑j=1i−1σjδj(3)
在等式(2)中,将 α \alpha α的公式带入,即可化为等式(3)。
阅读等式(2)比等式(3)更容易理解 Volume Rendering 的过程,但是前者可能不太简洁,不太方便放到论文上。所以我们看到的在NeRF论文上出现的是 等式(3)而不是等式(2)。
其中, C ^ ( r ) \hat{C}(\mathbf{r}) C^(r) 表示用论文方法 estimate(preditct)的成像点 C 的颜色。 r = o + t d \mathbf{r}=\mathbf{o}+t\mathbf{d} r=o+td,代表一条唯一的相机射线。
再介绍一下相邻采样点之间的距离 δ i \delta_i δi 的计算方法。针对每个采样点 C 1 , C 2 , . . . , C n \mathbf{C}_1,\mathbf{C}_2,...,\mathbf{C}_n C1,C2,...,Cn ,事先知道每个采样点对于的距离相机o的距离 z 1 , z 2 , . . z n z_1,z_2,..z_n z1,z2,..zn。那么有两个 list:
Z 1 = [ z 1 , z 2 , . . . , z n − 1 ] (4) Z_1 = [z_1,z_2,...,z_{n-1}] \tag{4} Z1=[z1,z2,...,zn−1](4)
Z 2 = [ z 2 , z 3 , . . . , z n ] (5) Z_2 = [z_2,z_3,...,z_{n}] \tag{5} Z2=[z2,z3,...,zn](5)
等式(5)减去等式(4),有
[ δ 1 , δ 2 , . . . , δ n − 1 ] : = [ z 2 − z 1 , z 3 − z 2 , . . . , z n − z n − 1 ] : = Z 2 − Z 1 [\delta_1,\delta_2,...,\delta_{n-1}]:= [z_2-z_1,z_3-z_2,...,z_{n}-z_{n-1}]:=Z_2-Z_1 [δ1,δ2,...,δn−1]:=[z2−z1,z3−z2,...,zn−zn−1]:=Z2−Z1
特殊地, δ n = + ∞ − z n \delta_{n}=+\infty-z_n δn=+∞−zn,在NeRF的代码中,作者将 δ n \delta_{n} δn设置成一个很大的正数。
NeRF成功的因素,除了文章本身的 idea 外,即训练一个 MLP :
F Θ : ( x , d ) ↦ ( C , σ ) (6) F_\Theta:(\mathbf{x},\mathbf{d})\mapsto (\mathbf{C}, \sigma) \tag{6} FΘ:(x,d)↦(C,σ)(6)
其中, x , d \mathbf{x},\mathbf{d} x,d 分别代表 scene 中的任意一个三维坐标和单位方向向量。
C , σ \mathbf{C},\sigma C,σ 分别代表由三维坐标和方向向量唯一确定的相机射线确定的三维坐标处的颜色,和三维坐标处的 Volume Density。
还包括一个非常重要的 trick:Positional Encoding。图2展示了 NeRF论文中的实验结果对比。在图2中用红色矩形框标注出来的就是没有使用 Positional Encoding 的结果,它明显比使用了的(Complete Model)结果差。差的部分主要体现在图像的一些 high-frequency 部分。
那么,为什么说Positional Encoding 至关重要呢?
为了回答这个问题,首先要介绍一下关于“图像”的一个概念:high-frequency (高频)和 low-frequency(低频)。如图4所示,标号1所处的地方是白色类似于桌布的东西,标号2所处的地方是核桃。在前者的小区域内,移动一点位置,颜色并不会变化很大,但是后者就会。前者的区域对应 low-frequency,后者的区域对应 high-frequency。
那么,Positional Encoding 具体是怎么做的呢?
首先,给出函数 γ ( x ) \gamma(x) γ(x) 的定义,
γ ( x ) = [ sin ( 2 0 π x ) , cos ( 2 0 π x ) , sin ( 2 1 π x ) , cos ( 2 1 π x ) , … , sin ( 2 L − 1 π x ) , cos ( 2 L − 1 π x ) ] T (7) \gamma(x) =[\sin(2^0\pi x), \cos(2^0\pi x) , \sin(2^1\pi x), \cos(2^1\pi x),\quad \dots \quad, \sin(2^{L-1}\pi x), \cos(2^{L-1}\pi x)]^{T} \tag{7} γ(x)=[sin(20πx),cos(20πx),sin(21πx),cos(21πx),…,sin(2L−1πx),cos(2L−1πx)]T(7)
公式(7)中的 L L L 在下文中会解释其含义。
图 5 中下半部分使用了 Positional Encoding: x \mathbf{x} x先经由 γ ( x ) \gamma(x) γ(x)处理,然后将处理后的中间量输入进 F Θ F_\Theta FΘ 。
This is consistent with recent work by Rahaman et al. [34], which shows that deep networks are biased towards learning lower frequency functions. They additionally show that mapping the inputs to a higher dimensional space using high frequency functions before passing them to thenetwork enables better fitting of data that contains high frequency variation.
上面这段话指出——深度神经网络偏向于学习图像 low-frequency 的部分,而针对 high-frequency 的部分难以学习,而对输入施加如公式(7)所示的 Positional Encoding 后,则能将 input 映射到高维空间从而解决这个问题。
公式(7)中 L L L的值决定了神经网络能学习到的最高频率的大小。
针对三维位置 x \mathbf{x} x,讨论 γ ( x ) \gamma(\mathbf{x}) γ(x) 输出的维度大小。
首先, x \mathbf{x} x 有 3 个分量。针对每个分量,如公式(7)所示,都会得到拥有 L × 2 = 10 × 2 = 20 L\times2=10\times2=20 L×2=10×2=20 个维度的输出。所以输出一共有 60 60 60 个维度。
在 NeRF中,一共有训练两个 MLP,分别是 m o d e l c model_c modelc (coarse)和 m o d e l f model_f modelf(fine),它们的模型结构一样,只是参数不同。它们分别对应 Hierarchical Volume Sampling 两次抽样的模型。
关于第二次采样,具体地,首先对 w e i g h t i weight_i weighti归一化,即
w i ^ = w i ∑ j = 1 N C w j (8) \hat{w_i}=\frac{w_i}{\sum_{j=1}^{N_C}w_j} \tag{8} wi^=∑j=1NCwjwi(8)
其中, w i w_i wi表示 w e i g h t i weight_i weighti, w i ^ \hat{w_i} wi^表示归一化后的 w i w_i wi, N c N_c Nc是第一次采样的采样点数目。
现在注意到, ∑ i = 1 N c w i ^ : = 1 \sum_{i=1}^{N_c} \hat{w_i}:=1 i=1∑Ncwi^:=1
对 w i ^ \hat{w_i} wi^ 构造累计分布函数(CDF)。
举个例子,比如说:第一次采样有 6 个采样点,它们的 weight 分别是
30 , 5 , 70 , 10 , 120 , 30 30, 5 , 70, 10 ,120, 30 30,5,70,10,120,30
那么该例子的 累计 weight 曲线如图 6 所示。横轴是采样样本点的 index,竖轴是累计 weight。
在竖轴上[0,1]范围内以 0.0625 为间距等距采样,灰色虚线与蓝色实线相交的点是“采样点”。
在 NeRF 代码中,实际上采样点取的是灰色虚线与 Theoretical 累计分布函数曲线的交点。图中的蓝色阶梯曲线是 Empirical累计分布函数曲线,所以看到有一些采样点是相同的。这是因为本人举的这个例子和画图技术所限,没能把 Theoretical 累计分布函数曲线画出来。但是应该不影响理解。
不难看出,在曲线拥有比较长的阶梯的地方,第二次采样的点的数目会很多。
阶梯长是因为那个地方对应的第一次采样点有大的 weight值。
最后,把那些黑色虚线与 Theoretical 累计分布函数曲线交点对应的点给采样出来,就实现了在 第一次采样点 中 weight 比较大的地方多采样,weight 比较小的地方少采样。