# 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="直接构造的网格")

create mesh

# 文件内容

# PCD

PCD 文件是一种常用的点云文件格式,由 PCL(Point Cloud Library)定义。PCD 文件可以包含点云的坐标、颜色、法线等信息。

PCD 文件头的具体内容如下:
VERSION
指定 PCD 文件的版本,目前支持 PCD_V0.7 和 PCD_V0.7_BINARY 两种版本。
FIELDS
指定点云数据中包含的字段,每个字段用空格分隔。
示例:

字段内容 含义
FIELDS x y z xyz 三个坐标字段
FIELDS x y z rgb xyz 三个坐标字段 + rgb 颜色字段(深度相机)
FIELDS x y z normal_x normal_y normal_z xyz 三个坐标字段 + normal_xnormal_ynormal_z 三个法线字段
FIELDS j1 j2 j3 j1j2j3 三个字段(例如激光雷达)

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 起,支持三种数据类型: asciibinarybinary_compressed

  • ascii 每一行代表一个点的属性,由于是 ascii 码因此可以直接阅读,缺点就是占用空间大,空的点用 NaN 表示
  • binary 其中数据是 pcl::PointCloud.points 数组 / 向量的完整内存副本。在 Linux 系统上,我们使用 mmap/munmap 操作对数据进行最快的读 / 写访问。
  • binary_compressed 该文件(头部之后的所有内容)以一个 32 位无符号二进制数开始,该二进制数指定以压缩形式存在的数据的字节大小。接下来是另一个 32 位无符号二进制数,该二进制数指定未压缩数据的字节大小。然后是压缩后的数据。压缩和解压缩都使用 Marc LehmannLZF 算法。该算法在压缩率方面表现一般,但是速度非常快。对于典型的点云,压缩后的数据大小为原始大小的 30% 到 60%。在压缩之前,数据会被重新排序以改善压缩效果,从标准的结构数组布局变为数组结构布局。例如,一个包含三个点和 xyz 字段的云将从 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.0format binary_little_endian 1.0format binary_big_endian 1.0
  • 元素类型: element vertex 1000element face 1000
  • 属性: property float xproperty float yproperty float zproperty uchar redproperty uchar greenproperty uchar blue
  • 结束: end_header
字段 说明
ply 表示文件格式为 PLY。
format 指定文件格式,可以是 asciibinary_little_endianbinary_big_endian
comment 表示注释,可以包含任何文本。
element 指定元素类型,可以是 vertexface
property 指定属性,可以是 floatintuchar 等。
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 ,表示自动检测文件格式,支持 pcdplyxyz 等格式。
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树八叉树 具体适用场景分析

  1. K-D 树的优势场景
  • 高维数据处理
  • 高效的最近邻搜索
  • 范围查询
  • 低内存占用
  1. 八叉树的优势场景
  • 纯 3D 空间的均匀划分
  • 体素化与多分辨率处理
  • 碰撞检测与空间占用查询
  • 点云压缩与降采样
  1. 实际应用中的选择策略
  • 优先使用 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])

原始点云、降采样后的点云、体素网格降采样后的点云可视化结果如下:
cat_points cat_down cat_voxel

# 体素化

体素化是一种将三维空间划分为固定大小的立方体(体素)的技术,用于表示三维空间中的物体或场景。体素化可以用于点云处理、三维重建、计算机视觉等领域。

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

点云体素化 体素栅构建Octree