本文最后更新于70 天前,其中的信息可能已经过时,如有错误请发送邮件到chengyulong@csu.edu.cn
这一节主要讲nerf中的位置编码以及体渲染函数两部分:
-
位置编码
位置编码是nerf取得成功的原因之一,因为研究表明神经网络中更加倾向于学习到低频特征,而难以学习到高频特征,进而丢失较多的细节。因此nerf对采样后输入的点和视角方向都进行位置编码。
根据原文所述,编码公式为:
其中:
对于采样后的点
,输入的视角方向 。 那假设x的维度是
,那么编码后应该是 ,与前面所说的输入维度相对应。 同理对于
应该是 。 具体代码如下:
def pos_enc(x, min_deg, max_deg): scales = torch.tensor([2**i for i in range(min_deg, max_deg)]).type_as(x) # 对于sin和cos中的各个维度的系数 #例如L=4的话scales=[1,2,4,8] xb = torch.reshape((x[..., None, :] * scales[:, None]), list(x.shape[:-1]) + [-1]) # 利用广播策略做矩阵元素对应相乘 # xb维度为(B,4,3) four_feat = torch.sin(torch.cat([xb, xb + 0.5 * np.pi], dim=-1)) # 分别求sin和cos,最后拼接在一起 four_feat维度为[B,4,6] return torch.cat([x] + [four_feat], dim=-1) # 最后和自身拼接得到最后结果 [B,27]
-
体渲染函数:
首先我们回顾论文中给出的公式:
具体函数为:
def volumetric_rendering(rgb, density, t_vals, dirs, white_bkgd): eps = 1e-10 dists = torch.cat( [ t_vals[..., 1:] - t_vals[..., :-1], torch.ones(t_vals[..., :1].shape, device=t_vals.device) * 1e10, ], dim=-1, ) dists = dists * torch.norm(dirs[..., None, :], dim=-1) # 计算光线采样点之间的实际距离(采样点距离与方向向量范数相乘) alpha = 1.0 - torch.exp(-density[..., 0] * dists) # 计算alpha即透明度 accum_prod = torch.cat( [ torch.ones_like(alpha[..., :1]), torch.cumprod(1.0 - alpha[..., :-1] + eps, dim=-1), ], dim=-1, ) # 计算T weights = alpha * accum_prod # 计算权重ω comp_rgb = (weights[..., None] * rgb).sum(dim=-2) # 把整个同一光线上的所有采样点合在一起,得到最终值 depth = (weights * t_vals).sum(dim=-1) # 计算最后的加权深度值 acc = weights.sum(dim=-1) # 每条光线的加权值 inv_eps = 1 / eps if white_bkgd: comp_rgb = comp_rgb + (1.0 - acc[..., None]) return comp_rgb, acc, weights
这其中alpha
代表1-alpha
则为
根据指数函数的性质:
所以:
accum_prod = torch.cat(
[
torch.ones_like(alpha[..., :1]),
torch.cumprod(1.0 - alpha[..., :-1] + eps, dim=-1),
],
dim=-1,
)
通过torch.cumprod
函数进行累成求积得到
由weights = alpha * accum_prod
得到权重
然后进行每条光线上的所有采样点求和得到最终的rgb
。