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

news2024/11/13 16:38:40

从 Python 工程师的角度理解和编写 Gaussian Splatting

欢迎来到雲闪世界。2023 年初,来自法国蔚蓝海岸大学和马克斯普朗克信息研究所的作者发表了一篇题为“用于实时场渲染的 3D 高斯溅射”的论文。¹ 该论文展示了实时神经渲染的重大进步,超越了 NeRF 等先前方法的实用性。² 高斯溅射不仅降低了延迟,而且达到或超过了 NeRF 的渲染质量,席卷了神经渲染领域。

高斯溅射虽然有效,但对于不熟悉相机矩阵和图形渲染的人来说,理解起来可能具有挑战性。此外,我发现在 Python 中实现高斯溅射的资源很少,因为作者的源代码甚至都是用 CUDA 编写的!本教程旨在弥补这一差距,为精通 Python 和机器学习但缺乏图形渲染经验的工程师提供基于 Python 的高斯溅射简介。GitHub 上的随附代码演示了如何初始化和渲染 COLMAP 扫描中的点,使其成为类似于溅射应用程序中的前向传递的最终图像(以及一些额外的 CUDA 代码,供有兴趣的人使用)。本教程还有一个配套的 jupyter 笔记本( GitHub 中的part_1.ipynb),其中包含学习所需的所有代码。虽然我们不会构建完整的高斯溅射场景,但如果按照本教程进行操作,读者应该具备基础知识,可以更深入地研究溅射技术。

首先,我们使用 COLMAP,这是一款使用“运动结构”(SfM) 提取在多幅图像中一致可见的点的软件。³ SfM 本质上是识别在多幅图像中发现的点(例如,门口的右上边缘)。通过匹配不同图像中的这些点,我们可以估算 3D 空间中每个点的深度。这非常接近模拟人类立体视觉的工作原理,通过比较每只眼睛略有不同的视图来感知深度。因此,SfM 从多幅图像中找到的公共点生成一组 3D 点,每个点都有 x、y 和 z 坐标,从而为我们提供场景的“结构”。

在本教程中资料和完整代码,可联系博主。预构建 COLMAP 扫描(Apache 2.0 许可证)。具体来说,我们将使用下载的数据集中的 Treehill 文件夹。

该图像以及从输入到 COLMAP 的所有图像中提取的所有点。请参阅下面的示例代码或 part_1.ipynb 以了解该过程

该文件夹包含三个文件,分别对应相机参数、图像参数和实际 3D 点。我们将从 3D 点开始。

点文件由数千个 3D 点以及相关颜色组成。这些点以所谓的世界原点为中心,本质上它们的 x、y 或 z 坐标基于相对于这个世界原点的观察位置。世界原点的确切位置对于我们的目的来说并不重要,因此我们不会关注它,因为它可以是空间中的任意点。相反,唯一重要的是知道您在这个世界中相对于这个原点的位置。这就是图像文件变得有用的地方!

广义上讲,图像文件告诉我们图像的拍摄位置和相机的方向,这两者都与世界原点有关。因此,我们关心的关键参数是四元数向量和平移向量。四元数向量使用 4 个不同的浮点值描述相机在空间中的旋转,这些浮点值可用于形成旋转矩阵(3Blue1Brown 有一个很棒的视频,在这里准确解释了四元数是什么)。然后,平移向量告诉我们相机相对于原点的位置。这些参数一起形成外部矩阵,四元值用于计算 3x3 旋转矩阵(公式),并将平移向量附加到此矩阵。

典型的“外部”矩阵。通过组合 3x3 旋转矩阵和 3x1 平移向量,我们能够将坐标从世界坐标系平移到我们的相机坐标系

外部矩阵将点从世界空间(点文件中的坐标)转换到相机空间,使相机成为世界的新中心。例如,如果相机在 y 方向上向上移动 2 个单位而不进行任何旋转,我们只需从所有点的 y 坐标中减去 2 个单位即可获得新坐标系中的点。

