Python 工程师对 3D 高斯溅射的介绍(第 2 部分)

理解并编码如何在 3D 高斯溅射中使用高斯

       欢迎来到雲闪世界现在开始讨论高斯!这是每个人最喜欢的分布。如果您刚刚加入我们,我们已经在第1 部分中介绍了如何根据相机的位置获取 3D 点并将其转换为 2D 。在本文中,我们将讨论高斯分布的高斯部分。我们将使用GitHub中的 part_2.ipynb 。

我们在这里要做出的一点小改动是,我们将使用透视投影,它利用的内部矩阵与上一篇文章中所示的不同。然而,当将点投影到 2D 时,这两种方法是等效的,我发现第 1 部分中介绍的第一种方法更容易理解,但是我们改变了我们的方法,以便在 Python 中尽可能多地复制作者的代码。具体来说,我们的“内部”矩阵现在将由此处显示的 OpenGL 投影矩阵给出,乘法顺序现在将是 points @ external.transpose() @ internal。

内部透视投影矩阵

       对于那些想了解这个新的内部矩阵的人(否则请随意跳过本段)r 和 l 是右侧和左侧的裁剪平面,本质上是相对于照片宽度可以看到哪些点,t 和 b 是顶部和底部裁剪平面。N 是近裁剪平面(点将被投影到该平面),f 是远裁剪平面。有关更多信息,及完整代码可联系博主。这还会返回标准化设备坐标(介于 -1 和 1 之间)中的点,然后我们将其投影到像素坐标。撇开题外话,任务保持不变,即取 3D 中的点并投影到 2D 图像平面上。但是,在本教程的这一部分中,我们现在使用高斯而不是点。

def getIntinsicMatrix(focal_x: torch.Tensor,focal_y: torch.Tensor,height: torch.Tensor,width: torch.Tensor,znear: torch.Tensor = torch.Tensor([100.0]),zfar: torch.Tensor = torch.Tensor([0.001]),,
) -> torch.Tensor:"""Gets the internal perspective projection matrixznear: near plane set by userzfar: far plane set by userfovX: field of view in x, calculated from the focal lengthfovY: field of view in y, calculated from the focal length"""fovX = torch.Tensor([2 * math.atan(width / (2 * focal_x))])fovY = torch.Tensor([2 * math.atan(height / (2 * focal_y))])tanHalfFovY = math.tan((fovY / 2))tanHalfFovX = math.tan((fovX / 2))top = tanHalfFovY * znearbottom = -topright = tanHalfFovX * znearleft = -rightP = torch.zeros(4, 4)z_sign = 1.0P[0, 0] = 2.0 * znear / (right - left)P[1, 1] = 2.0 * znear / (top - bottom)P[0, 2] = (right + left) / (right - left)P[1, 2] = (top + bottom) / (top - bottom)P[3, 2] = z_signP[2, 2] = z_sign * zfar / (zfar - znear)P[2, 3] = -(zfar * znear) / (zfar - znear)return P

       3D 高斯图由 x、y 和 z 坐标以及相关的协方差矩阵组成。正如作者所指出的:“一种显而易见的方法是直接优化协方差矩阵 Σ 以获得表示辐射场的 3D 高斯。然而,协方差矩阵只有当它们是半正定的时候才具有物理意义。对于我们所有参数的优化,我们使用梯度下降法,这种梯度下降法不容易被限制来产生这样的有效矩阵,而更新步骤和梯度很容易产生无效的协方差矩阵。”¹

因此,作者使用协方差矩阵的分解,该分解将始终产生正半定协方差矩阵。具体来说,他们使用 3 个“尺度”参数和 4 个四元数,将其转换为 3x3 旋转矩阵 (R)。然后,协方差矩阵由下式给出

协方差矩阵方程,其中 R 表示由 4 个四元数导出的 3x3 旋转矩阵,S 是 3 个尺度参数

       请注意,在转换为旋转矩阵之前,必须对四元数向量进行归一化,才能获得有效的旋转矩阵。因此,在我们的实现中,高斯点由以下参数组成:坐标(3x1 向量)、四元数(4x1 向量)、比例(3x1 向量)以及与不透明度(splat 的透明度)相关的最终浮点值。现在我们需要做的就是优化这 11 个参数来获得我们的场景 — 很简单!

但实际上情况比这要复杂一些。如果你还记得高中数学,高斯在特定点的强度由以下公式给出:

点 x 处的高斯强度由平均值 (mu) 和协方差矩阵的逆给出

      然而,我们关心的是 2D 中 3D 高斯的强度,即在图像平面中。但你可能会说,我们知道如何将点投影到 2D!尽管如此,我们还没有讨论将协方差矩阵投影到 2D,所以如果我们还没有找到 2D 协方差矩阵,我们就不可能找到 2D 协方差矩阵的逆。

