nerf论文代码理解

        近年来,生成式AI(AGI)快速发展,各类生成式模型层出不群,但我更觉得具有物理意义的生成式AI将是未来革命性技术。因此也在抽空看看关于三维重建的知识,这篇文章就是记录我对nerf的理解。

一、论文理解

        首先,需要知道nerf的输入和输出是什么?

        做cv的朋友都肯定会觉得输入必然是图像,其实图像的输入只是为了后续的loss监督使用,网络的输入是5D向量,包含空间位置xyz和角度theta和phi。

        (1)是什么的空间位置xyz和角度theta和phi?

        粒子,nerf假设空间中的粒子是自发光的,所以图像的渲染是通过这些自发光粒子得到的。

        (2)网络的结构是什么样?

       (3)怎样得到这些粒子?

        根据像素位置转换到归一化相机坐标系,然后再转换到统一的世界坐标系,沿着射线在2~6的范围内均匀采样64个点作为粒子。世界坐标系的转换矩阵,也就是pose是通过SFM(运动结构恢复)得到的(经典的方法是COLMAP)

        (4e)xyz是怎么变为63维的? 

        通过傅里叶级数将输入的xyz变为高频信号,就像transfomer里面的位置编码一样。这样做的好处是能够区别像素间的差异,对于空间重建比较重要,相邻的两个像素,在空间中的距离也可能距离很大。论文中设的embedding的维度是10,所以10*2*3+3=63。其中2表示sin和cos,3是xyz。

        (5)在第5层为什么要加入原始信息?

        为了保持更为准确的空间位置。

        (6)输出的theta是什么?

        在对应的粒子采样点处光线被粒子阻碍的概率密度。

        (7)视角的输入维度为什么是3,又怎么变为27?

        实际网络的输入是向量,是单位向量表示方向。embedding的维度是4,所以3*2*4+3=27.

        (8)为什么视角输入只是在最后加入?

        概率密度只和位置相关,颜色和视角相关。

        (9)最后怎么样渲染得到图像?

        体渲染技术。

        (10)nerf的核心是什么?

        前处理得到网络输入,后处理体渲染技术,分层采样。

        (11)分层采样指的是?

        nerf的模型包括corse模型和fine模型,corse模型的采样是(2)中的均匀等距采样,fine模型会根据corse模型输出的weight,采样逆变换采样的方式重新采样得到128个粒子,使得采样更加关注空间中不为空的区域,128+64=192个粒子作为fine模型的输入。

        (12)还有其他细节嘛?

        为了节省显存,不会整张图对应的射线粒子都会一股脑进入网络,会随机采样1024个像素对应的射线粒子进入网络。

二、代码理解。

获取输入粒子的坐标和方向主要关注以下代码。

    def initialize(self):

        warange = torch.arange(self.width,  dtype=torch.float32, device=self.device)
        harange = torch.arange(self.height, dtype=torch.float32, device=self.device)
        y, x = torch.meshgrid(harange, warange)

        self.transformed_x = (x - self.width * 0.5) / self.focal
        self.transformed_y = (y - self.height * 0.5) / self.focal #normailization coord

        # pre center crop
        self.precrop_index = torch.arange(self.width * self.height).view(self.height, self.width)

        dH = int(self.height // 2 * self.precrop_frac)
        dW = int(self.width  // 2 * self.precrop_frac)
        self.precrop_index = self.precrop_index[
            self.height // 2 - dH:self.height // 2 + dH, 
            self.width  // 2 - dW:self.width  // 2 + dW
        ].reshape(-1)

        poses = torch.FloatTensor(self.poses, device=self.device) #torch.cuda.FloatTensor(self.poses, device=self.device)
        all_ray_dirs, all_ray_origins = [], []

        for i in range(len(self.images)):
            ray_dirs, ray_origins = self.make_rays(self.transformed_x, self.transformed_y, poses[i])
            all_ray_dirs.append(ray_dirs)
            all_ray_origins.append(ray_origins)

        self.all_ray_dirs    = torch.stack(all_ray_dirs, dim=0)
        self.all_ray_origins = torch.stack(all_ray_origins, dim=0)
        self.images          = torch.FloatTensor(self.images, device=self.device).view(self.num_image, -1, 3)#torch.cuda.FloatTensor(self.images, device=self.device).view(self.num_image, -1, 3)


    def make_rays(self, x, y, pose):

        # 100, 100, 3
        # 坐标系在-y,-z方向上
        directions    = torch.stack([x, -y, -torch.ones_like(x)], dim=-1)
        camera_matrix = pose[:3, :3]
        
        # 10000 x 3
        ray_dirs = directions.reshape(-1, 3) @ camera_matrix.T
        ray_origin = pose[:3, 3].view(1, 3).repeat(len(ray_dirs), 1)
        return ray_dirs, ray_origin # xyz theta phi

体渲染主要关注以下代码。

def predict_to_rgb(sigma, rgb, z_vals, raydirs, white_background=False):

    device         = sigma.device
    delta_prefix   = z_vals[..., 1:] - z_vals[..., :-1] #0.0635 sample point delta 
    delta_addition = torch.full((z_vals.size(0), 1), 1e10, device=device)
    delta = torch.cat([delta_prefix, delta_addition], dim=-1)
    delta = delta * torch.norm(raydirs[..., None, :], dim=-1)

    alpha    = 1.0 - torch.exp(-sigma * delta) # 1 - e**-sigma*delta
    exp_term = 1.0 - alpha
    epsilon  = 1e-10
    exp_addition = torch.ones(exp_term.size(0), 1, device=device)
    exp_term = torch.cat([exp_addition, exp_term + epsilon], dim=-1)
    transmittance = torch.cumprod(exp_term, axis=-1)[..., :-1] #(1-alpha0)

    weights       = alpha * transmittance
    rgb           = torch.sum(weights[..., None] * rgb, dim=-2)
    depth         = torch.sum(weights * z_vals, dim=-1)
    acc_map       = torch.sum(weights, -1)

    if white_background:
        rgb       = rgb + (1.0 - acc_map[..., None])
    return rgb, depth, acc_map, weights

体渲染的公式:

连续的:

离散的:

 

不想打字了,去上班挣窝囊费了.....

推荐代码:

shouxieai/nerf: nerf (github.com)

推荐教学视频(讲的真的很好):

【较真系列】讲人话-NeRF全解(原理+代码+公式)_哔哩哔哩_bilibili

推荐b站博主(有时候会去直播,但是看我直播的只有一个人机粉丝):

奈阿伊超

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超超爱AI

土豪请把你的零钱给我点

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值