将点云转换为 3D 网格:Python 指南

news/2025/1/31 23:59:35/

3D 数据的世界往往是一个碎片化的景观。

存在点云,其细节丰富,但缺乏表面信息。

3D 网格,它明确地定义表面,但创建起来通常很复杂。

将点云转换为网格弥补了这一差距并开启了许多可能性,从真实模拟到 3D 数字环境设计。

即使您不使用网格进行渲染部分,拥有它也能让我们确保能够有效地模拟碰撞效果,计算 3D 建筑物中的可行走空间,或处理大型场景的遮挡剔除。

但是我们该怎么做呢?我们如何获取任意点云(来自摄影测量法、3D 高斯溅射法或激光扫描法)并生成可靠的 3D 网格?

让我们使用 Python 编写一种强大的 3D 点云网格化技术,并使其成为具有 GUI 的 Micro-Saas 应用程序。

3d3c42e38f32702bdea39203.png" width="800" />

🎯 任务目标

您是最后一位因为手头上不可能完成的任务而没有被解雇的 3D 工程师。您需要创建一个新的星球大战游戏作为 3D 高斯溅射场景。玩家是一个赫特人,根据用户上传到平台的场景向跳跃的绝地武士射击。

问题是,如果绝地武士位于拦截射线的物体后面,你就必须进行烘焙,并找到一种概括处理的方法。

绝地武士躲在一块岩石后面!在此阶段,您已经建立了工作流程。

您首先要开发一种稳健而有效的点云网格划分方法。您需要通过体素大小和 iso 级别等关键参数来控制网格划分过程,并根据您的需要定制输出。

除了单纯的转换之外,您还努力实现第二个目标:自动化。您希望让非程序员也能通过直观的 Web 界面轻松生成网格。

🦊 Florent这项任务的成功取决于您对基本原理的理解。您必须掌握 Marching Cubes 算法的精髓,通过巧妙分析虚拟网格来直观地了解它如何从点云中提取表面。您必须学会使用 NumPy、Open3D、scikit-image 和 SciPy 等 Python 库,并将它们运用到您的意愿中。最后,您必须拥抱 Gradio 的强大功能,设计一个用户友好的界面,让这项强大技术的使用变得民主化。

此外,您希望尽可能接近“点云”形状,因此决定避开心爱的体素(但您可以在文章末尾找到更多有关它们的信息)。让我们深入研究 3D 网格算法的深层概念漏洞:让我们揭开行进立方体的复杂性,并详细介绍将点云转换为网格的 Python 实现,例如下面的示例。

我们的 3D 建模应用程序的结果

🗝️ 3D 网格划分的 5 个关键点

  • 点云将 3D 形状表示为具有空间坐标和可选属性的点的集合。
  • 网格将 3D 形状表示为连通的三角形,明确定义表面。
  • 行进立方体算法弥补了这一空白,从点云创建了网格。
  • 参数调整和自动化对于最佳网格生成至关重要。
  • 构建 Web 应用程序使这项技术的访问变得民主化。

从点云到 3D 网格

想象一下捕捉一个物体的 3D 形状,使用激光扫描仪扫描其表面,生成点云

从激光扫描仪上对点云的表面进行采样。

该点云是数百万甚至数十亿个单独点的集合,并允许获得如下所示的 3D 数据集。

移动激光扫描仪的 3D 点云示例。每个点都包含其空间坐标(x、y、z)以及其他属性,例如颜色、强度或分类标签。点云是 3D 形状的原始直接表示,保留了精细的细节,但缺少明确的表面信息。

这是我们可以在 3D 数据表示上大展身手的地方:你可以将对象的形状表示为网。网格使用相互连接的三角形构建表面。

每个三角形由三个顶点定义,它们的连接形成近似于物体表面的网络。

3D 网格组件的定义。

网格明确地定义了表面,从而能够进行表面积和体积等计算,并通过逼真的灯光和阴影实现渲染。

然而,直接创建网格可能很复杂,并且通常依赖于从点云推断表面连接的算法。

让我们使用 Marching Cubes 算法来弥合这两种表示之间的差距,并在任何点云上获得结果,例如下面的结果。

让我们从 1(点云)到 2(3D 网格)。

深入探究 3D 网格的移动立方体算法

行进立方体算法由 Lorensen 和 Cline 于 1987 年开发,是一种从体积数据集创建网格的巧妙技术。