现在,这是最有趣的部分(取决于你如何看待它)。3D 高斯分布作者的一篇论文参考文献 EWA Splatting 精确地展示了如何将 3D 协方差矩阵投影到 2D。² 但是,这需要了解雅可比仿射变换矩阵,我们将在下面计算它。我发现代码在理解一个困难的概念时最有帮助,因此我在下面提供了一些代码,以举例说明如何从 3D 协方差矩阵转换为 2D。

def compute_2d_covariance(points: torch.Tensor,external_matrix: torch.Tensor,covariance_3d: torch.Tensor,tan_fovY: torch.Tensor,tan_fovX: torch.Tensor,focal_x: torch.Tensor,focal_y: torch.Tensor,
) -> torch.Tensor:"""Compute the 2D covariance matrix for each gaussian"""points = torch.cat([points, torch.ones(points.shape[0], 1, device=points.device)], dim=1)points_transformed = (points @ external_matrix)[:, :3]limx = 1.3 * tan_fovXlimy = 1.3 * tan_fovYx = points_transformed[:, 0] / points_transformed[:, 2]y = points_transformed[:, 1] / points_transformed[:, 2]z = points_transformed[:, 2]x = torch.clamp(x, -limx, limx) * zy = torch.clamp(y, -limy, limy) * zJ = torch.zeros((points_transformed.shape[0], 3, 3), device=covariance_3d.device)J[:, 0, 0] = focal_x / zJ[:, 0, 2] = -(focal_x * x) / (z**2)J[:, 1, 1] = focal_y / zJ[:, 1, 2] = -(focal_y * y) / (z**2)# transpose as originally set up for perspective projection# so we now transform backW = external_matrix[:3, :3].Treturn (J @ W @ covariance_3d @ W.T @ J.transpose(1, 2))[:, :2, :2]

       首先,tan_fovY 和 tan_fovX 是半个视场角的切线。我们使用这些值来限制我们的投影,防止任何狂野的屏幕外投影影响我们的渲染。我们可以从 3D 到 2D 的变换中推导出雅可比矩阵,就像我们在第 1 部分中介绍的初始正向变换一样,但我为您省去了麻烦,并在上面展示了预期的推导。最后,如果您还记得,我们在上面转置了旋转矩阵以适应术语的重新排列,因此我们在返回最终协方差计算之前在倒数第二行转置回来。正如 EWA 溅射论文所述,我们可以忽略第三行和第三列,因为我们只关心 2D 图像平面。您可能会想,为什么我们不能从一开始就这样做?好吧,协方差矩阵参数将根据您从哪个角度观察它而变化,因为在大多数情况下它不会是一个完美的球体!现在我们已经转换到正确的视点,协方差 z 轴信息就没有用了,可以丢弃。

       假设我们有 2D 协方差矩阵,我们几乎能够计算出每个高斯对图像中任何随机像素的影响,我们只需要找到逆协方差矩阵。再次回想一下线性代数,要找到 2x2 矩阵的逆,你只需要找到行列式,然后对项进行一些重新排列。以下是一些代码,可帮助你完成该过程。

def compute_inverted_covariance(covariance_2d: torch.Tensor) -> torch.Tensor:"""Compute the inverse covariance matrixFor a 2x2 matrixgiven as[[a, b],[c, d]]the determinant is ad - bcTo get the inverse matrix reshuffle the terms like soand multiply by 1/determinant[[d, -b],[-c, a]] * (1 / determinant)"""determinant = (covariance_2d[:, 0, 0] * covariance_2d[:, 1, 1]- covariance_2d[:, 0, 1] * covariance_2d[:, 1, 0])determinant = torch.clamp(determinant, min=1e-3)inverse_covariance = torch.zeros_like(covariance_2d)inverse_covariance[:, 0, 0] = covariance_2d[:, 1, 1] / determinantinverse_covariance[:, 1, 1] = covariance_2d[:, 0, 0] / determinantinverse_covariance[:, 0, 1] = -covariance_2d[:, 0, 1] / determinantinverse_covariance[:, 1, 0] = -covariance_2d[:, 1, 0] / determinantreturn inverse_covariance

        好了,现在我们可以计算图像中每个像素的像素强度了。但是,这样做非常慢而且没有必要。例如,除非协方差矩阵非常大,否则我们真的不需要浪费计算能力来弄清楚 (0,0) 处的 splat 如何影响 (1000, 1000) 处的像素。因此,作者选择计算他们所谓的每个 splat 的“半径”。如下面的代码所示,我们计算沿每个轴的特征值(请记住,特征值显示变化)。然后,我们取最大特征值的平方根以获得标准偏差测量值并将其乘以 3.0,这涵盖了 3 个标准偏差内的 99.7% 的分布。这个半径有助于我们找出 splat 接触的最小和最大 x 和 y 值。渲染时,我们只计算这些范围内像素的 splat 强度,从而节省了大量不必要的计算。很聪明,对吧?

