NeRF中的位置编码
最佳答案 问答题库538位专家为你答疑解惑
朴素NeRF中直接采用频率变换来做位置编码,为的是避免空间相邻采样点在MLP表示中的过平滑问题。比如位置(237, 332, 198)和位置(237,332,199)这两个点作为MLP的输入,MLP可能对个位不够敏感,导致输出过平滑的问题。例如:
由于缺乏位置编码,导致纹理相近区域的细节会丢失。
我们来看一下原文中关于Position Encoding的公式:
γ ( p ) = ( sin ( 2 0 π p ) , cos ( 2 0 π p ) , ⋯ , sin ( 2 L − 1 π p ) , cos ( 2 L − 1 π p ) ) (1) \gamma(p)=\left(\sin \left(2^0 \pi p\right), \cos \left(2^0 \pi p\right), \cdots, \sin \left(2^{L-1} \pi p\right), \cos \left(2^{L-1} \pi p\right)\right)\tag{1} γ(p)=(sin(20πp),cos(20πp),⋯,sin(2L−1πp),cos(2L−1πp))(1)
频率编码,很像傅里叶变换,代码如下:
import torchclass FreqEmbedder:def __init__(self, multires, include_input=True, input_dims=3, log_sampling=True):self.multires = multiresself.input_dims = input_dimsself.include_input = include_inputself.log_sampling = log_samplingself.periodic_fns = [torch.sin, torch.cos]self.embed_fns = Noneself.out_dim = Noneself.create_embedding_fn()def create_embedding_fn(self):embed_fns = []d = self.input_dimsout_dim = 0if self.include_input:embed_fns.append(lambda x: x)out_dim += dmax_freq = self.multires - 1N_freqs = self.multiresif self.log_sampling:freq_bands = 2. ** torch.linspace(0., max_freq, steps=N_freqs)else:freq_bands = torch.linspace(2. ** 0., 2. ** max_freq, steps=N_freqs)for freq in freq_bands:for p_fn in self.periodic_fns:embed_fns.append(lambda x, p_fn=p_fn, freq=freq: p_fn(x * freq))out_dim += dself.embed_fns = embed_fnsself.out_dim = out_dimdef embed(self, inputs):return torch.cat([fn(inputs) for fn in self.embed_fns], -1)
其中torch.sin
和torch.cos
实现的就是数学意义的功能,举个例子:
import torch
pi = 3.1415926
degree_30 = pi / 6 # 30 degreea = torch.Tensor([degree_30])
r = torch.sin(a)
print(r) # tensor([0.5000])
上面实验表明了 s i n ( 30 ° ) = 1 2 sin(30\degree)={1\over{2}} sin(30°)=21;
对于频率位置编码:假设一个位置的 x 0 = 30 x_0=30 x0=30,它相邻的位置是 x 1 = 31 x_1=31 x1=31,经过 r = s i n ( x ∗ 512 ) r=sin(x*512) r=sin(x∗512)编码以后, x 0 x_0 x0编码后的位置为 − 0.6842 -0.6842 −0.6842,而 x 1 x_1 x1编码后的位置为 0.6240 0.6240 0.6240。差距一目了然。
这里的512
则表示频率,如公式(1)所示的 2 L − 1 π 2^{L-1}\pi 2L−1π。
当然,也如公式(1)所示,我们并不以单一的频率来表示位置编码,比如我们挨个用 [ 1 , 2 , 4 , 8 , 16 , 32 , 64 , 128 , 256 , 512 ] [1,2,4,8,16,32,64,128,256,512] [1,2,4,8,16,32,64,128,256,512]这10种频率来表示编码位置(只需用公式 r = s i n ( p ∗ x ) r=sin(p*x) r=sin(p∗x),然后简单concat到一起)。这就完成了基本的位置编码。当然,我们还可以加入相位平移,把 c o s ( p ∗ x ) cos(p*x) cos(p∗x)的结果也concat到一起。
所以,对于一个位置 p ( x , y , z ) p(x,y,z) p(x,y,z),我们用10种频率(如 [ 1 , 2 , 4 , 8 , 16 , 32 , 64 , 128 , 256 , 512 ] [1,2,4,8,16,32,64,128,256,512] [1,2,4,8,16,32,64,128,256,512])来编码,每种频率采用两种相位(sin
和cos
),那编码后的位置应该有 3 × 10 × 2 = 60 3\times10\times2=60 3×10×2=60维来表示原始的三维坐标向量。通常,我们会把原始的三维坐标向量也concat到一起,那么就输出 60 + 3 = 63 60+3=63 60+3=63维,直接喂到MLP里去。
众所周知,NeRF除了位置 ( x , y , z ) (x,y,z) (x,y,z)输入外,还需要输入观测角度 ( θ , ϕ ) (\theta, \phi) (θ,ϕ)。观测角度可以用ray direction来表示,通常采用三维向量。也需要进行编码,也可以统称为位置编码。我们用同样的方法,但可以少用一些频率,比如我们用 [ 1 , 2 , 4 , 8 ] [1,2,4,8] [1,2,4,8]这四种频率来编码观测角度。编码后的维度也可计算出来: 3 × 4 × 2 + 3 = 27 3\times4\times2+3=27 3×4×2+3=27。
上图就是NeRF中MLP的输入顺序,图中并没有加原始位置,所以位置编码的维度为60,而方向编码的维度为24。输入阶段一目了然~
本文内容由本人亲自整理,如有疑问请留言交流~
99%的人还看了
相似问题
- vsto word 获取目录起始页和结束页,如目录起始位置为2、结束位置为3,返回2和3
- IP地理位置定位技术:保护网络安全的新利器
- WSL2安装ubuntu及修改安装位置,设置Ubuntu开机启动链接ssh服务
- ROS navigation栅格地图原点位置如何确定?
- 35. 搜索插入位置 --力扣 --JAVA
- 【实用技巧】更改ArduinoIDE默认库文件位置,解放系统盘,将Arduino15中的库文件移动到其他磁盘
- 76基于matlab的免疫算法求解配送中心选址问题,根据配送地址确定最佳配送中心地址位置。
- 小程序判断是否授权位置信息和手动授权
- 计算机毕业设计 基于SpringBoot的车辆网位置信息管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解
- 基于DOTween插件实现金币飞行到指定位置功能
猜你感兴趣
版权申明
本文"NeRF中的位置编码":http://eshow365.cn/6-14563-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!