将点云视为嵌入在虚拟网格中,类似于 3D 棋盘。

该算法检查此网格中的每个体素。它的工作原理是想象一个隐式表面流经此网格。每个体素角落的标量场的值暗示了这个表面,但并不直接定义它。

3de1c7d4f64d67a364ceb2ca4.png" width="800" />

根据我们的体素值绘制具有相同标量场的表面图。

在我们的例子中,标量场表示从每个网格点(体素角)到输入点云中最近点的距离。

这里的关键概念是等高线等表面。想象一下地形图上的等高线。每条等高线连接等高点。

与地形图和等值线平行。

Marching Cubes 中的等值面工作原理类似,但以 3D 形式呈现。它连接网格中标量场具有相同值的点。该等值面代表了我们网格的“边界”。

对于每个体素,算法都会检查其八个角的标量场值。如果一个角的值高于 iso 级别,则认为它位于表面“外部”;如果低于 iso 级别,则认为它位于表面“内部”。

该算法根据“内”角和“外”角的组合确定等值面如何与体素相交。然后一个或多个三角形表示此交点并构成最终网格。

通过对网格中的每个体素重复此过程,该算法创建一个完整的网格,近似于点云定义的表面。

使用 Marching Cube 算法将 3D 点云转换为 3D 网格。

iso 级别的选择至关重要。低 iso 级别会生成紧密的网格,紧跟点云,可能捕捉到噪音和精细细节。高 iso 级别会创建更平滑、更通用的网格。

🦚注意我们 Python 实现中的 iso_level_percentile 参数控制此级别,表示为标量场中计算的距离的百分位数。 体素的大小由 voxel_size 决定,也会影响结果。较小的体素会产生更精细的网格,能够捕获更多细节,但需要更多计算。较大的体素会创建更粗糙的网格,计算速度更快,但可能会丢失细微特征。

步骤 1:3D Python 设置

让我们确保您拥有开始工作所需的一切。

库导入

此阶段涉及导入必要的 Python 库:用于数值运算的 NumPy、用于点云和网格处理的 Open3D、用于 Marching Cubes 实现的 scikit-image 以及用于空间数据结构和计算的 SciPy。

import numpy as np
import open3d as o3d
from skimage import measure
from scipy.spatial import cKDTree

收集 3D 点云

在这里,您可以收集要处理的点云数据。这可能涉及从磁盘读取文件(例如 .ply、.las、.xyz 格式)或从其他来源获取数据。每个点云通常表示为一个数据结构,其中包含每个点的 x、y 和 z 坐标,以及任何其他属性,例如颜色或强度。

点云数据集说明了我使用的 3D 网格划分流程。

🦚注意我建议从小点云开始来测试该方法的参数。

3D Python 库、软件、工具和资源

下面,我列出了我们将在本教程中利用的所有各种工具和库。

  • Anaconda: https://www.anaconda.com/——一个流行的数据科学和机器学习 Python 分发平台,提供包管理和环境控制。

pip然后,可以通过(Python 包安装程序)在 Anaconda 环境或任何其他 Python 环境中安装一组 5 个 Python 库。

  • NumPy: https://numpy.org/——Python中数值计算的基本库,提供强大的数组操作。
  • Open3D: https: //www.open3d.org/——专为 3D 数据处理设计的库,包括点云和网格操作、可视化和算法。
  • scikit-image: scikit-image: Image processing in Python — scikit-image — 基于 NumPy 构建的图像处理库,提供图像分析、分割等工具(包括 Marching Cubes 实现)。
  • SciPy: https ://scipy.org/ — 基于 NumPy 构建的库,提供高级科学计算功能,包括 KD-Trees 等空间数据结构。
  • Gradio:https: //gradio.app/——用于创建机器学习模型用户界面的 Python 库,允许您构建交互式 Web 演示。

最后,有一个开源软件:

  • CloudCompare: CloudCompare - Open Source project — 开源 3D 点云和网格处理软件,可用于可视化、分析和编辑 3D 数据。它还具有用于批处理的命令行界面。

漂亮;一旦你根据自己的喜好设置了环境,我们就可以进入第二阶段:Marching Cube 的实现。

步骤 2:Marching Cubes 三维网格划分函数

这是整个过程的核心,也是行进立方体的神奇之处发生的地方。

让我详细介绍点云到 3D 网格策略的核心组件。我们分为 7 个阶段(a 到 g),如下图所示。

