nerf_factory源码笔记(三)

nerf_factory源码笔记(三)

这一节主要讲nerf中的位置编码以及体渲染函数两部分:

  1. 位置编码

    ​ 位置编码是nerf取得成功的原因之一,因为研究表明神经网络中更加倾向于学习到低频特征,而难以学习到高频特征,进而丢失较多的细节。因此nerf对采样后输入的点和视角方向都进行位置编码。

    ​ 根据原文所述,编码公式为:
    γ ( 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 ) ) \gamma(p)=(sin(2^0\pi p),cos(2^0\pi p),...sin(2^{L-1}\pi p),cos(2^{L-1}\pi p)) γ(p)=(sin(20πp),cos(20πp),...sin(2L1πp),cos(2L1πp))
    其中:

    对于采样后的点 L = 10 L=10 L=10,输入的视角方向 L = 4 L=4 L=4

    那假设x的维度是 ( N , 3 ) (N,3) (N,3),那么编码后应该是 ( N , 3 ∗ 2 ∗ 10 + 3 ) − ( N , 63 ) (N,3*2*10+3)-(N,63) (N,3210+3)(N,63),与前面所说的输入维度相对应。

    同理对于 d d d应该是 ( N , 3 ∗ 2 ∗ 4 + 3 ) − ( N , 27 ) (N,3*2*4+3)-(N,27) (N,324+3)(N,27)

    具体代码如下:

    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]
    

  2. 体渲染函数:

    首先我们回顾论文中给出的公式
    C c ( r ) = ∑ i = 1 N c ω i c i , ω i = T i ( 1 − e x p ( − δ i σ i ) ) C_c(r)=\sum_{i=1}^{N_c}\omega_ic_i,\quad \omega_i=T_i(1-exp(-\delta_i\sigma_i)) Cc(r)=i=1Ncωici,ωi=Ti(1exp(δiσi))

    T i = e x p ( − ∑ j = 1 i − 1 σ j δ j ) T_i=exp(-\sum^{i-1}_{j=1}\sigma_j\delta_j) Ti=exp(j=1i1σjδj)

    具体函数为:

    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-exp(-\sigma*\delta)$为每个点的透明度,那么`1-alpha`则为$exp(-\sigma*\delta)$。

根据指数函数的性质:
T i = e x p ( − ∑ j = 1 i − 1 σ j δ j ) = ∏ j = 1 i − 1 e x p ( − σ j δ j ) = ∏ j = 1 i − 1 ( 1 − a l p h a ) T_i=exp(-\sum^{i-1}_{j=1}\sigma_j\delta_j)=\prod_{j=1}^{i-1}exp(-\sigma_j\delta_j)=\prod_{j=1}^{i-1}(1-alpha) Ti=exp(j=1i1σjδj)=j=1i1exp(σjδj)=j=1i1(1alpha)
所以

    accum_prod = torch.cat(
        [
            torch.ones_like(alpha[..., :1]),
            torch.cumprod(1.0 - alpha[..., :-1] + eps, dim=-1),
        ],
        dim=-1,
    ) 

通过torch.cumprod函数进行累成求积得到 T i T_i Ti

weights = alpha * accum_prod得到权重 ω i \omega_i ωi

然后进行每条光线上的所有采样点求和得到最终的rgb

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值