当我们将坐标从世界空间转换为相机空间时,我们仍然有一个 3D 矢量,其中 z 坐标表示相机视图中的深度。此深度信息对于确定 splats 的顺序至关重要,我们稍后需要使用它来进行渲染。

我们通过解释相机参数文件来结束我们的 COLMAP 检查。相机文件提供高度、宽度、焦距(x 和 y)和偏移量(x 和 y)等参数。使用这些参数,我们可以组成固有矩阵,它表示 x 和 y 方向的焦距和主点坐标。

如果你对相机矩阵完全不熟悉,我会向你推荐 Shree Nayar 讲授的《计算机视觉第一原理》讲座。特别是针孔和前瞻性投影讲座,以及随后的内在和外在矩阵讲座。

典型的固有矩阵。表示 x 和 y 方向的焦距以及主点坐标。

内在矩阵用于将点从相机坐标(使用外在矩阵获得)转换为 2D 图像平面,即您所看到的“图像”。相机坐标中的点本身并不能表明它们在图像中的外观,因为必须反映深度才能准确评估相机将看到的内容。

要将 COLMAP 点转换为 2D 图像,我们首先使用外部矩阵将它们投影到相机坐标系,然后使用内部矩阵将它们投影到 2D 图像。但是,一个重要的细节是,我们在此过程中使用齐次坐标。外部矩阵为 4x4,而我们的输入点为 3x1,因此我们将 1 堆叠到输入点上,使它们变成 4x1。

以下是该过程的分步说明:

  1. 将点转换为相机坐标:将 4x4 外部矩阵乘以 4x1 点向量。

  2. 转换为图像坐标:将 3x4 内在矩阵乘以得到的 4x1 向量。

这会产生一个 3x1 矩阵。为了获得最终的 2D 坐标,我们用这个 3x1 矩阵的第三个坐标除以它,然后得到图像中的 x 和 y 坐标!您可以准确地看到图像编号 100 的显示效果,下面显示了复制结果的代码。

 
 

def get_intrinsic_matrix( f_x: float, f_y: float, c_x: float, c_y: float ) -> torch.Tensor: """ Get the homogenous intrinsic matrix for the camera """ return torch.Tensor( [ [f_x, 0, c_x, 0], [0, f_y, c_y, 0], [0, 0, 1, 0], ] ) def get_extrinsic_matrix(R: torch.Tensor, t: torch.Tensor) -> torch.Tensor: """ Get the homogenous extrinsic matrix for the camera """ Rt = torch.zeros((4, 4)) Rt[:3, :3] = R Rt[:3, 3] = t Rt[3, 3] = 1.0 return Rt def project_points( points: torch.Tensor, intrinsic_matrix: torch.Tensor, extrinsic_matrix: torch.Tensor ) -> torch.Tensor: """ Project the points to the image plane Args: points: Nx3 tensor intrinsic_matrix: 3x4 tensor extrinsic_matrix: 4x4 tensor """ homogeneous = torch.ones((4, points.shape[0]), device=points.device) homogeneous[:3, :] = points.T projected_to_camera_perspective = extrinsic_matrix @ homogeneous projected_to_image_plane = (intrinsic_matrix @ projected_to_camera_perspective).T # Nx4 x = projected_to_image_plane[:, 0] / projected_to_image_plane[:, 2] y = projected_to_image_plane[:, 1] / projected_to_image_plane[:, 2] return x, y colmap_path = "treehill/sparse/0" reconstruction = pycolmap.Reconstruction(colmap_path) points3d = reconstruction.points3D images = read_images_binary(f"{colmap_path}/images.bin") cameras = reconstruction.cameras all_points3d = [] all_point_colors = [] for idx, point in enumerate(points3d.values()): if point.track.length() >= 2: all_points3d.append(point.xyz) all_point_colors.append(point.color) gaussians = Gaussians( torch.Tensor(all_points3d), torch.Tensor(all_point_colors), model_path="point_clouds" ) # we will examine the 100th image image_num = 100 image_dict = read_image_file(colmap_path) camera_dict = read_camera_file(colmap_path) # convert quaternion to rotation matrix rotation_matrix = build_rotation(torch.Tensor(image_dict[image_num].qvec).unsqueeze(0)) translation = torch.Tensor(image_dict[image_num].tvec).unsqueeze(0) extrinsic_matrix = get_extrinsic_matrix( rotation_matrix, translation ) focal_x, focal_y = camera_dict[image_dict[image_num].camera_id].params[:2] c_x, c_y = camera_dict[image_dict[image_num].camera_id].params[2:4] intrinsic_matrix = get_intrinsic_matrix(focal_x, focal_y, c_x, c_y) points = project_points(gaussians.points, intrinsic_matrix, extrinsic_matrix)

