nerf_factory源码笔记(四)
本文最后更新于48 天前,其中的信息可能已经过时,如有错误请发送邮件到chengyulong@csu.edu.cn


这一节是关于光线的生成以及光线的点的采样:

  1. 光线的生成

    首先看总体的数据处理代码

     def split_each(
            self,
            _images,
            _normals,
            render_poses,
            idx,
            dummy=True,
        ):
       # 这里是如果是fit or test render_poses=None,predict话_images和_normals是None
            images = None
            normals = None
            radii = None
            multloss = None
    
            if _images is not None:# 读取训练的数据:外参内参以及图片大小
                extrinsics_idx = self.extrinsics[idx]
                intrinsics_idx = self.intrinsics[idx]
                image_sizes_idx = self.image_sizes[idx]
            else:  # 进行推理的时读取渲染姿势
                extrinsics_idx = render_poses
                N_render = len(render_poses)
                intrinsics_idx = np.stack([self.intrinsics[0] for _ in range(N_render)])
                image_sizes_idx = np.stack([self.image_sizes[0] for _ in range(N_render)])
    
            _rays_o, _rays_d, _viewdirs, _radii, _multloss = batchified_get_rays(
                intrinsics_idx,
                extrinsics_idx,
                image_sizes_idx,
                self.use_pixel_centers,
                self.load_radii,
                self.ndc_coord,
                self.ndc_coeffs,
                self.multlosses[idx] if self.multlosses is not None else None,
            )
    
            device_count = self.num_devices
            n_dset = len(_rays_o)
            dummy_num = (
                (device_count - n_dset % device_count) % device_count if dummy else 0
            ) # 计算添加虚拟数据的数量,从而最终使得每个gpu均匀获得数据
            #  进行数据填充
            rays_o = np.zeros((n_dset + dummy_num, 3), dtype=np.float32)
            rays_d = np.zeros((n_dset + dummy_num, 3), dtype=np.float32)
            viewdirs = np.zeros((n_dset + dummy_num, 3), dtype=np.float32)
    
            rays_o[:n_dset], rays_o[n_dset:] = _rays_o, _rays_o[:dummy_num]
            rays_d[:n_dset], rays_d[n_dset:] = _rays_d, _rays_d[:dummy_num]
            viewdirs[:n_dset], viewdirs[n_dset:] = _viewdirs, _viewdirs[:dummy_num]
    
            viewdirs = viewdirs / np.linalg.norm(viewdirs, axis=1, keepdims=True)
    
            if _images is not None:
                images_idx = np.concatenate([_images[i].reshape(-1, 3) for i in idx])
                images = np.zeros((n_dset + dummy_num, 3))
                images[:n_dset] = images_idx
                images[n_dset:] = images[:dummy_num]
    
            ......
    
            rays_info = {
                "rays_o": rays_o,
                "rays_d": rays_d,
                "viewdirs": viewdirs,
                "images": images,
                "radii": radii,
                "multloss": multloss,
                "normals": normals,
            }
    
            return RaySet(rays_info), dummy_num
    
    

    具体的获取光线的代码为batchified_get_rays

    这里是具体的代码:

    def batchified_get_rays(
        intrinsics,
        extrinsics,
        image_sizes,
        use_pixel_centers,
        get_radii,
        ndc_coord,
        ndc_coeffs,
        multlosses,
    ):
    
        radii = None
        multloss_expand = None
    
        center = 0.5 if use_pixel_centers else 0.0
        #  为每个图片创建网格坐标
        mesh_grids = [
            np.meshgrid(
                np.arange(w, dtype=np.float32) + center,
                np.arange(h, dtype=np.float32) + center,
                indexing="xy",
            )
            for (h, w) in image_sizes
        ]
    
        i_coords = [mesh_grid[0] for mesh_grid in mesh_grids]
        j_coords = [mesh_grid[1] for mesh_grid in mesh_grids]
    #  计算x方向的方向向量和y方向的方向向量 (点的位置-光心)/f  (转换到了相机坐标系下)
        dirs = [
            np.stack(
                [
                    (i - intrinsic[0][2]) / intrinsic[0][0],
                    (j - intrinsic[1][2]) / intrinsic[1][1],
                    np.ones_like(i),
                ],
                -1,
            )
            for (intrinsic, i, j) in zip(intrinsics, i_coords, j_coords)
        ]
    #  这里的blender的外参矩阵是c2w矩阵,因此第四列代表相机的光心在世界坐标系的位置
        rays_o = np.concatenate(
            [
                np.tile(extrinsic[np.newaxis, :3, 3], (1, h * w, 1)).reshape(-1, 3)
                for (extrinsic, (h, w)) in zip(extrinsics, image_sizes)
            ]
        ).astype(np.float32)
    #  通过爱因斯坦求和,由相机坐标系转到世界坐标系,成为世界坐标系中的方向向量
        rays_d = np.concatenate(
            [
                np.einsum("hwc, rc -> hwr", dir, extrinsic[:3, :3]).reshape(-1, 3)
                for (dir, extrinsic) in zip(dirs, extrinsics)
            ]
        ).astype(np.float32)
    
        viewdirs = rays_d
        viewdirs /= np.linalg.norm(viewdirs, axis=-1, keepdims=True)#  单位方向向量,表示方向但不具有尺度
    
        if ndc_coord: #  是否使用归一化坐标系,即ndc坐标系
            rays_o, rays_d = convert_to_ndc(rays_o, rays_d, ndc_coeffs)
    
        ......
    
        return rays_o, rays_d, viewdirs, radii, multloss_expand
    
    

     