def compute_extent_and_radius(covariance_2d: torch.Tensor):mid = 0.5 * (covariance_2d[:, 0, 0] + covariance_2d[:, 1, 1])det = covariance_2d[:, 0, 0] * covariance_2d[:, 1, 1] - covariance_2d[:, 0, 1] ** 2intermediate_matrix = (mid * mid - det).view(-1, 1)intermediate_matrix = torch.cat([intermediate_matrix, torch.ones_like(intermediate_matrix) * 0.1], dim=1)max_values = torch.max(intermediate_matrix, dim=1).valueslambda1 = mid + torch.sqrt(max_values)lambda2 = mid - torch.sqrt(max_values)# now we have the eigenvalues, we can calculate the max radiusmax_radius = torch.ceil(3.0 * torch.sqrt(torch.max(lambda1, lambda2)))return max_radius

       上述所有步骤都为我们提供了预处理场景,然后可以在渲染步骤中使用。总结一下,我们现在有了 2D 中的点、与这些点相关的颜色、2D 中的协方差、2D 中的逆协方差、排序的深度顺序、每个 splat 的最小 x、最小 y、最大 x、最大 y 值以及相关的不透明度。有了所有这些组件,我们终于可以开始渲染图像了!

感谢关注雲闪世界。(亚马逊aws和谷歌GCP服务协助解决云计算及产业相关解决方案)

 订阅频道(https://t.me/awsgoogvps_Host)
 TG交流群(t.me/awsgoogvpsHost)

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

口袋奇兵游戏攻略:云手机辅助战锤入侵策略指南!

在《口袋奇兵》中,战锤入侵是一个重要的游戏环节,了解如何有效地参与战锤入侵能够帮助玩家获取更多的资源和提升自己的战力。本文将详细介绍战锤入侵的策略和技巧,帮助玩家在战锤入侵活动中取得更好的成绩。除了找到强力的游戏辅助&#xff0…

【JVM】JVM调优练习-随笔

JVM实战笔记-随笔 前言字节码如何查看字节码文件jclasslibJavapArthasArthurs监控面板Arthus查看字节码信息 内存调优内存溢出的常见场景解决内存溢出发现问题Top命令VisualVMArthas使用案例 Prometheus Grafana案例 堆内存情况对比内存泄漏的原因:代码中的内存泄漏并发请求问…

vmware配置centos+配置静态ip联网+更换镜像

centos7配置参考【实战】VMware17虚拟机以及Centos7详细安装教程-CSDN博客 ip配置步骤: 先更改编辑虚拟网络编辑器中的内容 就按照还原默认设置来,设定后就是以上内容,然后一定要记住子网ip和子网掩码 接下来就是NAT设置: 网关…

国产麒麟、UOS在线打开pdf加盖印章

PageOffice支持两种电子印章方案,可实现对Word、Excel、PDF文档加盖PageOffice自带印章或ZoomSeal电子印章(全方位保护、防篡改、防伪造)。Word和Excel的盖章功能请参考:Word和Excel加盖印章和签字功能 (目前只支持win…

PHP pwn 学习 (2)

文章目录 A. 逆向分析A.1 基本数据获取A.2 函数逆向zif_addHackerzif_removeHackerzif_displayHackerzif_editHacker A.3 PHP 内存分配 A.4 漏洞挖掘B. 漏洞利用B.1 PHP调试B.2 exp 上一篇blog中,我们学习了一些PHP extension for C的基本内容,下面结合一…

磷酸制备除杂,提高磷酸纯度的工艺

在化肥生产领域,磷酸作为一种关键的生产原料,其纯度对化肥的品质和性能有着直接的影响。然而,在实际生产过程中,磷酸原料中常常含有各种杂质,如金属离子、有机物等,这些杂质的存在会严重影响磷酸的纯度&…

如何在web页面下做自动化测试?

自动化测试是在软件开发中非常重要的一环,它可以提高测试效率并减少错误率。在web页面下进行自动化测试,可以帮助我们验证网页的功能和交互,并确保它们在不同浏览器和平台上的一致性。本文将从零开始,详细介绍如何在web页面下进行…

MySQL相关知识

一、什么是数据库? 数据库(Database,简称DB)概念: 长期存放在计算机内,有组织、可共享的大量数据的集合,是一个 数据“仓库”。 二、数据库的特点: 1.结构化:数据在数…

如何在Ubuntu上安装并启动SSH服务(Windows连接)

在日常的开发和管理工作中,通过SSH(Secure Shell)连接到远程服务器是一个非常常见的需求。如果你在尝试通过SSH连接到你的Ubuntu系统时遇到了问题,可能是因为SSH服务未安装或未正确配置。本文将介绍如何在Ubuntu上安装并启动SSH服…

GitHub私有派生仓库(fork仓库) | 派生仓库改为私有

GitHub私有派生仓库 前言解决方案 前言 在GitHub上Fork的派生仓库默认为公有仓库,且无法修改为私有仓库。 若想创建私有的派生仓库,可通过GitHub的导入仓库功能实现,具体步骤请参见下文解决方案。 解决方案 打开GitHub页面,在个…

C#医学影像管理系统源码 PACS系统源码带三维重建,全院级数字医学影像系统

C#医学影像管理系统源码 医学影像存储与传输系统源码 PACS系统源码带三维重建,三甲以下医院都能满足。 PACS系统模块组成 : 工作站: 分诊工作站、超声工作站、放射工作站、内镜工作站、病理工作站。 基本信息维护: 输入模板、输入…

Token Labeling(NeurIPS 2021, ByteDance)论文解读

paper:All Tokens Matter: Token Labeling for Training Better Vision Transformers official implementation:https://github.com/zihangJiang/TokenLabeling 出发点 ViTs的局限性:尽管ViTs在捕捉长距离依赖方面表现出色, 但…

windows程序设计基础--学习记录

一、Windows程序的Hello world #include<windows.h>int WINAPI WinMain(HINSTANCE hInstance, //程序实例句柄HINSTANCE hPreHinstance, //上一个程序的实例句柄&#xff08;被遗弃&#xff0c;参数始终为NULL&#xff09;LPSTR lpCmdeLine, //命令行参数int nCmdeSh…

Jangow

关于靶场环境配置&#xff0c;确实这个靶场存在很大的问题&#xff0c;不仅仅是网络的配置问题&#xff0c;更重要的是明知道如何修改网络环境配置&#xff0c;但是键盘存在很大的问题。许多字符输入不一致。 Vulnhub靶场&#xff0c;Jangow靶机环境找不到ip解决方法。_jangow…

vue3运行若依前后台项目步骤(2024-07-19)

环境配置 1、jdk > 1.8 (我的1.8&#xff09; 2、mysql >5.7 (我的5.8&#xff09; 3、navicat (数据库管理器&#xff0c;连接mysql使用 ,我的是15) 4、mysql&#xff08;数据库&#xff0c;我的5.0&#xff09; 4、npm (我的是18.20.0) 5、idea编辑器,webtorm &#x…

API接口防刷实现(重复调用)

API接口 - 限制重复调用&#xff08;防刷&#xff09; 环境代码实现实现方式定义注解注解应用 环境 JDK 8 SpringBoot 代码实现 实现方式 定义注解&#xff0c;通过切面的方式&#xff0c;检测重复调用。 定义注解 import cn.nhd.fsl.entity.system.Prevent; import cn.nhd…

打造超酷的 React 迷你日历组件,只需几步!

你好&#xff0c;我是小白Coding日志&#xff0c;一个热爱技术的程序员。在这里&#xff0c;我分享自己在编程和技术世界中的学习心得和体会。希望我的文章能够给你带来一些灵感和帮助。欢迎来到我的博客&#xff0c;一起在技术的世界里探索前行吧&#xff01; 前言 现在市面…

基于Linux的USB-wifi配置流程

目录 内核配置 配置 CFG80211 配置usb 配置 Netlink 配置DHCP 工作流程 1.连接到无线网络 2.设置网络接口&#xff1a; 3.验证连接&#xff1a; 4. 接收数据&#xff1a; 最近daisy一直忙活这个linux的wifi驱动和bluze蓝牙驱动&#xff0c;相比较蓝牙&#xff0c;WiFi的驱动和内…

面对垃圾邮件的骚扰,U-Mail邮件安全网关来帮你

在近几年的时间里&#xff0c;企业面临垃圾邮件的威胁成指数级增长&#xff0c;据第三方统计&#xff0c;垃圾邮件占电子邮件总通讯量的60%以上。与此同时&#xff0c;垃圾邮件的类型以及发送手段也愈加复杂化、多样化&#xff0c;电子邮件也一跃成为病毒或恶意软件的主要传播渠…

金蝶官宣:法大大电子签章“星空旗舰版”来了!

融合了数字签名、实名认证、AI、区块链、大数据等能力的法大大电子签章“星空旗舰版”来了&#xff01;通过与金蝶云星空旗舰版的集成产品打造&#xff0c;法大大携手金蝶共同面向客户提供高质量的产品综合解决方案服务。以下内容转载自金蝶云生态&#xff1a; 产品与解决方案 …