我们的函数内部工作用于创建点云的行进立方体 3D 网格。这些阶段是我们功能的一部分,它接受以下两个参数:

3dc9" style="text-align:start">
voxel_size=0.1 
iso_level_percentile=20

太好了,我不会在那里创造更多的吸引力,但让我详细介绍一下 3D 行进立方体功能中发生的所有七个步骤:

a. Open3D 到 PCD

我们用 Open3D 加载点云,然后将其作为 numpy 数组放进去。

pcd = o3d.io.read_point_cloud(dataset)# Convert Open3D point cloud to numpy array
points = np.asarray(pcd.points)

这确保了与向下流程的兼容性。

b. 边界计算

让我们确定点云沿每个轴(x、y、z)的最小和最大范围。这定义了将包围我们的体素网格的边界框。

# Compute the bounds of the point cloud
mins = np.min(points, axis=0)
maxs = np.max(points, axis=0)

c. 为 3D 点云创建 3D 网格

让我们在边界框内创建一个规则的 3D 网格或体素网格。

voxel_size参数决定网格点之间的间距;该网格中的每个单元都是一个体素。

# Create a 3D grid
x = np.arange(mins[0], maxs[0], voxel_size)
y = np.arange(mins[1], maxs[1], voxel_size)
z = np.arange(mins[2], maxs[2], voxel_size)
x, y, z = np.meshgrid(x, y, z, indexing='ij')

🦚注意此代码创建了一个用于空间计算的三维网格。它首先使用 生成三个单独的一维数组(x、y 和 z) ,这将创建从到np.arange()均匀分布的值,每个维度的步长为。然后,获取这些一维数组并将它们转换为三个相同形状的三维数组,其中网格中的每个点都包含其对应的 x、y 和 z 坐标。参数确保数组索引遵循矩阵约定。结果是三个数组共同定义三维网格中的所有点,使您可以使用所有三个数组中的相同索引访问任何点的坐标:()。minsmaxsvoxel_sizenp.meshgrid()(i,j,k)indexing=’ij’x[i,j,k], y[i,j,k], z[i,j,k]

d. KD-Tree 效率

现在是时候从点云数据构建 KD 树(k 维树)了。简而言之,KD 树是一种高效的空间数据结构,有助于快速进行最近邻搜索。但在这个阶段,你应该记住这一点。😉

# Create a KD-tree for efficient nearest neighbor search
tree = cKDTree(points)

是的,就是这么简单。我们的树已经建好了;让我们利用这棵美丽的树。

e.标量场:到最近点的距离

对于每个网格点(体素的角),我们将使用 KD-Tree 计算到点云中最近点的距离。

该距离值成为该网格点处的标量场值,如下所示:

# Compute the scalar field (distance to nearest point)
grid_points = np.vstack([x.ravel(), y.ravel(), z.ravel()]).T
distances, _ = tree.query(grid_points)
scalar_field = distances.reshape(x.shape)

🦚注意此代码将三个 3D 坐标数组转换为 2D 点数组。该函数ravel()首先将每个 3D 数组展平为 1D 数组,然后np.vstack()垂直堆叠这些展平的数组以创建一个 3×N 数组,其中 N 是点的总数(nx * ny * nz)。最后,.T转置此数组以获得一个 N×3 数组,其中每行代表一个具有 (x,y,z) 坐标的点。

f. 确定 Iso 级别

好了,现在该关注 ISO 级别了。ISO 级别定义了 Marching Cubes 算法的表面阈值。它是按上一步计算的距离的百分位数计算的。iso_level_percentile参数控制这一点,我将在下面进行说明。

我们的体素网格上的等值线。

# Determine iso-level based on percentile of distances
iso_level = np.percentile(distances, iso_level_percentile)

通过这个小代码片段,我们利用函数来找到给定百分比( )以下距离的np.percentile()值。iso_level_percentile

例如,如果iso_level_percentile为 50,它会找到中位距离值;如果为 75,它会找到 75% 的距离较低的值。

🌱增长我通常在表面重建中使用它来决定将表面放置在何处;距离低于此阈值的点将被视为表面“内部”,而距离高于此阈值的点将被视为“外部”。这是一种根据距离测量分布自动确定良好截止值的方法,而不是设置任意固定阈值。

g. 应用行进立方体

我们快完成了:我们现在可以调用我们的行进立方体函数。skimage.measure.marching_cubes 函数将标量场和 iso 级别作为输入。

