NeRF从入门到放弃5: Neurad代码实现细节

Talk is cheap, show me the code。

CNN Decoder

如patch设置为32x32,patch_scale设置为3,则先在原图上采样96x96大小的像素块,然后每隔三个取一个像素,降采样成32x32的块。

用这32x32个像素render feature,再经过CNN反卷积预测出96x96的像素,与真值对比。

def _patches_from_centers(
    self,
    image: torch.Tensor,
    patch_center_indices: torch.Tensor,
    rgb_size: int,
    device: Union[torch.device, str] = "cpu",
):
    """Convert patch center coordinates to the full set of ray indices and image patches."""
    offsets = torch.arange(-(rgb_size // 2), (rgb_size // 2) + rgb_size % 2, device=device)
    zeros = offsets.new_zeros((rgb_size, rgb_size))
    relative_indices = torch.stack((zeros, *torch.meshgrid(offsets, offsets, indexing="ij")), dim=-1)[
        None
    ]  # 1xKxKx3,原图采样大小
    rgb_indices = patch_center_indices[:, None, None] + relative_indices  # NxKxKx3
    ray_indices = rgb_indices[
        :, self.patch_scale // 2 :: self.patch_scale, self.patch_scale // 2 :: self.patch_scale
    ]  # NxKfxKfx3,降采样
    ray_indices = ray_indices.reshape(-1, 3)  # (N*Kf*Kf)x3
    img_patches = image[rgb_indices[..., 0], rgb_indices[..., 1], rgb_indices[..., 2]]
    return ray_indices, img_patches

相机位姿优化

参考nerfstudio/cameras/camera_optimizers.py

每迭代一次优化一次

  1. 初始化
self.pose_adjustment = torch.nn.Parameter(torch.zeros((num_cameras, 6), device=device)) # Nx6,前3维表示平移,后三维表示后3维表示切向量,再通过exp_map_SO3xR3,把6维变量映射为位姿和位移变量。相当于优化的是每个相机的标定参数
  1. 计算位姿偏移量
def forward(
        self,
        indices: Int[Tensor, "camera_indices"],
    ) -> Float[Tensor, "camera_indices 3 4"]:
    correction_matrices = exp_map_SO3xR3(self._get_pose_adjustment()[indices, :])
    
  1. 应用到相机的原始位姿上
def apply_to_raybundle(self, raybundle: RayBundle) -> None:
    """Apply the pose correction to the raybundle"""
    if self.config.mode != "off":
        correction_matrices = self(raybundle.camera_indices.squeeze())  # type: ignore
        raybundle.origins = raybundle.origins + correction_matrices[:, :3, 3]
        raybundle.directions = (
            torch.bmm(correction_matrices[:, :3, :3], raybundle.directions[..., None])
            .squeeze()
            .to(raybundle.origins)
        )
  1. 可学习的6维向量如何转成旋转矩阵
# nerfstudio/cameras/lie_groups.py
# We make an exception on snake case conventions because SO3 != so3.
def exp_map_SO3xR3(tangent_vector: Float[Tensor, "b 6"]) -> Float[Tensor, "b 3 4"]:
    """Compute the exponential map of the direct product group `SO(3) x R^3`.

    This can be used for learning pose deltas on SE(3), and is generally faster than `exp_map_SE3`.

    Args:
        tangent_vector: Tangent vector; length-3 translations, followed by an `so(3)` tangent vector.
    Returns:
        [R|t] transformation matrices.
    """
    # code for SO3 map grabbed from pytorch3d and stripped down to bare-bones
    log_rot = tangent_vector[:, 3:]
    nrms = (log_rot * log_rot).sum(1)
    rot_angles = torch.clamp(nrms, 1e-4).sqrt()
    rot_angles_inv = 1.0 / rot_angles
    fac1 = rot_angles_inv * rot_angles.sin()
    fac2 = rot_angles_inv * rot_angles_inv * (1.0 - rot_angles.cos())
    skews = torch.zeros((log_rot.shape[0], 3, 3), dtype=log_rot.dtype, device=log_rot.device)
    skews[:, 0, 1] = -log_rot[:, 2]
    skews[:, 0, 2] = log_rot[:, 1]
    skews[:, 1, 0] = log_rot[:, 2]
    skews[:, 1, 2] = -log_rot[:, 0]
    skews[:, 2, 0] = -log_rot[:, 1]
    skews[:, 2, 1] = log_rot[:, 0]
    skews_square = torch.bmm(skews, skews)

    ret = torch.zeros(tangent_vector.shape[0], 3, 4, dtype=tangent_vector.dtype, device=tangent_vector.device)
    ret[:, :3, :3] = (
        fac1[:, None, None] * skews
        + fac2[:, None, None] * skews_square
        + torch.eye(3, dtype=log_rot.dtype, device=log_rot.device)[None]
    )

    # Compute the translation
    ret[:, :3, 3] = tangent_vector[:, :3]
    return ret

Apperance embedding

就是简单的使用torch.nn.Embedding(num_embeds, self.config.appearance_dim)

# Appearance embedding settings
# num_sensor指的是相机个数,如果配置temporal,则每一帧都有单独的embedding 
if self.config.use_temporal_appearance:
    self._num_embeds_per_sensor = math.ceil(self._duration * self.config.temporal_appearance_freq)
    num_embeds = num_sensors * self._num_embeds_per_sensor
else:
    num_embeds = num_sensors

# num_embeds=6,self.config.appearance_dim=16,表示6个相机,每个相机有16维的Embedding特征
self.appearance_embedding = torch.nn.Embedding(num_embeds, self.config.appearance_dim)

def _get_appearance_embedding(self, ray_bundle, features):
    sensor_idx = ray_bundle.metadata.get("sensor_idxs")
    if sensor_idx is None:
        assert not self.training, "Sensor sensor_idx must be present in metadata during training"
        sensor_idx = torch.full_like(features[..., :1], self.fallback_sensor_idx.value, dtype=torch.long)

    if self.config.use_temporal_appearance:
        time_idx = ray_bundle.times / self._duration * (embd_per_sensor := self._num_embeds_per_sensor)
        before_idx = time_idx.floor().clamp(0, embd_per_sensor - 1)
        after_idx = (before_idx + 1).clamp(0, embd_per_sensor - 1)
        ratio = time_idx - before_idx
        # unwrap to true embedding indices, which also account for the sensor index, not just the time index
        before_idx, after_idx = (x + sensor_idx * embd_per_sensor for x in (before_idx, after_idx))
        before_embed = self.appearance_embedding(before_idx.squeeze(-1).long())
        after_embed = self.appearance_embedding(after_idx.squeeze(-1).long())
        embed = before_embed * (1 - ratio) + after_embed * ratio
    else:
        embed = self.appearance_embedding(sensor_idx.squeeze(-1))
    return embed

lidar建模和采样

lidar发射射线和camer类似,只需要根据世界坐标系下lidar原点的坐标和点云的坐标,就能确定一条射线了,沿这条射线采样点,真值是这条射线上真正扫描到的点。

采样时,根据每次迭代设置的采样点数N如16384,平均到每帧的每个点上。

采样方式是把全部帧的点云concate起来,每个点有个全局的序号和帧的idx,假设总点数为100万,采样时在0-100万之间随机生成N个随机数。

    def get_lidar_batch_and_ray_bundle(self):
        if not len(self.lidar_dataset.lidars):
            return None, None
        batch = self.point_sampler.sample(self.cached_points)
        ray_indices = batch.pop("indices") # Nx2, 0: lidar index, 1: point index,共采样16384个点,每帧采样点数一样
        ray_bundle: RayBundle = self.lidar_ray_generator(ray_indices, points=batch["lidar"]) #把所有的点都concate起来了
        return batch, ray_bundle # batch存储lidar原始点,ray_bundle存储采样的方向,原点信息

另外,pixel_area的作用没太看懂,有点像是MipNerf里面的用锥形体界面去积分,而不是直接的射线?

    dx = self.horizontal_beam_divergence[lidar_indices.squeeze(-1)]  # ("num_rays":...,)
    dy = self.vertical_beam_divergence[lidar_indices.squeeze(-1)]  # ("num_rays":...,)
    pixel_area = dx * dy  # ("num_rays":..., 1)

sdf实现

如果使用sdf,直接根据下面公式预测出不透明度α;否则便是先预测出密度density,再根据density积分得到不透明度。

因此两种render weight的方式是不同的。
在这里插入图片描述

if self.config.use_sdf:
    signed_distance = geo_out  # 直接把mlp的输出当作signed distance
    outputs[FieldHeadNames.SDF] = signed_distance
    outputs[FieldHeadNames.ALPHA] = self.sdf_to_density(signed_distance)
else:
    outputs[FieldHeadNames.DENSITY] = trunc_exp(geo_out) # 调用了torch.exp(), 为什么不能直接用geo_out作为density?有两个原因:1.因为density的物理意义是大于0的,geo_out不保证大于0  2. 网络输出的值可能非常小,使用epx放大,可以保持数值稳定性

self.sdf_to_density = SigmoidDensity(self.config.sdf_beta, learnable_beta=self.config.learnable_beta)

这个名字应该叫SigmoidAlpha,最后输出的被当做α,不是density了

class SigmoidDensity(nn.Module):
    """Learnable sigmoid density"""

    def __init__(self, init_val, beta_min=0.0001, learnable_beta=False):
        super().__init__()
        self.register_buffer("beta_min", torch.tensor(beta_min))
        self.register_parameter("beta", nn.Parameter(init_val * torch.ones(1), requires_grad=learnable_beta))

    def forward(self, sdf: Tensor, beta: Union[Tensor, None] = None) -> Tensor:
        """convert sdf value to density value with beta, if beta is missing, then use learable beta"""

        if beta is None:
            beta = self.get_beta()

        # negtive sdf will have large density
        return torch.sigmoid(-sdf * beta) #这里就是上面的公式,这里叫α,和density不是一个东西

    def get_beta(self):
        """return current beta value"""
        beta = self.beta.abs() + self.beta_min
        return beta

render_weight_from_alpha()直接处理不透明度,而[render_weight_from_density()]则需要先从密度计算不透明度。

def _render_weights(self, outputs, ray_samples):
    value = outputs[FieldHeadNames.ALPHA if self.config.field.use_sdf else                 FieldHeadNames.DENSITY].squeeze(-1)
    if self.device.type in ("cpu", "mps"):
        # Note: for debugging on devices without cuda
        weights = torch.zeros_like(value) + 0.5
    elif self.config.field.use_sdf:
        weights, _ = nerfacc.render_weight_from_alpha(value)
    else:
        weights, _, _ = nerfacc.render_weight_from_density(
            t_ends=ray_samples.frustums.ends.squeeze(-1),
            t_starts=ray_samples.frustums.starts.squeeze(-1),
            sigmas=value,
        )
    return weights

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/734912.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

高考填报志愿(选专业),怎样找准自己的兴趣?

在很多的高考报考指南中,第一要点,都会建议我们根据自己的兴趣来选择自己的专业。很多人虽然是依据这条规则,选择了自己大学的专业。却依然在学习的过程中发现,好像自己对这个专业并不是那么的有兴趣。 甚至对专业学习深入了解之…

MySQL数据库笔记(二)

第一章 单行函数 1.1 什么是函数 函数的作用是把我们经常使用的代码封装起来,需要的时候直接调用即可。这样既提高了代码效率,又提高了可维护性。在SQL中使用函数,极大地提高了用户对数据库的管理效率。 1.2 定义 操作数据对象。 接受参数返回一个结果。 只对一行进行…

探索计算机视觉(人工智能重要分支)的发展与应用

引言 在当今快速发展的科技时代,计算机视觉作为人工智能领域的重要分支,正日益成为各行各业不可或缺的关键技术。从简单的图像处理到复杂的智能系统,计算机视觉的发展不仅改变了我们看待世界的方式,也深刻影响着工业、医疗、交通等…

不同交换机之间相同VLAN间主机通信

1、搭建网络拓扑 搭建拓扑,分配IP地址,划分vlan,分配端口 2、配置交换机 //进入全局配置模式 Switch>enable Switch#config terminal Enter configuration commands, one per line. End with CNTL/Z. Switch(config)#hostname SW1 …

如何级联移位寄存器(74HC595)

在这个项目中,我们将使用 74HC595 移位寄存器将 2 个移位寄存器级联在一起。这样级联移位寄存器现在可以控制 16 个输出。 当然您可以级联任意数量的移位寄存器。如果您要级联第三个移位寄存器,它可以控制 24 个输出。如果您级联第四个移位寄存器&#x…

Rxjava2最全面的解析

说到区别,可能有的小伙伴会问,我没看过rxjava1。可以直接看rxjava2么。个人觉得不必要,因为 rxjava2.x 是按照 Reactive-Streams specification 规范完全的重写的,完全独立于 rxjava1.x 而存在,它改变了以往 rxjava1的…

1999-2022年 297个地级市-医院卫生院数量及床位数量(数据收集)

全国297个地级市的医院卫生院数量的稳步增长是医疗事业发展的一个重要标志。政府的持续投入和对医疗设施的改善,不仅提升了医疗服务的硬件水平,也通过引进和培养医疗人才、优化服务流程,提高了医疗服务的整体质量。这些举措极大地增强了人民群…

WordPress项目教程:自动采集并发布,让你轻松实现网站内容更新

随着互联网的发展,越来越多的人开始关注自己的个人网站,通过网站展示自己的才华、分享知识、推广产品等。然而,个人网站的运营并非易事,尤其是内容更新方面。为了解决这个问题,今天我们将为大家推荐一款WordPress插件主…

测试辅助工具(抓包工具)的使用3 之 弱网测试

1.为什么要进行弱网测试? 1.带宽1M和带宽100M打开tpshop网站效果一样吗? 2.手机使用2G网络和使用3G网络打开京东的效果一样吗? 弱网环境下,出现丢包、延时软件的处理机制,避免造成用户的流失。 2.如何进行弱网测试&…

记一道MO数学练习题

手玩发现, 要么是行共线, 也就是说(1,1)填1之后,(1,4)要填1,(1,7)要填1, 事实上,可以给(1,x)&a…

YOLOv10目标检测算法的使用

目录 一、环境安装 1、创建虚拟环境 2、安装依赖 二、数据集准备 1、预训练权重 2、数据划分 3、建立数据集的yaml文件 三、训练 1、终端运行指令 2、建立一个 python 文件运行 四、验证 1、终端运行指令 2、建立一个 python 文件运行 五、模型推理 1、单张图片推…

AtCoder Beginner Contest 359(ABCDEFG题)视频讲解

A - Count Takahashi Problem Statement You are given N N N strings. The i i i-th string S i S_i Si​ ( 1 ≤ i ≤ N ) (1 \leq i \leq N) (1≤i≤N) is either Takahashi or Aoki. How many i i i are there such that S i S_i Si​ is equal to Takahashi? C…

基于IDEA的Maven(坐标信息介绍和编写)

这篇博客来学习和分析一下: " pom.xml " 所生成的最基本的信息。 之前的博客中讲到,学 Maven 就是学 " pom.xml " 的配置。后面也会围绕这个文件进行学习。 目录 一、分析 pom.xml 文件 (1)分析的 "p…

YOLOv9基础 | 实时目标检测新SOTA,手把手带你深度解析yolov9论文!

前言:Hello大家好,我是小哥谈。YOLOv9是Chien-Yao Wang等人提出的YOLO系列的最新版本之一(截止到目前,YOLOv10已发布),于2024年2月21日发布。它是 YOLOv7的改进版本,两者均由Chien-Yao Wang及其同事开发。本节课就以YOLOv9论文为基础带大家深入解析YOLOv9算法。🌈 …

React+TS前台项目实战(十五)-- 全局常用组件Table封装

文章目录 前言Table组件1. 功能分析2. 代码详细注释3. 使用方式4. 效果展示 总结 前言 在这篇文章中,我们将对本系列项目中常用的表格组件Table进行自定义封装,以提高性能并适应项目需求。后期也可进行修改和扩展,以满足项目的需求。 Table组…

html--404页面

<!DOCTYPE html> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8"> <meta http-equiv"X-UA-Compatible" content"IEedge,chrome1"> <title>404 错误页面不存在&…

【Linux】进程间通信3——线程安全

1.Linux线程互斥 1.1.进程线程间的互斥相关背景概念 临界资源&#xff1a; 多线程执行流共享的资源叫做临界资源。临界区&#xff1a; 每个线程内部&#xff0c;访问临界资源的代码&#xff0c;就叫做临界区。互斥&#xff1a; 任何时刻&#xff0c;互斥保证有且只有一个执行…

一年前 LLM AGI 碎片化思考与回顾系列⑦ · 在SystemⅡ未知之境之中徘徊

阅读提示&#xff1a; 本篇系列内容的是建立于自己过去一年在以LLM为代表的AIGC快速发展浪潮中结合学术界与产业界创新与进展的一些碎片化思考并记录最终沉淀完成&#xff0c;在内容上&#xff0c;与不久前刚刚完稿的那篇10万字文章「融合RL与LLM思想&#xff0c;探寻世界模型以…

乾坤微服务的使用

前言&#xff1a; 在这里整理下用乾坤来开发微服务的一些资料。 使用好处&#xff1a; 使用乾坤可以实现什么效果呢&#xff1f;众所周知&#xff0c;前端的框架五花八门&#xff0c;react/vue/angular等各领风骚&#xff0c;那么如果我们有需要把不同技术栈的项目整合起来&…

UFS Power Mode Change 介绍

一. UFS Power Mode Change简介 1.UFS Power Mode指的是Unipro层的Power State, 也可以称为链路(Link)上的Power Mode, 可以通过配置Unipro Attribute, 然后控制切换Unipro Power State, 当前Power Mode Change有两种触发方式&#xff1a; (1) 通过DME Power Mode Change触发…