我们有所需的各种位置和相机参数,我们现在就可以取任意一组 3D 点,并将它们投影到 2D 图像平面上!掌握了这些知识后,我们可以继续理解第2 部分中高斯溅射的“高斯”部分

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

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

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

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

相关文章

一文吃透,低代码是什么?盘点国内十大低代码平台,你用过哪个?

什么是低代码(Low Code)?低代码是怎么火的?国内十大低代码平台分别是谁?低代码项目开发流程是怎样的?低代码和无代码区别是啥?以及低代码的核心价值是什么?可以使用低代码开发平台创建哪些应用?…

el-cascader数据回显失败

el-cascader选中数据第一次回显正常,当选中数据改变再次回显时失败,呈现的还是上次的选中数据 如图 常用的方法this. n e x t T i c k ( ( ) > ) 跟 t h i s . nextTick(() > {})跟this. nextTick(()>)跟this.forceUpdate();强制刷新数据都无…

leetcode hot100_part30_二分查找

上次写博客已经一个月了,这段时间在做外卖项目,真的啥也没有就是个小玩具。 上次接触是在最长递增子序列的一个题解里,复习一下已经忘完了。如果我们在一个有序数组中进行查找某个target,一般肯定就是从小到大or从大到小遍历查找&…

js reduce 的别样用法