关于这里维度的变换说一下特殊的说明,方便大家的理解。

首先我们看内参矩阵,观察dirs的由来:


再看图像坐标系和像素坐标系的转换:



因此进而得到归一化坐标系下对应的像素坐标,此时的光心在归一化坐标系为(0,0),从而得到相应的方向向量:


在通过外参矩阵转变成世界坐标系的方向向量(模拟光线的生成)。

正常情况下会乘以一个Z即深度信息,得到具体的点。

所以但这里没加入具体深度信息就指的是这方向是所有的点,即为整条光线,非常合理。

具体参考:

请添加图片描述请添加图片描述

然后就是沿着光线采样,这里的策略是先进行随机采样。然后通过粗网络得出来的值进行反推优化采样方式

随机采样:

def sample_along_rays(
    rays_o,
    rays_d,
    num_samples,
    near,
    far,
    randomized,
    lindisp,
):
    bsz = rays_o.shape[0]  # 读出batchsize
    t_vals = torch.linspace(0.0, 1.0, num_samples + 1, device=rays_o.device) #线性均匀取点 
    if lindisp:
        t_vals = 1.0 / (1.0 / near * (1.0 - t_vals) + 1.0 / far * t_vals)
    else:
        t_vals = near * (1.0 - t_vals) + far * t_vals  #相当于是从near开始,far结束的均匀样点

    if randomized:  # 随机采样
        mids = 0.5 * (t_vals[..., 1:] + t_vals[..., :-1]) # 取点的中间值
        upper = torch.cat([mids, t_vals[..., -1:]], -1)  #  包含最大的那个点
        lower = torch.cat([t_vals[..., :1], mids], -1)  #  包含最小的那个点
        t_rand = torch.rand((bsz, num_samples + 1), device=rays_o.device)
        t_vals = lower + (upper - lower) * t_rand  #  最后进行随机采样
    else:
        t_vals = torch.broadcast_to(t_vals, (bsz, num_samples + 1))

    coords = cast_rays(t_vals, rays_o, rays_d)  # 最后进行计算世界坐标系下的具体坐标

    return t_vals, coords

反推优化采样策略下节再讲。

参考:

https://blog.csdn.net/qq_40918859/article/details/122271381

https://zhuanlan.zhihu.com/p/593204605

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