# Apply Marching Cubes
verts, faces, _, _ = measure.marching_cubes(scalar_field, level=iso_level)

该函数分析每个体素,并根据等值面与体素的相交方式生成三角形。

我们终于准备好进行 3D 网格后处理了。

步骤 3.3D 网格后处理

到此阶段,我们的网格几乎已准备就绪。我们希望她(为什么不呢?)返回到其原始位置并生成 3D 网格对象。让我们首先解决 3D 变换。

3D 变换:缩放和平移

生成的三角形的顶点最初位于体素网格坐标中。此步骤将顶点缩放并平移回原始点云坐标系。我们可以按照下图所示移动。

3D 变换图示:缩放(红色)和平移(绿色)。

使用 Python 代码表示:

# Scale and translate vertices back to original coordinate system
verts = verts * voxel_size + mins

漂亮极了!现在,我们可以从点云生成 3D 网格了。

创建 3D 网格

让我们创建一个 Open3D TriangleMesh 对象,其中包含缩放和平移的顶点和三角形连接信息(面)。我们可以使用 Open3D 来执行此操作,如下所示。

# Create mesh
mesh = o3d.geometry.TriangleMesh()
mesh.vertices = o3d.utility.Vector3dVector(verts)
mesh.triangles = o3d.utility.Vector3iVector(faces)

从那里,我们可以利用一个额外的步骤:法线。

计算 3D 网格法线

为了用法线填充 3D 网格,让我们计算顶点法线。这些法线对于渲染过程中的正确照明和阴影至关重要,使网格看起来平滑。

# Compute vertex normals
mesh.compute_vertex_normals()

漂亮极了!在此阶段,我们已准备好继续可视化 3D 网格。

🦚注意整个过程取决于两个定义的参数,即体素大小和等百分位数选择。第一个参数,即体素大小,将极大地影响计算时间,尤其是对于大点云。第二个参数也会影响计算时间,但影响较小。然而,它的几何影响是显著的。

步骤 4.3D 网格可视化

您一直在等待的时刻已经到了:我们现在可以使用以下命令可视化我们的 3D 网格:

# Visualize the result
o3d.visualization.draw_geometries([mesh], mesh_show_back_face=True)

结果如下:

3de0c671c1ffbb6b05021983bdb5.png" width="800" />

我们从点云中获取的具有各种参数的树的 3D 网格的几个结果。

使用 Open3D 的可视化功能显示生成的网格,我们可以检查结果。如您所见,3D 树网格的网格划分非常有趣。

🦚 注意: 您可以将网格保存到文件(例如 .ply、.obj、.stl)以便在其他应用程序中使用,例如如下所示的 Blender。

3dfa81948e57dc5d.png" width="800" />

我们在 Blender 中对米兰室内大教堂的整个点云的 3D 网格进行处理的结果。© Florent Poux

步骤5.自动参数估计

为了使网格划分过程自动化,您可以估算voxel_size和的合适值iso_level_percentile。如果您还记得的话,我们在开始时就将它们初始化为这些值,没有太多解释:

voxel_size=0.1 
iso_level_percentile=20

但让我们实际讨论一种自动找到最佳参数的方法。

🦚注意这些都是高级技术。最重要的是,大规模优化对于大数据集也至关重要,例如分块(以较小的块处理点云)、并行化和高效的数据结构,以显著提高性能。这些技术在 3D Segmentor OS中进行了讲解。

为了指导您完成此过程,我建议首先估算体素大小。您可以使用点云中与 k 个最近邻居的平均距离,通过一些启发式方法定义体素大小。

然后,为了估计等值百分位数,您可以使用标量场的距离分布来确定合适的百分位数。这需要根据距离 ( ) 计算变异系数 (CV) standard deviation / mean

最后,您可以根据您的 CV 调整百分比。较高的 CV 值(分散点更多)通常会导致较低的百分位数,反之亦然。

步骤 6. 使用 Gradio 创建 Web 应用程序

让我们使用 Gradio 构建一个用户友好的 Web 界面。该界面允许用户上传点云文件、调整参数(体素大小、iso 级别)并在浏览器中直接可视化生成的网格。这使网格划分过程的访问变得民主化,甚至非程序员也可以访问它。

要使用 Gradio 构建一个简单的 GUI,我们通常遵循一个模式:

import gradio as gr
with gr.Blocks() as app:# ... (Gradio interface setup)

我们的目标是创建一个拖放界面来上传点云并直接在浏览器中生成网格,如下所示。

