# 3D 文件
# 点云文件
点云文件是一种用于存储点云数据的文件格式。通常包括点云的坐标、颜色等信息。
默认情况下,Open3D 支持以下点云文件格式:
Format | 说明 |
---|---|
xyz |
纯文本格式,每行包含一个点的坐标(x, y, z) |
xyzn |
纯文本格式,每行包含一个点的坐标(x, y, z)和法线(nx, ny, nz) |
xyzrgb |
纯文本格式,每行包含一个点的坐标(x, y, z)和颜色(r, g, b) |
pcd |
PCD 文件格式,由 PCL(Point Cloud Library)定义 |
ply |
PLY 文件格式,由 PLY(Polygon File Format)定义 |
obj |
OBJ 文件格式,由 3D 建模软件定义 |
o3d.io.read_point_cloud(pcd,format)
函数未指定 format
参数时,Open3D 会根据文件扩展名自动推断文件格式。
import open3d as o3d | |
# 读取点云文件 | |
pcd = o3d.io.read_point_cloud("path_to_point_cloud_file.pcd") | |
# 显示点云 | |
o3d.visualization.draw_geometries([pcd]) |
代码直接构造点云数据
import open3d as o3d | |
import numpy as np | |
# 定义点坐标 | |
vertices = np.array([ | |
[0, 0, 0], # 点 0 | |
[100, 0, 0], # 点 1 | |
[0, 100, 0], # 点 2 | |
[0, 0, 100] # 点 3 | |
], dtype=np.float64) | |
# 创建点云对象 | |
pcd = o3d.geometry.PointCloud() | |
pcd.points = o3d.utility.Vector3dVector(vertices) | |
o3d.visualization.draw_geometries([pcd], window_name="直接构造的点云") |
# 网格文件
网格文件是一种用于存储三维网格数据的文件格式。通常包含多边形、顶点、法线、纹理等信息。
默认情况下,Open3D 支持以下网格文件格式:
文件后缀 | 说明 |
---|---|
ply |
PLY 文件格式,由 PLY(Polygon File Format)定义 |
obj |
OBJ 文件格式,由 3D 建模软件定义 |
stl |
STL 文件格式,由 STL(Stereolithography)定义 |
off |
OFF 文件格式,由 OFF(Object File Format)定义 |
o3d.io.read_triangle_mesh(mesh,format)
函数未指定 format
参数时,Open3D 会根据文件扩展名自动推断文件格式。
import open3d as o3d | |
# 读取网格文件 | |
mesh = o3d.io.read_triangle_mesh("path_to_mesh_file.ply") | |
# 显示网格 | |
o3d.visualization.draw_geometries([mesh]) |
代码直接构造点网格数据
注意: 网络数据定义面时,(右手定则)大拇指为面的法线方向,即可视化所展示的面。
import open3d as o3d | |
import numpy as np | |
# 定义顶点坐标 | |
vertices = np.array([ | |
[0, 0, 0], # 点 0 | |
[100, 0, 0], # 点 1 | |
[0, 100, 0], # 点 2 | |
[0, 0, 100] # 点 3 | |
], dtype=np.float64) | |
# 创建点云对象 | |
# 定义面(每个面由 3 个顶点索引组成) | |
triangles = np.array([ | |
[0, 2, 1], # 面 0: 点 0-2-1 | |
[0, 1, 3], # 面 1: 点 0-1-3 | |
[0, 3, 2], # 面 2: 点 0-3-2 | |
[1, 2, 3] # 面 3: 点 1-2-3 | |
], dtype=np.int32) | |
# 创建网格对象 | |
mesh = o3d.geometry.TriangleMesh() | |
# 设置顶点坐标和面索引 | |
mesh.vertices = o3d.utility.Vector3dVector(vertices) | |
mesh.triangles = o3d.utility.Vector3iVector(triangles) | |
# 可选:计算法线并可视化 | |
mesh.compute_vertex_normals() | |
o3d.visualization.draw_geometries([mesh], window_name="直接构造的网格") |
# 文件内容
# PCD
PCD 文件是一种常用的点云文件格式,由 PCL(Point Cloud Library)定义。PCD 文件可以包含点云的坐标、颜色、法线等信息。
PCD 文件头的具体内容如下:
VERSION
指定 PCD 文件的版本,目前支持 PCD_V0.7 和 PCD_V0.7_BINARY 两种版本。
FIELDS
指定点云数据中包含的字段,每个字段用空格分隔。
示例:
字段内容 | 含义 |
---|---|
FIELDS x y z |
x 、 y 、 z 三个坐标字段 |
FIELDS x y z rgb |
x 、 y 、 z 三个坐标字段 + rgb 颜色字段(深度相机) |
FIELDS x y z normal_x normal_y normal_z |
x 、 y 、 z 三个坐标字段 + normal_x 、 normal_y 、 normal_z 三个法线字段 |
FIELDS j1 j2 j3 |
j1 、 j2 、 j3 三个字段(例如激光雷达) |
SIZE
指定每个维度的大小
示例:
字段 | 说明 |
---|---|
SIZE 4 4 4 |
每个字段大小为 4 字节 |
SIZE 4 4 4 4 |
每个字段大小为 4 字节 |
TYPE
指定每个字段的类型,每个字段用空格分隔。
示例:
字段 | 说明 |
---|---|
TYPE F F F |
每个字段类型为浮点型 |
TYPE U U U |
每个字段类型为无符号整型 |
TYPE I I I |
每个字段类型为有符号整型 |
COUNT
指定每个维度的元素数量,每个字段用空格分隔。例如,x 数据通常有 1 个元素,但像 VFH 这样的特征描述符有 308 个元素。
WIDTH
指定点云数据的宽度,有序有结构表示行数,无需无结构等价 POINTS
。
HEIGHT
指定点云数据的高度,表示点云数据行数(无序无结构为 1)。
VIEWPOINT
为数据集中的点指定采集视点。这可能会在以后用于构建不同坐标系之间的变换,或用于帮助处理需要一致方向的特征,如曲面法线。
它由平移(tx-ty-tz)加上旋转四元数(qw-qx-qy-qz)组成。默认值为:
VIEWPOINT 0 0 0 1 0 0 0 |
POINTS
指定点云数据中点的数量。
DATA
指定点云数据的存储方式自版本 0.7 起,支持三种数据类型: ascii
、 binary
和 binary_compressed
。
ascii
每一行代表一个点的属性,由于是ascii
码因此可以直接阅读,缺点就是占用空间大,空的点用NaN
表示binary
其中数据是pcl::PointCloud.points
数组 / 向量的完整内存副本。在 Linux 系统上,我们使用mmap/munmap
操作对数据进行最快的读 / 写访问。binary_compressed
该文件(头部之后的所有内容)以一个 32 位无符号二进制数开始,该二进制数指定以压缩形式存在的数据的字节大小。接下来是另一个 32 位无符号二进制数,该二进制数指定未压缩数据的字节大小。然后是压缩后的数据。压缩和解压缩都使用Marc Lehmann
的LZF
算法。该算法在压缩率方面表现一般,但是速度非常快。对于典型的点云,压缩后的数据大小为原始大小的 30% 到 60%。在压缩之前,数据会被重新排序以改善压缩效果,从标准的结构数组布局变为数组结构布局。例如,一个包含三个点和x
、y
、z
字段的云将从xyzxyzxyz
重新排序为xxxyyyzzz
。
完整实例:
# PCD 文件头 | |
VERSION .7 | |
FIELDS x y z | |
SIZE 4 4 4 | |
TYPE F F F | |
COUNT 1 1 1 | |
WIDTH 27895 | |
HEIGHT 1 | |
VIEWPOINT 0 0 0 1 0 0 0 | |
POINTS 27895 | |
DATA ascii | |
4.5875902 37.243198 46.776299 | |
3.7279501 37.171902 46.5345 | |
2.84834 37.056301 46.309101 | |
...... |
# PLY
PLY 文件是一种常用的多边形文件格式,由 PLY(Polygon File Format)定义。PLY 文件可以包含多边形、顶点、法线、颜色等信息。
文件头
PLY 文件头包含以下信息:
- 文件格式:
format ascii 1.0
或format binary_little_endian 1.0
或format binary_big_endian 1.0
- 元素类型:
element vertex 1000
或element face 1000
- 属性:
property float x
或property float y
或property float z
或property uchar red
或property uchar green
或property uchar blue
- 结束:
end_header
字段 | 说明 |
---|---|
ply |
表示文件格式为 PLY。 |
format |
指定文件格式,可以是 ascii 、 binary_little_endian 或 binary_big_endian 。 |
comment |
表示注释,可以包含任何文本。 |
element |
指定元素类型,可以是 vertex 或 face 。 |
property |
指定属性,可以是 float 、 int 、 uchar 等。 |
end_header |
表示文件头结束。 |
完整实例
ply | |
format binary_little_endian 1.0 | |
comment VCGLIB generated | |
element vertex 15934 | |
property double x | |
property double y | |
property double z | |
property uchar red | |
property uchar green | |
property uchar blue | |
property uchar alpha | |
element face 31390 | |
property list uchar int vertex_indices | |
end_header | |
# vertex | |
-0.015828 0.011844 0.0 | |
255 255 255 255 | |
...... | |
-0.015828 0.011844 0.0 | |
255 255 255 255 | |
# face (首位表示此面所有点的数量,此处是三个点索引构成的面) | |
3 0 1 2 | |
3 3 4 5 | |
3 6 7 8 | |
...... | |
3 15631 15632 15633 |
# 点云操作
# 安装 Open3D
pip install open3d |
# 读取点云
o3d.io.read_point_cloud
函数可以读取点云文件,并返回一个 open3d.geometry.PointCloud
对象。
函数定义如下:
def read_point_cloud(filename, format='auto', remove_nan_points=False, remove_infinite_points=False, print_progress=False): | |
""" | |
Returns: | |
open3d.cpu.pybind.geometry.PointCloud | |
""" |
参数 | 说明 |
---|---|
filename |
点云文件的路径。 |
format |
点云文件的格式,默认为 auto ,表示自动检测文件格式,支持 pcd 、 ply 、 xyz 等格式。 |
remove_nan_points |
是否移除包含 NaN 值的点,默认为 False 。 |
remove_infinite_points |
是否移除包含 Inf 值的点,默认为 False 。 |
print_progress |
是否打印读取进度,默认为 False 。 |
import open3d as o3d | |
pcd = o3d.io.read_point_cloud("test.pcd") |
# 写入点云
o3d.io.write_point_cloud
函数可以写入点云文件,并返回一个 open3d.geometry.PointCloud
对象。
函数定义如下:
def write_point_cloud(filename, pointcloud, format='auto', write_ascii=False, compressed=False, print_progress=False): # real signature unknown; restored from __doc__ | |
""" | |
Returns: | |
bool | |
""" | |
return False |
参数 | 说明 |
---|---|
filename |
点云文件的路径。 |
pointcloud |
要写入的点云对象。 |
format |
点云文件的格式,默认为 auto ,表示自动检测文件格式。 |
write_ascii |
是否以 ASCII 格式写入,默认为 False 。 |
compressed |
是否以压缩格式写入,默认为 False 。 |
print_progress |
是否打印写入进度,默认为 False 。 |
o3d.io.write_point_cloud("test.pcd", pcd) |
# 显示点云
o3d.visualization.draw_geometries
函数可以显示点云。
函数定义如下:
def draw_geometries(geometry_list, open3d_cpu_pybind_geometry_Geometry=None, *args, **kwargs): |
参数 | 说明 |
---|---|
geometry_list |
要显示的几何对象列表。 |
window_name |
窗口标题,默认为 “Open3D”。 |
width |
窗口宽度,默认为 1920。 |
height |
窗口高度,默认为 1080。 |
left |
窗口左边界,默认为 50。 |
top |
窗口上边界,默认为 50。 |
point_show_normal |
是否显示点法线,默认为 False, 如果为 True,需要事先计算点云法线 |
mesh_show_wireframe |
是否显示网格线框,默认为 False。 |
mesh_show_back_face |
是否显示网格背面,默认为 False。 |
lookat |
相机观察点,默认为 None。 |
up |
相机上方向,默认为 None。 |
front |
相机前方向,默认为 None。 |
zoom |
相机缩放比例,默认为 None。 |
import open3d as o3d | |
pcd = o3d.io.read_point_cloud("test.pcd") | |
o3d.visualization.draw_geometries([pcd], window_name="Open3D") |
# 点云数据结构
# KDTree
o3d.geometry.KDTreeFlann
是 Open3D 中用于实现 k-d 树的类。k-d 树是一种用于多维空间中快速搜索最近邻点的数据结构。
o3d.geometry.KDTreeFlann
: k-d 树类,用于实现 k-d 树。search_knn_vector_3d
:在点云中搜索指定点的 k 个最近邻点。search_radius_vector_3d
:在点云中搜索指定点半径内的所有点。search_hybrid_vector_3d
:在点云中搜索指定点 k 个最近邻点和半径内的所有点。
import open3d as o3d | |
pcd = o3d.io.read_point_cloud("cat.pcd") | |
pcd_tree = o3d.geometry.KDTreeFlann(pcd) | |
# 将点云设置为灰色 | |
pcd.paint_uniform_color([0.5, 0.5, 0.5]) | |
# 搜索某点的 K 个最近邻点 | |
k = 10 | |
[num_k, idx, _] = pcd_tree.search_knn_vector_3d(pcd.points[100], k) | |
np.asarray(pcd.colors)[idx[1:], :] = [1, 0, 0] # 将最近邻点的颜色设置为红色 | |
print("所得K邻域点索引点数为:", num_k) | |
# 搜索每个点半径为 r 的所有点 | |
r = 0.1 | |
[_, idx, _] = pcd_tree.search_radius_vector_3d(pcd.points[500], r) | |
np.asarray(pcd.colors)[idx[1:], :] = [0, 1, 0] # 将半径内的点的颜色设置为绿色 | |
# 搜索每个点的 k 个最近邻点和半径为 r 的所有点 | |
k = 10 | |
r = 0.1 | |
[_, idx_knn, _] = pcd_tree.search_hybrid_vector_3d(pcd.points[100], k, r) | |
np.asarray(pcd.colors)[idx[1:], :] = [0, 0, 1] # 将半径内的点的颜色设置为蓝色 | |
o3d.visualization.draw_geometries([pcd], window_name="Open3D") |
# Octree
八叉树(Octree)是一种用于三维空间中的数据结构,它将空间划分为 8 个子空间,每个子空间再继续划分,直到达到指定的深度。八叉树可以用于点云数据的快速搜索和可视化,例如最近邻搜索、空间分割等。
o3d.geometry.Octree(max_depth=int)
: 八叉树类,用于实现八叉树。max_depth
:八叉树的最大深度。
convert_from_point_cloud
:将点云转换为八叉树。
import open3d as o3d | |
pcd = o3d.io.read_point_cloud("cat.pcd") | |
# 创建八叉树 | |
octree = o3d.geometry.Octree(max_depth=5) | |
# 将点云转换为八叉树 | |
octree.convert_from_point_cloud(pcd) | |
o3d.visualization.draw_geometries([octree]) |
体素栅格构建 Octree
使用 create_from_voxel_grid
方法从体素栅格创建八叉树。
import open3d as o3d | |
import numpy as np | |
pcd = o3d.io.read_point_cloud("cat.pcd") | |
# 创建一个体素栅格 | |
voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd, voxel_size=0.05) | |
# 创建八叉树 | |
octree = o3d.geometry.Octree(max_depth=5)# max_depth: 八叉树的最大深度 | |
octree.convert_from_voxel_grid(voxel_grid) | |
o3d.visualization.draw_geometries([octree]) |
K-D树
和 八叉树
具体适用场景分析:
- K-D 树的优势场景
- 高维数据处理
- 高效的最近邻搜索
- 范围查询
- 低内存占用
- 八叉树的优势场景
- 纯 3D 空间的均匀划分
- 体素化与多分辨率处理
- 碰撞检测与空间占用查询
- 点云压缩与降采样
- 实际应用中的选择策略
- 优先使用 K-D 树的情况:
- 需频繁进行最近邻搜索(如 ICP 配准、法线估计);
- 点云包含非空间维度信息(如颜色、强度);
- 数据分布不均匀,需要自适应划分提高搜索效率。
- 优先使用八叉树的情况:
- 处理纯 3D 空间数据,且需要快速划分空间(如体素化、空间索引);
- 涉及多分辨率分析或三维重建;
- 需要高效的碰撞检测或空间占用查询。
- 混合使用的场景:
- 在复杂任务中,两者可结合使用。例如:
- 先用八叉树进行粗粒度空间划分,再对每个叶节点内的点构建 K-D 树进行精细搜索;
- 在实时 SLAM 系统中,用八叉树管理全局地图的空间索引,用 K-D 树进行局部点云的特征匹配。
总结
K-D树
更适合处理高维数据和非均匀分布的点云,在最近邻搜索和范围查询中表现优异;八叉树
则在纯 3D 空间划分、体素化和多分辨率处理中具有天然优势。
# 降采样
# 点云法线估计
点云法线估计是点云处理中的一项重要任务,它可以帮助我们理解点云的几何形状和结构。点云法线估计的目的是计算每个点的法线向量,法线向量垂直于点云表面的切平面。
o3d.geometry.PointCloud.estimate_normals
函数可以估计点云的法线。
函数定义如下:
def estimate_normals(self, search_param=None, fast_normal_computation=False, print_progress=False): # real signature unknown; restored from __doc__ | |
""" | |
Returns: | |
open3d.cpu.pybind.geometry.PointCloud | |
""" |
参数 | 说明 |
---|---|
search_param |
搜索参数,默认为 None。 |
fast_normal_computation |
是否使用快速法线计算,默认为 False。 |
print_progress |
是否打印进度,默认为 False。 |
pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30)) |
# 点云降采样
点云降采样是一种常用的点云处理技术,用于减少点云中的点数,从而降低计算复杂度。点云降采样可以通过多种方法实现,例如体素网格降采样、八叉树降采样等。
o3d.geometry.PointCloud.uniform_down_sample
函数可以降采样点云。
示例:
pcd = o3d.io.read_point_cloud("cat.pcd") | |
pcd = pcd.uniform_down_sample(every_k_points=8) # every_k_points: 每 K 个点保留一个 |
体素网格降采样
pcd = o3d.io.read_point_cloud("cat.pcd") | |
pcd = pcd.voxel_down_sample(voxel_size=5) # voxel_size: 体素大小 | |
o3d.visualization.draw_geometries([pcd]) |
原始点云、降采样后的点云、体素网格降采样后的点云可视化结果如下:
# 体素化
体素化是一种将三维空间划分为固定大小的立方体(体素)的技术,用于表示三维空间中的物体或场景。体素化可以用于点云处理、三维重建、计算机视觉等领域。
o3d.geometry.VoxelGrid
是 Open3D 中用于表示体素网格的类。
# mesh 体素化
o3d.geometry.VoxelGrid.create_from_triangle_mesh
函数可以将三角网格转换为体素网格。
import open3d as o3d | |
# 读取 mesh 文件 | |
mesh = o3d.io.read_triangle_mesh("cat.ply") | |
# 创建体素网格 (voxel_size: 体素大小) | |
voxel_grid = o3d.geometry.VoxelGrid.create_from_triangle_mesh(mesh, voxel_size=0.05) | |
# 可视化体素网格 | |
o3d.visualization.draw_geometries([voxel_grid]) |
# 点云体素化
o3d.geometry.VoxelGrid.create_from_point_cloud
函数可以将点云转换为体素网格。
import open3d as o3d | |
# 读取点云文件 | |
pcd = o3d.io.read_point_cloud("cat.pcd") | |
# 创建体素网格 (voxel_size: 体素大小) | |
voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd, voxel_size=0.2) | |
# 可视化体素网格 | |
o3d.visualization.draw_geometries([voxel_grid]) |
体素栅构建 Octree
使用 create_from_voxel_grid
方法从体素栅格创建八叉树。
import open3d as o3d | |
import numpy as np | |
pcd = o3d.io.read_point_cloud("cat.pcd") | |
# 创建一个体素栅格 | |
voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd, voxel_size=0.2) | |
# 创建八叉树 | |
octree = o3d.geometry.Octree(max_depth=5)# max_depth: 八叉树的最大深度 | |
octree.convert_from_voxel_grid(voxel_grid) | |
o3d.visualization.draw_geometries([octree]) |