首页 > 编程语言 >Python 工程师对 3D 高斯溅射的介绍(第 1 部分)

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

时间:2024-07-21 10:26:28浏览次数:12  
标签:matrix Python 溅射 image torch 矩阵 相机 Tensor 3D

从 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)

标签:matrix,Python,溅射,image,torch,矩阵,相机,Tensor,3D
From: https://blog.csdn.net/2401_85233349/article/details/140542225

相关文章

  • 使用Python读取PDF文件,部分内容显示为一串乱码。我应该如何恢复它?
    使用Python读取PDF文件,部分内容显示为一串乱码。我该如何恢复它?importfitzdoc=fitz.open("2303.11366v4.pdf")#downloadfromhttps://arxiv.org/pdf/2303.11366print(doc[2].get_text().split('Figure1')[0])我得到了这样的文字:<RXDUHLQWKHPLGGOHRIDURRP>@7DVN......
  • Python 迭代列表
    分配sum_extra给定列表test_grades收到的额外学分总额。满分是100分,所以超过100分都是额外分。对于给定程序,sum_extra是8,因为1+0+7+0是8。给定程序的示例输出:额外总和:8请原谅我,我是编码新手,而且真的很糟糕!这是我的代码(不起作用)请......
  • 计算机毕业设计Python+Spark新能源汽车推荐系统 汽车大数据 汽车数据分析 汽车可视化
    表2黄河交通学院本科毕业设计(论文)开题报告学生姓名刘丹杰专业班级20本大数据一班学号2080910T01521设计(论文)题目基于Hadoop的新能源汽车销售数据分析系统的设计与实现选题的目的和意义:选题目的:新能源汽车销售数据分析系统的设计与实现旨在利用Hadoop等大数......
  • 为什么我的 python 程序一直说没有名为“PIL”的模块?
    我正在pythonIDLEshell中工作,由于某种原因pyautogui.locateOnScreen将无法工作。这是我的代码:frompyautoguiimport*importpyscreezeimportpyautoguiimporttimeimportkeyboardimportrandomimportwin32api,win32conwhile1:ifpyautogui.locateOnS......
  • 我在 Python 时间格式化函数中遇到代码问题
    我一直在研究一个Python函数,将给定的秒数转换为可读的时间格式(HH:MM:SS)。该函数对于大多数测试用例都能正常工作,但对于一些特定的输入会失败。这是我编写的函数:defmake_readable(seconds):ifseconds<60:s1=secondsh1,m1=(0,0)return......
  • 《流畅的Python》第二版 第11章
     fromarrayimportarrayimportmathclassVector2d:__match_args__=('x','y')typecode='d'def__init__(self,x,y):self.__x=float(x)self.__y=float(y)@propertydefx(self)......
  • 《流畅的Python》第二版 第12章
       fromarrayimportarrayimportmathimportfunctoolsimportitertoolsimportoperatorimportreprlibclassVector:typecode='d'def__init__(self,components):self._components=array(self.typecode,components)......
  • 如何在Python中给jupyter单元标准输入?
    我正在尝试在接受用户输入的jupyter笔记本上运行一个程序,但我不知道如何让它读取标准输入。例如,如果我使用shift-enter:a=input()print(a)运行代码,则单元格指示它正在运行,但不接受我的输入。我如何让它接受输入?你遇到的问题是Jupyternotebook中的代码单元默认......
  • Python:如何从 csvreader 列表中删除括号和单引号?
    Pythonn00b在这里。尝试使用csvreader从文件导入数组并打印一个值,但它添加了括号和单引号。这是我的代码:importrandomimportcsvwithopen('crimes.csv','r')ascsvfile:crimes=list(csv.reader(csvfile))hello=["Hello","Greetings","Hi&q......
  • Python 中的多行输入,支持空行并在控制台中检查“\n”
    您好,亲爱的社区,在解决某个竞赛任务时出现了问题。我解决了它,但由于输入棘手而无法交付。我在谷歌上搜索并尝试了几种找到的方法,但如果应用于此任务,它们中的每一种都有一些弱点。而且我无法完全重现所应用的输入,因为它嵌入在竞赛界面中;我可能只依赖描述。这就是为什么我......