let mergedItems list.reduce((accumulator, currentItem) > {let existingItem accumulator.find((item) > item.manObject_name currentItem.manObject_name);if (existingItem) {existingItem.laborCostHand currentItem.laborCostHand; //劳务费existingItem.wor…

【网络】Socket编程

文章目录 正确理解端口号理解源IP地址和目的IP地址认识端口号端口号和进程ID 理解Socket网络字节序socket编程接口创建socket套接字bind绑定套接字listen建立监听accept接受连接connect建立连接sendto发送数据接收数据close关闭套接字 sockaddr结构体 正确理解端口号 理解源IP…

Windows 电脑部署 ollama3 并安装模型

Windows 电脑部署 ollama3 并安装模型 部署中为了尽可能减少对本地环境的污染,使用 Docker 安装! github: https://github.com/ollama/ollama 准备部署文件 version: 3.8services:ollama:volumes:- ./models:/root/.ollama # 将本地文件夹挂载到容器中…

独立游戏《星尘异变》UE5 C++程序开发日志7——实现加载界面

目录 一、创建自定义AssetManager类 二、异步加载资产 三、加载界面UI的实现 1.UI布局 2.在打开关卡前和进入关卡后创建UI并统计进度 有时可能会遇到关卡已经进去但依然会卡顿一段时间的情况,所以我们需要在进入关卡后,玩家可以操作关卡之前&#x…

RAG技术进化史:从初级到高级,再到模块化RAG架构的创新发展

大型语言模型(LLMs)通过在自然语言任务及其它领域的成功应用,如 ChatGPT、Bard、Claude 等所示,已经彻底改变了 AI 领域。这些 LLMs 能够生成从创意写作到复杂代码的文本。然而,LLMs 面临着幻觉、过时知识和不透明、无…

Codeforces Round 672 (Div. 2) C1. Pokémon Army (easy version) (DP)

不知道能不能用贪心,反正我是没看出来,所以用DP求解。 首先分析一下题意,我们要在一段序列中取出一段子序列,然后让这段子序列按顺序逐个先加后减最终得到的结果最大。 如果要用DP,那么我们首先就要思考怎么表示状态…

心好累,早点睡!永远不要提前焦虑——早读(逆天打工人爬取热门微信文章解读)

你们遇到心烦的事,会怎么排解呢? 引言Python 代码第一篇 洞见 永远不要提前焦虑第二篇 故事来源于生活结尾 引言 这两天天气都是阴雨 雨时而大 时而小 就是下个不停 老天还算给面子 上班路上总是细雨或者无雨 昨天晚上回来 山地车的前轮有些送动 马上发…

C++从入门到起飞之——this指针 全方位剖析!

个人主页:秋风起,再归来~ C从入门到起飞 个人格言:悟已往之不谏,知来者犹可追 克心守己,律己则安! 目录 1、this指针 2、C和C语⾔实现Stack对⽐ C实现Stack代码 C实现Stack代…

深度挖掘行情接口:股票市场中的关键金融数据API接口解析

在股票市场里,存在若干常见的股票行情数据接口,每一种接口皆具备独特的功能与用途。以下为一些常见的金融数据 API 接口,其涵盖了广泛的金融数据内容,其中就包含股票行情数据: 实时行情接口 实时行情接口&#xff1a…

光耦合器技术的实际应用

光耦合器也称为光隔离器,是现代电子产品中的关键组件,可确保电路不同部分之间的信号完整性和隔离。它们使用光来传输电信号,提供电气隔离和抗噪性。 结构和功能 光耦合器通常由以下部分组成: 1.LED(发光二极管&#…

Bone Collector-动态规划题解

Bone Collector Problem - 2602 (hdu.edu.cn)https://acm.hdu.edu.cn/showproblem.php?pid2602 Problem Description Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’…

Ubuntu18 中JDK的安装

文章目录 一、背景说明二、获取安装包三、安装JDK3.1 上传安装包3.2 复制和解压3.3 环境变量的设置3.4 验证安装 四、问题列表4.1 .bashrc文件在哪里?.bashrc是什么?4.2 为什么使用rz上传安装包时会报: 传输失败? 五、总结 一、背…

SpringBoot项目中将word转换为pdf

需求&#xff0c;用户将用户上传的word文件转换成为pdf格式&#xff0c;然后返回 第一步&#xff1a;引入依赖 <dependency><groupId>aspose</groupId><artifactId>aspose-words</artifactId><version>15.8.0-jdk16</version></…

走进标杆 | 宁波市领导一行至金鸡强磁开展专项调研活动

为深入了解supOS助力宁波打造数实融合标杆城市的实践成果&#xff0c;日前&#xff0c;宁波市领导和专家共赴蓝卓supOS标杆项目——宁波金鸡强磁股份有限公司&#xff08;以下简称“金鸡强磁”&#xff09;调研考察&#xff0c;蓝卓总经理助理杨明明、浙江大区副总经理章来胜等…

力扣3202:找出有效子序列的最大长度||

class Solution { public:int maximumLength(vector<int>& nums, int k) {int res0;for(int m0;m<k;m){//假设子序列两数%k之后的结果为m 相当于枚举vector<int> v(k,0);for(auto num:nums){v[num%k]v[(m-num%kk)%k]1; //知道m之后可以知道需要的子序列当前…

做外贸如何高效跟进转化客户

做外贸业务&#xff0c;基本上每天要做的几个事情&#xff0c;开发客户、跟进转化客户、维护客户等等&#xff0c;经常也有外贸新手朋友问我&#xff0c;遇到什么什么客户要怎么跟进&#xff0c;客户不回复怎么办等等&#xff0c;今天就来跟大家聊聊客户跟进转化这块的一些问题…

通俗易懂讲解电池的主要性能参数(下)

接上期内容&#xff0c;上次主要分享影响电池性能评估的两个主要性能参数&#xff1a;电池容量和电池功率。可以点击这里回顾&#xff1a;一起学习电池的主要性能参数和测试方法 今日接着分享电池性能评估的另外两个主要性能参数&#xff1a;电池的使用寿命和自放电率。 上期…