我们的网络应用程序可将任何点云转换为 3D 网格。© Florent Poux

为了获得这样的结果,对于 3D 模型视图,您可以使用以下代码:

# Create Gradio interface
iface = gr.Interface(fn=point_cloud_to_mesh,inputs=gr.File(label="Upload Point Cloud",file_types=[".pcd", ".ply", ".xyz", ".pts"],),outputs=gr.Model3D(clear_color=[0.0, 0.0, 0.0, 0.0],  label="3D Model"),title="Point Cloud to Mesh Converter",examples=[],cache_examples=False,)

此代码使用 Gradio 创建基于 Web 的用户界面,旨在将点云文件转换为 3D 网格。该界面配置了一个文件上传组件,该组件明确接受点云文件(.pcd、.ply、.xyz 或 .pts 格式)并将其显示在具有透明背景的 3D 模型查看器中。

该处理由调用的函数处理point_cloud_to_mesh,该函数包含我们之前解释过的实际转换逻辑。您可以将这些行添加到我们的 Python 文件中:

# Launch the interface
if __name__ == "__main__":iface.launch()

直接运行脚本时,它会启动我们的网页“点云到网格转换器”,用户可以在其中上传他们的点云文件并在交互式查看器中查看生成的 3D 网格。

 


http://www.ppmy.cn/news/1568283.html

相关文章

CAPL与外部接口

CAPL与外部接口 目录 CAPL与外部接口1. 引言2. CAPL与C/C++交互2.1 CAPL与C/C++交互简介2.2 CAPL与C/C++交互实现3. CAPL与Python交互3.1 CAPL与Python交互简介3.2 CAPL与Python交互实现4. CAPL与MATLAB交互4.1 CAPL与MATLAB交互简介4.2 CAPL与MATLAB交互实现5. 案例说明5.1 案…

【2024年华为OD机试】(C卷,200分)- 启动多任务排序 (JavaScriptJava PythonC/C++)

一、问题描述 题目解析 本题是一个典型的拓扑排序问题。拓扑排序用于解决有向无环图(DAG)中的节点排序问题,使得对于图中的每一条有向边 (u, v),u 在排序中总是位于 v 的前面。在本题中,任务之间的依赖关系可以看作是有向图中的边,而任务的执行顺序就是拓扑排序的结果。…

RK3588平台开发系列讲解(ARM篇)ARM64底层中断处理

文章目录 一、异常级别二、异常分类2.1、同步异常2.2、异步异常三、中断向量表沉淀、分享、成长,让自己和他人都能有所收获!😄 一、异常级别 ARM64处理器确实定义了4个异常级别(Exception Levels, EL),分别是EL0到EL3。这些级别用于管理处理器的特权级别和权限,级别越高…

SOME/IP--协议英文原文讲解4

前言 SOME/IP协议越来越多的用于汽车电子行业中,关于协议详细完全的中文资料却没有,所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块: 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 4.1.3 End…

【Rust自学】15.0. 智能指针(序):什么是智能指针及Rust智能指针的特性

喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 15.0.1 指针的基本概念 指针是一个变量在内存中包含的是一个地址,指向另一个数据。 Rust 中最常见的指针是引用&#xff0c…

【含开题报告+文档+PPT+源码】基于SpringBoot的校园跑腿管理系统

开题报告 本文旨在探讨校园跑腿系统的设计与实现,通过深入研究与分析,实现了一套包含用户管理、发布跑腿单、跑腿抢单、跑腿单评论、在线留言以及用户在线充值等功能的综合性系统。该系统以提高校园内物品跑腿与配送效率为核心目标,为广大学…

【C语言】函数递归

目录 1. 什么是递归 1.1 递归的思想: 1.2 递归的限制条件 2. 递归的限制条件 2.1 举例1:求n的阶乘 2.1.1 分析和代码实现 2.1.2 画图推演 2.2 举例2:顺序打印⼀个整数的每⼀位 2.2.1 分析和代码实现 2.2.2 画图推演 3. 递归与迭代…

Spring AI 在微服务中的应用:支持分布式 AI 推理

1. 引言 在现代企业中,微服务架构 已成为开发复杂系统的主流方式,而 AI 模型推理 也越来越多地被集成到业务流程中。如何在分布式微服务架构下高效地集成 Spring AI,使多个服务可以协同完成 AI 任务,并支持分布式 AI 推理&#x…