什么是3维内容
3维内容是包括形状和外观模型的组合,能够渲染为不同视角的2维图像。
直接构造精准的三维模型非常困难和耗时,因此利用反渲染的技术从图像直接构建出三维模型。
NeRF
显示三维表示和隐式三维表示
使用全连接层隐式表示三维结构。
三维空间显示表示:体素Voxel、点云Point Cloud、三角面片Mesh等。
三维空间隐式表示:符号距离函数(SDF,Signed Distance Funciton)、占用场(Occupancy Field)、神经辐射场(NeRF,Neural Radiance Field)等。
符号距离函数:表示一个点到一个曲面的最小距离,用正负来区分点在曲面内外。点在曲面内部则规定距离为负值,点在曲面外部则规定距离为正值,点在曲面上则距离为0。
S D F ( x ) = s ( x ∈ R 3 , s ∈ R ) SDF(x)=s\ \ \ (x\in\mathbb R^3,s\in\mathbb R) SDF(x)=s (x∈R3,s∈R)
占用场:表示一个点被曲面占用的概率(占用就是在曲面内部),用神经网络表示的占用场又叫做Neural Surface Field。 p p p是空间中的点, s s s表示 p p p被曲面占用的概率。和SDF的区别在于,SDF中 s s s没有取值范围限制,而占用场的 s s s的取值是 [ 0 , 1 ] [0,1] [0,1]。
F ( p ) = s ( p ∈ R 3 , s ∈ [ 0 , 1 ] ) F(p)=s\ \ \ (p\in\mathbb R^3,s\in[0,1]) F(p)=s (p∈R3,s∈[0,1])
神经辐射场:将“空间中的点+点发出的一条射线”映射到“点的密度值+射线的方向对应的颜色值”。将传统的离散采样几何替换为了连续的体积函数,参数化为多层感知机。
F ( x , y , z , θ , ϕ ) → ( R , G , B , σ ) F(x,y,z,\theta,\phi)\rightarrow (R,G,B,\sigma) F(x,y,z,θ,ϕ)→(R,G,B,σ)
( θ , ϕ ) (\theta,\phi) (θ,ϕ)是球坐标系下表示的方向, θ \theta θ表示俯仰角, ϕ \phi ϕ表示偏航角。视线 r r r上的点表示为 r ( t ) = o + t d r(t)=o+td r(t)=o+td
o o o是相机的光心在世界坐标系的坐标
d d d是直线方向,世界坐标系下的单位向量
t t t是一个实数表示从 o o o点沿着视线到 r ( t ) r(t) r(t)点的距离
r ( t ) r(t) r(t)表示世界坐标系下的点
(笛卡尔坐标系,方向在球坐标系是 ( θ , ϕ ) (\theta,\phi) (θ,ϕ),为了方便使用通常用笛卡尔坐标系,虽然包含三个量,但方向向量是单位向量,所以只有2个自由度)
由 r ( t ) = o + t d r(t)=o+td r(t)=o+td的计算公式,输入点 P P P的坐标和视角是在世界坐标系下的。
(场的概念类似于函数,表示一种映射关系,场的定义是向量到向量或数的映射,空间中的场可以认为是 “空间中点”到“点的属性”的映射,也就是每个点对应这个点的属性。)
三维模型的外观表示
1、Material texture map & environment lighting材料纹理映射和环境光
把三维模型的外观(材料、纹理)展开为2维图像,每个像素存储对应的3维点,即把2维的材料、纹理性质映射到3维模型上面。
全景图表示光照,从不同方向入射到物体表面的光。
材料光照分开,可以编辑,如更换材质贴图,光照。
2、辐射场Radiance field(表面光场surface light field)
材料光照结合,无法编辑。
NeRF的形状表示
NeRF的形状表示类似于烟雾,软形状表示方式soft shape。
NeRF的外观表示
NeRF输入:
图像 ( u , v ) → ( x , y , z , θ , ϕ ) (u,v)\rightarrow(x,y,z,\theta,\phi) (u,v)→(x,y,z,θ,ϕ),从图像和相机位姿计算射线 r ( t ) = o + t d r(t)=o+td r(t)=o+td 。
世界坐标 → W 2 C / [ R T ] \overset{W2C/[RT]}{\rightarrow} →W2C/[RT]相机坐标
相机坐标 → C 2 W / [ R T ] − 1 \overset{C2W/[RT]^{-1}}{\rightarrow} →C2W/[RT]−1世界坐标
( u , v ) → K , C 2 W o , d (u,v)\overset{K,C2W}{\rightarrow}o,d (u,v)→K,C2Wo,d
[ t n e a r , t f a r ] → 采样 [ r ( t n e a r ) , r ( t f a r ) ] [t_{near},t_{far}]\overset{采样}{\rightarrow}[r(t_{near}),r(t_{far})] [tnear,tfar]→采样[r(tnear),r(tfar)]
( x , y , z , d ) / ( x , y , z , θ , ϕ ) (x,y,z,d)/(x,y,z,\theta,\phi) (x,y,z,d)/(x,y,z,θ,ϕ)
从图像上采样(哪些像素,一个像素对应一条射线),从射线上采样粒子( t n e a r ∼ t f a r t_{near}\sim t_{far} tnear∼tfar 采样粒子)。例如,一张图像采样1024个像素,得到1024条射线,每条射线采样64个粒子。共1024*64个粒子。
输入模型
[ 1024 ∗ 64 , 3 , r a y d i r ] [1024*64,3,ray_{dir}] [1024∗64,3,raydir]
p o i n t s : ( 1024 , 64 , 3 ) points:(1024,64,3) points:(1024,64,3), v i e w d i r s : ( 1024 , 3 ) viewdirs:(1024,3) viewdirs:(1024,3)
位置编码:
1、没有视图依赖,缺少镜面反射的渲染。
2、没有位置编码,建模结果细节丢失,缺乏高频信息。
全连接层 F Θ F_{\Theta} FΘ直接输入 ( x , y , z , θ , ϕ ) (x,y,z,\theta,\phi) (x,y,z,θ,ϕ)在渲染的颜色和几何高频变化时表现不佳。On the spectral bias of neural networks表明神经网络偏向于学习低频函数,使用高频函数将输入映射到高维空间可以更好地拟合包含高频变化的数据,因此引入位置编码对输入 ( x , y , z , θ , ϕ ) (x,y,z,\theta,\phi) (x,y,z,θ,ϕ)进行编码。
利用位置编码把一个值从 R \mathbb R R维映射到 R 2 L \mathbb R^{2L} R2L维,NeRF对于空间坐标 x , L = 10 x,L=10 x,L=10,对于视角坐标 d , L = 4 d,L=4 d,L=4。
γ ( p ) = ( s i n ( 2 0 π p ) , c o s ( 2 0 π p ) , ⋯ , s i n ( 2 L − 1 π p ) , c o s ( 2 L − 1 π p ) ) , p ∈ R \gamma(p)=(sin(2^0\pi p),cos(2^0\pi p),\cdots,sin(2^{L-1}\pi p),cos(2^{L-1}\pi p)),p\in\mathbb R γ(p)=(sin(20πp),cos(20πp),⋯,sin(2L−1πp),cos(2L−1πp)),p∈R
γ ( ⋅ ) \gamma(\cdot) γ(⋅)应用到坐标的每个值, p p p归一化为 [ − 1 , 1 ] [-1,1] [−1,1]。
两个相近的坐标位置,经过位置编码后,前面的维度变化频率较低,后面的维度高频变化。
( x , y , z ) → 位置编码 ( γ ( x ) , γ ( y ) , γ ( z ) ) ( L = 10 , 3 维 → 60 维 ) (x,y,z)\overset{位置编码}{\rightarrow}(\gamma(x),\gamma(y),\gamma(z))\ (L=10,3维\rightarrow 60维) (x,y,z)→位置编码(γ(x),γ(y),γ(z)) (L=10,3维→60维)
( d x , d y , d z ) → 位置编码 ( γ ( d x ) , γ ( d y ) , γ ( d z ) ) ( L = 4 , 3 维 → 24 维 ) (d_x,d_y,d_z)\overset{位置编码}{\rightarrow}(\gamma(d_x),\gamma(d_y),\gamma(d_z))\ (L=4,3维\rightarrow24维) (dx,dy,dz)→位置编码(γ(dx),γ(dy),γ(dz)) (L=4,3维→24维)
所以,对于空间坐标输入 x x x,size变为 3 → 3 ∗ 20 = 60 3\rightarrow3*20=60 3→3∗20=60,对于视角坐标输入 d d d,size变为 3 → 3 ∗ 8 = 24 3\rightarrow3*8=24 3→3∗8=24。实际代码中输入为63维和27维(加上原始输入)。
损失函数:
自监督方式,GT是图像像素的RGB值,将该像素位置对应的射线上的粒子颜色求和得到预测值,然后把预测值和真实像素RGB值做MSE。
L = ∑ r ∈ R ∣ ∣ C ^ ( r ) − C ( r ) ∣ ∣ 2 2 L=\sum_{r\in R}||\hat C(r)-C(r)||^2_2 L=∑r∈R∣∣C^(r)−C(r)∣∣22, R R R是每个batch的射线(1024条)。
体渲染
怎么进行颜色求和?——体渲染
**体渲染:**视线 r r r上所有的点投射到图像上形成像素颜色 C C C的过程。
r ( t ) r(t) r(t)为三维空间中的一个点, d d d为视线方向,三维场景的近端和远端边界分别为 t n t_n tn和 t f t_f tf, C ( r ( t ) , d ) C(r(t),d) C(r(t),d)为三维点 r ( t ) r(t) r(t)从视角 d d d看到的颜色值, σ ( r ( t ) ) \sigma(r(t)) σ(r(t))为体密度函数,反应了 r ( t ) r(t) r(t)位置的物理材质吸收光线的能力, T ( t ) T(t) T(t)是射线上从 t n t_n tn到 t t t的累积透射率。
C ( r ) = ∫ t n t f T ( t ) σ ( r ( t ) ) C ( r ( t ) , d ) d t , w h e r e T ( t ) = e x p ( − ∫ t n t σ ( r ( u ) ) d u ) C(r)=\int^{t_f}_{t_n}T(t)\sigma(r(t))C(r(t),d)dt,where\ T(t)=exp(-\int^{t}_{t_n}\sigma(r(u))du) C(r)=∫tntfT(t)σ(r(t))C(r(t),d)dt,where T(t)=exp(−∫tntσ(r(u))du)
离散化计算积分值,将 t n t_n tn到 t f t_f tf拆分为 N N N个均匀分布区间,从每个区间中随机均匀抽取一个样本 t i t_i ti;
t i ∼ U [ t n + i − 1 N ( t f − t n ) , t n + i N ( t f − t n ) ] i ∈ [ 1 , N ] t_i\sim \mathcal U[t_n+\frac{i-1}{N}(t_f-t_n),t_n+\frac{i}{N}(t_f-t_n)]\ i\in[1,N] ti∼U[tn+Ni−1(tf−tn),tn+Ni(tf−tn)] i∈[1,N]
体渲染推导细节
s = o + t d = r ( t ) s=o+td=r(t) s=o+td=r(t)表示粒子的空间位置。 σ ( s ) \sigma(s) σ(s)表示在 s s s点处,光线碰撞粒子的概率密度。 C ( s ) C(s) C(s)表示 s s s点处,粒子发出光的颜色。 T ( s ) T(s) T(s)表示在 s s s点之前,光线没有被阻碍的概率。
C ^ ( s ) = ∫ 0 + ∞ T ( s ) σ ( s ) C ( s ) d s \hat C(s)=\int^{+\infin}_0T(s)\sigma(s)C(s)ds C^(s)=∫0+∞T(s)σ(s)C(s)ds
T ( s ) = e − ∫ 0 s σ ( t ) d t T(s)=e^{-\int^s_0\sigma(t)dt} T(s)=e−∫0sσ(t)dt
KaTeX parse error: {align} can be used only in display mode.
T ( s + d s ) − T ( s ) = − T ( s ) σ ( s ) d s T(s+ds)-T(s)=-T(s)\sigma(s)ds T(s+ds)−T(s)=−T(s)σ(s)ds
d T ( s ) = − T ( s ) σ ( s ) d s dT(s)=-T(s)\sigma(s)ds dT(s)=−T(s)σ(s)ds
d T ( s ) T ( s ) = − σ ( s ) d s \frac{dT(s)}{T(s)}=-\sigma(s)ds T(s)dT(s)=−σ(s)ds
∫ 0 t d T ( s ) T ( s ) = ∫ 0 t − σ ( s ) d s \int^t_0\frac{dT(s)}{T(s)}=\int^t_0-\sigma(s)ds ∫0tT(s)dT(s)=∫0t−σ(s)ds
l n T ( s ) ∣ 0 t = ∫ 0 t − σ ( s ) d s lnT(s)|^t_0=\int^t_0-\sigma(s)ds lnT(s)∣0t=∫0t−σ(s)ds
l n T ( t ) − l n T ( 0 ) = ∫ 0 t − σ ( s ) d s lnT(t)-lnT(0)=\int^t_0-\sigma(s)ds lnT(t)−lnT(0)=∫0t−σ(s)ds
l n T ( t ) = ∫ 0 t − σ ( s ) d s lnT(t)=\int^t_0-\sigma(s)ds lnT(t)=∫0t−σ(s)ds
T ( t ) = e − ∫ 0 t σ ( s ) d s T(t)=e^{-\int^t_0\sigma(s)ds} T(t)=e−∫0tσ(s)ds
实际计算是离散化的数据,把光线[0,s]划分为N个等间距区间 [ T n → T n + 1 ] [T_n\rightarrow T_{n+1}] [Tn→Tn+1], n = 0 , 1 , 2 , ⋯ , N n=0,1,2,\cdots,N n=0,1,2,⋯,N,间隔长度为 δ n \delta_n δn。假设区间内密度 σ n \sigma_n σn和颜色 C n C_n Cn固定。
C ^ ( r ) = ∑ i = 1 N T i ( 1 − e − σ i δ i ) C i , T i = e − ∑ j = 1 i − 1 σ j δ j \hat C(r)=\sum^N_{i=1}T_i(1-e^{-\sigma_i\delta_i})C_i,T_i=e^{-\sum^{i-1}_{j=1}\sigma_j\delta_j} C^(r)=∑i=1NTi(1−e−σiδi)Ci,Ti=e−∑j=1i−1σjδj
其中, δ i = t i + 1 − t i \delta_i=t_{i+1}-t_i δi=ti+1−ti表示相邻采样点之间的距离。
网络训练策略
每条射线上采样3D点,采用均匀采样的方式,大部分区域是空的或者被遮挡,对最终的颜色没有贡献。
所以,NeRF采用了从粗到细(coarse to fine)的层级采样策略,同时优化coarse网络和fine网络。利用coarse网络得到的颜色、 σ \sigma σ值确定哪些位置贡献大,然后在这些贡献大的区域再度进行更精细的采样得到128个3d点,64+128=192个3d点输入到fine网络。
coarse网络层次采样 N c N_c Nc个位置,计算每个位置的权重 w ^ i \hat w_i w^i:
C ( r ) = ∑ i = 1 N c T i ( 1 − e x p ( − σ i δ i ) ) c i → C ( r ) = ∑ i = 1 N c w i c i , w h e r e w i = T i ( 1 − e x p ( − σ i δ i ) ) C(r)=\sum^{N_c}_{i=1}T_i(1-exp(-\sigma_i\delta_i))c_i\rightarrow C(r)=\sum^{N_c}_{i=1}w_ic_i,where\ w_i=T_i(1-exp(-\sigma_i\delta_i)) C(r)=∑i=1NcTi(1−exp(−σiδi))ci→C(r)=∑i=1Ncwici,where wi=Ti(1−exp(−σiδi))
所以 C ( r ) = ∑ i = 1 N c w i c i C(r)=\sum^{N_c}_{i=1}w_ic_i C(r)=∑i=1Ncwici表示结果为各个采样区域的色彩进行加权求和。把所有位置的权重 w i w_i wi进行归一化,得到概率密度函数,表示了不同区域的重要性。
w ^ i = w i / ∑ j = 1 N c w j \hat w_i=w_i/\sum^{N_c}_{j=1}w_j w^i=wi/∑j=1Ncwj
所以,在概率大的地方多采样,这些位置的值和颜色对于更新更加重要,依据权重 w ^ i \hat w_i w^i采样 N f N_f Nf个位置。因此fine网络的单条视线上采样总数为 N c + N f N_c+N_f Nc+Nf。
损失函数实际上要同时优化coarse网络和fine网络,因此包括了两个网络的均方误差损失。
L = ∑ r ∈ R ( ∣ ∣ C ^ c ( r ) − C ( r ) ∣ ∣ 2 2 + ∣ ∣ C ^ f ( r ) − C ( r ) ∣ ∣ 2 2 ) \mathcal L=\sum_{r\in \mathcal R}(||\hat C_c(r)-C(r)||^2_2+||\hat C_f(r)-C(r)||^2_2) L=∑r∈R(∣∣C^c(r)−C(r)∣∣22+∣∣C^f(r)−C(r)∣∣22)
其中 r r r表示一条采样光线, R \mathcal R R表示所有采样的光线。
总结
NeRF后续
NeRF++
NeRF对于没有边界的场景存在分辨率的问题,NeRF采样的点放在前景部分,前景清晰,背景模糊;放在整个场景中,前景背景都变模糊。
NeRF++分开前景和背景
NeRF具有组合的特性,用两个NeRF模型,一个表示前景,一个表示背景。
对于360 outward-facing的场景,其和360 inward-facing呈现出对称的性质,因此可以把单位球域外的无边界区域映射到有边界单位cube里。距离近的场景压缩小,远距离场景压缩多,分辨率低。
把无边界区域映射后,就可以利用普通的NeRF同时建模前景和背景。
NeRF++把前景和背景分开了
无边界区域的背景有更多细节
Mip-NeRF
混叠现象
混叠现象:根据奈奎斯特准则,当采样频率低于2倍的信号频率时,会导致原本的高频信号被采样成低频信号,和低频部分的信号叠加在一起。这种频谱的重叠导致的失真称为混叠,也就是高频信号被混叠成了低频信号。
图中 f m a x f_{max} fmax为采样频率的1/2,即 f s 2 \frac{f_s}{2} 2fs。
当原始信号频率> f s 2 \frac{f_s}{2} 2fs 时,会导致将高出来的频率部分高频信号采样成低频,原始的低频很好的保留了,而高频这部分采样成了低频,和原始的低频信号有了混合,这就是混叠。(出现右图的Aliased现象)
图像中的混叠现象:
1、锯齿
2、摩尔纹
通常发生在一些有高频表面的物体上,采样后造成的特定奇怪的低频纹路,同样的输入信号,由于采样的位置不同或角度不同,形成的纹路是不规则的。
**抗混叠:**增加采样频率、使用低通滤波器。
NeRF的渲染模型存在缺陷,对于观察场景的距离不敏感,忽略了每条视线看到的体积的形状和大小,当观察场景距离变化时,可能导致过度模糊和混叠。
方式一:对每个像素进行超采样,采样多条视线进行渲染。
Mip-NeRF:
受到计算机图形学渲染管线中抗锯齿技术——mipmapping技术启发,用mipmap表示一组离散的不同尺度采样的信号,根据射线相交到的三维几何图形,选择mipmap中分辨率最接近的信号(可以对mimap中不同分辨率的信号进行邻近的差值得到所需的分辨率信号)。
尺度感知的架构。
视线变视锥。
集成位置编码(IPE,Integrated Positional Encoding),用三维高斯逼近圆锥台。
NeRF面临着抗锯齿的问题(anti-aliasing issue),对于低分辨率图像的渲染出现锯齿/噪点。如果训练时采用多分辨率尺度训练,会降低高分辨率的质量。Mip-NeRF对低分辨率模型进行低通滤波降采样。
NeRF采用位置编码(Fourier features)提高了输入特征的频率,低分辨率下,相当于降低了采用率,可能不满足奈奎斯特采样定理。因此Mip-NeRF对信号本身进行一个低通滤波,对Fourier features进行低通滤波过滤。图像的采样频率是像素大小,一个像素对映到三维空间为一个锥体(cone),低通滤波器的size由cone的size控制。
视锥追踪和位置编码(Cone Tracing and Positional Encoding)
从相机光心 o o o沿着方向 d d d投射一个圆锥,圆锥顶点是 o o o,成像平面与圆锥的交平面圆半径为 r ˙ \dot r r˙, r ˙ \dot r r˙的值设置为世界坐标系下像素宽度的 2 12 \frac{2}{\sqrt{12}} 122。
若 x x x位于由相机位置 o o o、视线方向 d d d、圆锥台半径 r ˙ \dot r r˙、圆锥台深度区间 [ t 0 , t 1 ] [t_0,t_1] [t0,t1]定义的圆锥台中,则 F ( x , ⋅ ) = 1 F(x,\cdot)=1 F(x,⋅)=1( F F F是一个示性函数,当 x x x位于投射的圆锥台内部,则值为1)。
KaTeX parse error: Undefined control sequence: \and at position 83: …d||_2}<t_1\big)\̲a̲n̲d̲\big(\frac{d^T(…
d T ( x − o ) ∣ ∣ d ∣ ∣ 2 2 \frac{d^T(x-o)}{||d||^2_2} ∣∣d∣∣22dT(x−o)表示向量 x − o x-o x−o在 d d d上的投影大小位于 [ t 0 , t 1 ] [t_0,t_1] [t0,t1]之间。
d T ( x − o ) ∣ ∣ d ∣ ∣ 2 ∣ ∣ x − o ∣ ∣ 2 > 1 1 + ( r ˙ ∣ ∣ d ∣ ∣ 2 ) 2 \frac{d^T(x-o)}{||d||_2||x-o||_2}>\frac{1}{\sqrt{1+(\frac{\dot r}{||d||_2})^2}} ∣∣d∣∣2∣∣x−o∣∣2dT(x−o)>1+(∣∣d∣∣2r˙)21表示 c o s θ ′ = d T ( x − o ) ∣ ∣ d ∣ ∣ 2 ∣ ∣ x − o ∣ ∣ 2 > c o s θ = 1 1 + ( r ˙ ∣ ∣ d ∣ ∣ 2 ) 2 cos\theta'=\frac{d^T(x-o)}{||d||_2||x-o||_2}>cos\theta=\frac{1}{\sqrt{1+(\frac{\dot r}{||d||_2})^2}} cosθ′=∣∣d∣∣2∣∣x−o∣∣2dT(x−o)>cosθ=1+(∣∣d∣∣2r˙)21
圆锥台位置编码——集成位置编码
圆锥台的位置编码定义为:
γ ∗ ( o , d , r ˙ , t 0 , t 1 ) = ∫ γ ( x ) F ( x , o , d , r ˙ , t 0 , t 1 ) d x ∫ F ( x , o , d , r ˙ , t 0 , t 1 ) d x = E ( γ ( x ) ) \gamma^*(o,d,\dot r,t_0,t_1)=\frac{\int\gamma(x)F(x,o,d,\dot r,t_0,t_1)dx}{\int F(x,o,d,\dot r,t_0,t_1)dx}=E(\gamma(x)) γ∗(o,d,r˙,t0,t1)=∫F(x,o,d,r˙,t0,t1)dx∫γ(x)F(x,o,d,r˙,t0,t1)dx=E(γ(x))
分子为圆台内位置编码的和,分母为圆台内总个数,这个式子对圆台内所有位置积分,没有封闭解。因此,利用多元高斯对圆锥截锥进行近似,称为集成位置编码((IPE,Integrated Positional Encoding)。
用一个三维高斯球逼近圆台,让三维高斯球里包含的 x x x个数尽量与圆台内的重叠,三维高斯球用下式表示:
μ t = t μ + 2 t μ t δ 2 3 t μ 2 + t δ 2 , σ t 2 = t δ 2 3 − 4 t δ 4 ( 12 t μ 2 − t δ 2 ) 15 ( 3 t μ 2 + t δ 2 ) 2 , σ r 2 = r ˙ 2 ( t μ 2 4 + 5 t δ 2 12 − 4 t δ 4 15 ( 3 t μ 2 + t δ 2 ) ) \mu_t=t_{\mu}+\frac{2t_{\mu}t_{\delta}^2}{3t_{\mu}^2+t_{\delta}^2},\ \sigma_t^2=\frac{t_{\delta}^2}{3}-\frac{4t_{\delta}^4(12t_{\mu}^2-t_{\delta}^2)}{15(3t_{\mu}^2+t_{\delta}^2)^2},\ \sigma_r^2=\dot r^2\big(\frac{t_{\mu}^2}{4}+\frac{5t_{\delta}^2}{12}-\frac{4t_{\delta}^4}{15(3t_{\mu}^2+t_{\delta}^2)}\big) μt=tμ+3tμ2+tδ22tμtδ2, σt2=3tδ2−15(3tμ2+tδ2)24tδ4(12tμ2−tδ2), σr2=r˙2(4tμ2+125tδ2−15(3tμ2+tδ2)4tδ4)
μ t \mu_t μt表示对应圆锥截台到相机的平均距离, σ t 2 \sigma_t^2 σt2为沿光线方向的距离方差, σ r 2 \sigma_r^2 σr2为垂直于光线方向的距离方差。其中, t μ = t 0 + t 1 2 t_{\mu}=\frac{t_0+t_1}{2} tμ=2t0+t1, t δ = t 1 − t 0 2 t_{\delta}=\frac{t_1-t_0}{2} tδ=2t1−t0, r ˙ \dot r r˙为世界坐标下像素宽度的 1 3 \frac{1}{\sqrt{3}} 31。
为什么把 r ˙ \dot r r˙设置为半径为 1 3 \frac{1}{\sqrt{3}} 31倍像素的大小?
圆的面积为 π r 2 = 1 → r = 1 π ≈ 1 3 \pi r^2=1\rightarrow r=\frac{1}{\sqrt{\pi}}\approx\frac{1}{\sqrt{3}} πr2=1→r=π1≈31
将三维高斯球的坐标转换到世界坐标系:
μ = o + μ t d , ∑ = σ t 2 ( d d T ) + σ r 2 ( I − d d T ∣ ∣ d ∣ ∣ 2 2 ) \mu=o+\mu_td,\ \sum=\sigma_t^2(dd^T)+\sigma_r^2(I-\frac{dd^T}{||d||^2_2}) μ=o+μtd, ∑=σt2(ddT)+σr2(I−∣∣d∣∣22ddT)
其中 o o o表示相机位置, d d d表示视线方向。
接下来推导IPE集成位置编码:
NeRF的位置编码: γ ( q ) = ( s i n ( 2 0 π q ) , c o s ( 2 0 π q ) , ⋯ , s i n ( 2 L − 1 π q ) , c o s ( 2 L − 1 π q ) ) \gamma(q)=\big(sin(2^0\pi q),cos(2^0\pi q),\cdots,sin(2^{L-1}\pi q),cos(2^{L-1}\pi q)\big) γ(q)=(sin(20πq),cos(20πq),⋯,sin(2L−1πq),cos(2L−1πq))
将PE写为矩阵形式:
KaTeX parse error: {align} can be used only in display mode.
与原位置编码的差异:
1、省略了常数 π \pi π
2、原NeRF是以sin、cos、sin、cos……的方式排列,矩阵形式将sin放在一起,cos放在一起。
所以:
E ( γ ( x ) ) = [ E ( s i n ( P x ) ) E ( c o s ( P x ) ) ] = [ E ( s i n ( p 1 ) ) ⋯ E ( s i n ( p 3 L ) ) E ( c o s ( p 1 ) ) ⋯ E ( c o s ( p 3 L ) ) ] E(\gamma(x))=\left[\begin{matrix}E(sin(Px))\\E(cos(Px))\end{matrix}\right]=\left[\begin{matrix}E(sin(p_1))\\\cdots\\E(sin(p_{3L}))\\E(cos(p_1))\\\cdots\\E(cos(p_{3L}))\end{matrix}\right] E(γ(x))=[E(sin(Px))E(cos(Px))]= E(sin(p1))⋯E(sin(p3L))E(cos(p1))⋯E(cos(p3L))
当 p p p服从高斯分布时,计算 E ( s i n ( p ) ) E(sin(p)) E(sin(p))和 E ( c o s ( p ) ) E(cos(p)) E(cos(p)):
E p ∼ N ( μ , σ 2 ) [ s i n ( p ) ] = s i n ( μ ) e x p ( − 1 2 σ 2 ) E_{p\sim\mathcal N(\mu,\sigma^2)}[sin(p)]=sin(\mu)exp(-\frac{1}{2}\sigma^2) Ep∼N(μ,σ2)[sin(p)]=sin(μ)exp(−21σ2)
E p ∼ N ( μ , σ 2 ) [ c o s ( p ) ] = c o s ( μ ) e x p ( − 1 2 σ 2 ) E_{p\sim\mathcal N(\mu,\sigma^2)}[cos(p)]=cos(\mu)exp(-\frac{1}{2}\sigma^2) Ep∼N(μ,σ2)[cos(p)]=cos(μ)exp(−21σ2)
将三维高斯球坐标进行编码:
μ = o + μ t d , ∑ = σ t 2 ( d d T ) + σ r 2 ( I − d d T ∣ ∣ d ∣ ∣ 2 2 ) → μ γ = P μ , ∑ γ = P ∑ P T \mu=o+\mu_td,\ \sum=\sigma_t^2(dd^T)+\sigma_r^2(I-\frac{dd^T}{||d||^2_2})\rightarrow \mu_{\gamma}=P\mu,\ \sum_{\gamma}=P\sum P^T μ=o+μtd, ∑=σt2(ddT)+σr2(I−∣∣d∣∣22ddT)→μγ=Pμ, ∑γ=P∑PT
对 μ γ , ∑ γ \mu_{\gamma},\sum_{\gamma} μγ,∑γ进行编码的值:
x ∼ N ( μ , ∑ ) , P x ∼ N ( μ γ , ∑ γ ) x\sim \mathcal N(\mu,\sum),\ Px\sim\mathcal N(\mu_{\gamma},\sum_{\gamma}) x∼N(μ,∑), Px∼N(μγ,∑γ)
γ ∗ ( μ , ∑ ) = E P x ∼ N ( μ γ , ∑ γ ) [ γ ( x ) ] = [ s i n ( μ γ ) ∘ e x p ( − 1 2 d i a g ( ∑ γ ) ) c o s ( μ γ ) ∘ e x p ( − 1 2 d i a g ( ∑ γ ) ) ] \gamma^*(\mu,\sum)=E_{Px\sim\mathcal N(\mu_{\gamma},\sum_{\gamma})}[\gamma(x)]=\left[\begin{matrix}sin(\mu_{\gamma})\circ exp(-\frac{1}{2}diag(\sum_{\gamma}))\\cos(\mu_{\gamma})\circ exp(-\frac{1}{2}diag(\sum_{\gamma}))\end{matrix}\right] γ∗(μ,∑)=EPx∼N(μγ,∑γ)[γ(x)]=[sin(μγ)∘exp(−21diag(∑γ))cos(μγ)∘exp(−21diag(∑γ))]
集成位置编码 γ ( x ) \gamma(x) γ(x)的方差计算公式:
d i a g ( ∑ γ ) = [ d i a g ( ∑ ) , 4 d i a g ( ∑ ) , ⋯ , 4 L − 1 d i a g ( ∑ ) ] T diag(\sum_{\gamma})=[diag(\sum),4diag(\sum),\cdots,4^{L-1}diag(\sum)]^T diag(∑γ)=[diag(∑),4diag(∑),⋯,4L−1diag(∑)]T
集成位置编码vs位置编码
- NeRF:PE位置编码,从图中可以看到,高频位置总是有值,所以会产生混叠;
- Mip-NeRF:集成位置编码圆锥台越小,越近似到一个点上。越小的时候,积分区域就越小,表明累加的圆锥台越小,就越容易落到一个点上,高频区域就会有信号值;距离远的时候,平均的数量就变多了,本来差别很大的,就给拉平了,导致更不容易学到高频信息,越高频越没有值,这样就可以自适应的调节了。
总结:NeRF vs Mip-NeRF
位置编码(NeRF) vs 集成位置编码(Mip-NeRF)
采样方式,每个像素对应一条视线,视线上采样点,远处和近处看到的都是一样的 vs 每个像素对应一个视锥,随远近变化能够感知不同尺度
网络数量,NeRF双网络,分为Coarse和Fine两个网络 vs Mip-NeRF单个网络。
Instant-NGP
Mip-NeRF是对NeRF的性能进行提升,NeRF发出的是视线,无法感知尺度变化,Mip-NeRF发出视锥,能够感知尺度变化,提升了NeRF的抗锯齿能力。而NeRF的一个最大的缺点就是速度太慢,Instant-NGP正是在速度上优化NeRF。
球谐函数
一组代表球面上不同位置的值的基函数。只和 θ , ϕ \theta,\phi θ,ϕ有关,与 r r r无关,参数量为阶数的平方。
球谐函数可视化
r
=
f
(
θ
,
ϕ
)
r=f(\theta,\phi)
r=f(θ,ϕ)
球谐函数表达颜色:
( r , g , b ) = ( f r ( θ , ϕ ) , f g ( θ , ϕ ) , f b ( θ , ϕ ) ) (r,g,b)=(f_r(\theta,\phi),f_g(\theta,\phi),f_b(\theta,\phi)) (r,g,b)=(fr(θ,ϕ),fg(θ,ϕ),fb(θ,ϕ))
球谐函数在一定程度上能弱化高频信息,是一种有损压缩,并且能将离散的信息变为连续信息,进行梯度计算和迭代。
Instant-NGP中使用3阶球谐函数(16维)编码视线方向。
多分辨率哈希编码
Instant-NGP和NeRF的流程基本相同,仅编码部分不同。Instant-NGP认为NeRF的编码过于低效,需要大量的全连接层选择编码的哪几维代表当前点的坐标值。于是Instant-NGP设计了一种多分辨率哈希编码,将空间划分成一个个的三维网格,存储这些网格顶点位置的位置编码。给定任何一个位置的点 x x x,它的位置编码由它所在网格顶点的四个位置编码通过线性插值来得到。距离哪个顶点越近,这个顶点位置编码对它的贡献就越大。这样空间中任何一个点的位置编码就都可以拿到了。
1、将空间划分为多分辨率网格
以二维图像为例,2∗2分辨率网格,图像会有9个顶点;3*3分辨率网格,图像会有16个顶点。划分的网格越细,则表达的内容越准确,不会被周围的内容所平均。大网格倾向于这个整体区域怎么样,越小的网格越反应局部小区域里的信息,表达就会越细致。对于处于低频的点,尽量用大的表达就行,而高频区域的点用小的表达。拼在一起既能表现这个点的高频信息,又能表现这个点的低频信息。
在粗的网格里,四个顶点是在描述关注的这一大块信息。如果这一大块里少量高频大部分低频,那么这四个顶点会倾向于学习低频信息。但假设一个点正好处于低频区域里的高频区域的话,编码就不够准确了。比如上图中的点,在蓝色 2 × 2 2\times2 2×2分辨率内,该区域以低频主导,但在红色 3 × 3 3\times3 3×3分辨率时,这块区域是以高频主导。
分辨率确定:将场景空间划分为 L ( l = 0 , 1 , ⋯ , L − 1 ) L(l=0,1,\cdots,L-1) L(l=0,1,⋯,L−1)种不同分辨率的网格,每种分辨率 N l N_l Nl通过尺度因子是 b b b的等比数列来计算:
N l : = ⌊ N m i n ⋅ b l ⌋ N_l:=\lfloor N_{min}\cdot b^l\rfloor Nl:=⌊Nmin⋅bl⌋
b : = e x p ( l n N m a x − l n N m i n L − 1 ) = ( N m a x N m i n ) 1 L − 1 b:=exp(\frac{lnN_{max}-lnN_{min}}{L-1})=(\frac{N_{max}}{N_{min}})^{\frac{1}{L-1}} b:=exp(L−1lnNmax−lnNmin)=(NminNmax)L−11
N m i n N_{min} Nmin和 N m a x N_{max} Nmax分别表示最粗与最细分辨率时场景空间沿一个方向划分的网格数, b b b表示相邻两个分辨率之间的沿一个方向的网格数的比值。
2、每个体素网格顶点的位置编码通过网络训练得到,初始时随机初始化。
3、给定任意一个点 x x x,找到其落入的网格,通过网格顶点特征码线性插值获得位置编码。
使用哈希表存储网格的位置编码,每个分辨率网格对应一张哈希表。如果不使用哈希表,存储每个网格的位置编码那么存储量是 N × N × N = N 3 N\times N\times N=N^3 N×N×N=N3级,需要大量的存储!!!
要算一个顶点的位置编码,首先要将周围四个顶点的位置编码取出来。现在拿到的是坐标 x x x,但想要的是特征码。这里需要使用哈希函数,将每个坐标映射成了图中的0, 1, 2这样的数,然后从这些数里将特征编码取出来。哈希表在这里的作用就是查找每个顶点的特征编码。
如果直接使用坐标的 ( x , y , z ) (x,y,z) (x,y,z)作为哈希值,因为其不是一个整数,所以用作哈希并不容易。所以用该分辨率下体素网格顶点的索引作为哈希值。
获取网格顶点的算法如下,输入量为:采样点的坐标、采样点所在网格的边界、网格数量。
得到采样点所在网格的索引坐标后,到对应分辨率的哈希表查找位置编码,把不同分辨率的位置编码拼接起来。
哈希计算:KaTeX parse error: Limit controls must follow a math operator at position 29: …t{d}{\bigoplus}\̲l̲i̲m̲i̲t̲s̲_{i=1}x_i\pi_i)…
其中, ⨁ \bigoplus ⨁表示按位异或操作, π 1 = 1 , π 2 = 2654435761 , π 3 = 805458861 \pi_1=1,\pi_2=2654435761,\pi_3=805458861 π1=1,π2=2654435761,π3=805458861
T T T表示对应分辨率哈希表的容量。
4、组合多个分辨率的特征码(位置编码),送入MLP预测体密度与颜色值。
Instand-NGP网络结构
L = 16 L=16 L=16种分辨率网格,位置编码维度为 F = 2 F=2 F=2。 N N N为batchsize。
NeRF把三维内容的形状和外观信息混合在一起,一些显性的三维内容如点云、mesh、体素等,这些三维内容表示把形状和外观信息进行了解耦合,能够使得三维内容的编辑更易进行。NeRF这种耦合的表示,难以进行三维内容的编辑。(三维内容编辑例如更改光照、颜色、纹理、把某个位置的猫换成狗等)
数据集类型
LLFF (Local Light Field Fusion)
-
LLFF 数据集来源于真实世界的场景。
-
使用实际拍摄的多张图片(通常通过手机或相机拍摄),从不同视角对同一场景进行捕获。
-
通常包括相机的位姿(pose)和内参(intrinsics),用于从图片重建场景的3D结构。
-
适用于 NeRF 中的真实场景建模。
-
由于这些图片是通过相机拍摄的,包含真实世界的光线与材质属性,因此建模更接近自然场景。
-
LLFF 格式数据通常包括:
- 图像文件夹(包含多张视角图片)。
- 相机参数文件(如 JSON 或 TXT 文件)。
Blender
-
blender 数据集通常是使用 3D 渲染软件(如 Blender)生成的合成数据。
-
数据集包含高质量的 3D 模型和视角精确的渲染图像。
-
每个图像都有对应的相机位姿和内参。
-
用于验证 NeRF 的性能和精度,因为生成的合成数据非常干净,没有噪声或复杂的光影。
-
特别适合研究 NeRF 在合成环境中的表现。
-
数据集内容:
- 渲染图片(RGB)。
- 相机位姿文件(transforms.json),定义每个图片对应的视角。
-
NeRF 论文中使用的经典场景如 Chair, Lego, Ficus 都来自 Blender 渲染。
LINEMOD
-
LINEMOD 是一个广泛使用的物体识别数据集,主要用于 6D 位姿估计任务。
-
包含真实世界中单个目标物体的 RGB-D 图像数据(RGB 图像 + 深度图)。
-
数据集中每个图像都有对应的物体位姿信息。
-
用于场景中单一物体的精确建模和渲染。
-
常用于验证 NeRF 对物体的局部重建能力。
-
数据内容:
- RGB 图像。
- 深度图(Depth)。
- 位姿文件。
- LINEMOD 的场景通常包括桌面上的物体(如饮料罐、杯子等)。
DeepVoxels
-
DeepVoxels 数据集是基于合成数据的 3D 场景数据集。
-
场景通常由一个 3D 模型组成,使用固定路径渲染多张视角图片。
-
数据集的名称来源于使用体素(Voxels)作为主要表示方法的早期方法。
-
用于基于体素的 3D 场景建模,适合研究体素方法与 NeRF 方法的性能对比。
-
数据集结构化且干净,适合初步实验。
-
数据内容:
- 多视角的 RGB 图像。
- 对应的相机位姿和内参。
- 深度图或体素网格。
-
常见的场景如房间的内部或建筑模型。
数据类型 | 数据来源 | 场景复杂度 | 数据质量 | 主要应用 |
---|---|---|---|---|
LLFF | 真实场景 | 高 | 噪声多 | 真实世界的场景建模 |
Blender | 合成数据 | 中 | 高 | 高精度建模和算法验证 |
LINEMOD | 真实物体识别 | 低 | 中 | 单个物体的位姿估计与重建 |
DeepVoxels | 合成数据 | 中 | 高 | 基于体素的场景建模与对比实验 |
NeRF-pytorch一点注释
视角场(FOV,Field of View)
camera_angle_x = float(meta['camera_angle_x'])
focal = .5 * W / np.tan(.5 * camera_angle_x)
trans_t = lambda t : torch.Tensor([
[1,0,0,0],
[0,1,0,0],
[0,0,1,t],
[0,0,0,1]]).float()
rot_phi = lambda phi : torch.Tensor([
[1,0,0,0],
[0,np.cos(phi),-np.sin(phi),0],
[0,np.sin(phi), np.cos(phi),0],
[0,0,0,1]]).float()
rot_theta = lambda th : torch.Tensor([
[np.cos(th),0,-np.sin(th),0],
[0,1,0,0],
[np.sin(th),0, np.cos(th),0],
[0,0,0,1]]).float()
def pose_spherical(theta, phi, radius):
#相机沿着z轴平移radius,x3=x3+radius
c2w = trans_t(radius)
#相机再绕x轴旋转phi角
c2w = rot_phi(phi/180.*np.pi) @ c2w
#相机再绕y轴旋转theta角
c2w = rot_theta(theta/180.*np.pi) @ c2w
#将坐标系调整到NeRF的惯用坐标系,x轴取负,y轴变z轴,z轴变y轴。
c2w = torch.Tensor(np.array([[-1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]])) @ c2w
return c2w
if args.white_bkgd:
#(1. - images[...,-1:]) 是背景颜色的贡献。如果图像像素完全透明(alpha = 0),那么 RGB 值就完全由 (1. - alpha)(背景颜色的贡献)决定;如果图像像素完全不透明(alpha = 1),则完全由 RGB 通道决定。
images = images[...,:3]*images[...,-1:] + (1.-images[...,-1:])
else:
images = images[...,:3]
# 数据集里的pose是c2w?
# 为什么c2w[:3,-1]是光心
# Ray helpers
def get_rays(H, W, K, c2w):
i, j = torch.meshgrid(torch.linspace(0, W-1, W), torch.linspace(0, H-1, H)) # pytorch's meshgrid has indexing='ij'
i = i.t()
j = j.t()
dirs = torch.stack([(i-K[0][2])/K[0][0], -(j-K[1][2])/K[1][1], -torch.ones_like(i)], -1)
# Rotate ray directions from camera frame to the world frame
rays_d = torch.sum(dirs[..., np.newaxis, :] * c2w[:3,:3], -1) # dot product, equals to: [c2w.dot(dir) for dir in dirs]
# Translate camera frame's origin to the world frame. It is the origin of all rays.
rays_o = c2w[:3,-1].expand(rays_d.shape)
return rays_o, rays_d
# 数据集里的pose是c2w?
# 为什么c2w[:3,-1]是光心
# Ray helpers
def get_rays(H, W, K, c2w):
i, j = torch.meshgrid(torch.linspace(0, W-1, W), torch.linspace(0, H-1, H)) # pytorch's meshgrid has indexing='ij'
i = i.t()
j = j.t()
dirs = torch.stack([(i-K[0][2])/K[0][0], -(j-K[1][2])/K[1][1], -torch.ones_like(i)], -1)
# Rotate ray directions from camera frame to the world frame
rays_d = torch.sum(dirs[..., np.newaxis, :] * c2w[:3,:3], -1) # dot product, equals to: [c2w.dot(dir) for dir in dirs]
# Translate camera frame's origin to the world frame. It is the origin of all rays.
rays_o = c2w[:3,-1].expand(rays_d.shape)
return rays_o, rays_d
参考链接
NeRF 其一:NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_深蓝学院 nerf-优快云博客
NeRF 其二:Mip-NeRF_mipnerf-优快云博客
NeRF 其三:Instant-NGP_instant-ngp网络结构-优快云博客
浅谈3D隐式表示(SDF,Occupancy field,NeRF)-优快云博客
【较真系列】讲人话-NeRF全解(原理+代码+公式)_哔哩哔哩_bilibili