Open3D 点云处理

FreeGuideOnline 15阅读 2026-07-13

bash pip install open3d

验证安装:
```python
import open3d as o3d
print(o3d.__version__)

如需使用最新的可视化功能,请确保显卡驱动和 OpenGL 环境正常。Jupyter Notebook 中可视化需要额外运行 o3d.visualization.draw_plotly 或使用 Web 可视化模块。

加载与保存点云

Open3D 支持常见的点云格式(PLY, PCD, XYZ, XYZRGB, PTS 等)。

读取点云

import open3d as o3d

pcd = o3d.io.read_point_cloud("example.ply")
print(pcd)               # 查看基本信息
print(np.asarray(pcd.points)[:5])  # 打印前5个点的坐标

创建自定义点云

import numpy as np

points = np.array([[0,0,0], [1,0,0], [0,1,0], [1,1,0.5]])
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points)
# 可添加颜色
pcd.colors = o3d.utility.Vector3dVector(np.random.rand(4,3))

保存点云

o3d.io.write_point_cloud("output.ply", pcd)

点云可视化

基本显示

o3d.visualization.draw_geometries([pcd])

弹出窗口提供旋转、平移、缩放功能,按 h 可以看到全部快捷键。

设置点大小与背景色

vis = o3d.visualization.Visualizer()
vis.create_window(visible=True)
vis.add_geometry(pcd)
opt = vis.get_render_option()
opt.point_size = 2.0
opt.background_color = np.array([0.1, 0.1, 0.1])
vis.run()
vis.destroy_window()

批量可视化

o3d.visualization.draw_geometries([pcd1, pcd2, pcd3])

不同点云可分别设置颜色以区分。

点云预处理:滤波与下采样

实际扫描的点云常包含噪声和冗余点,预处理至关重要。

体素下采样

在保持点云整体形状的同时均匀减少点数。

voxel_size = 0.05
downsampled = pcd.voxel_down_sample(voxel_size)

统计离群点去除

移除距离邻居过远的点(适合去除稀疏噪声)。

cl, ind = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
inlier_cloud = pcd.select_by_index(ind)
outlier_cloud = pcd.select_by_index(ind, invert=True)

半径离群点去除

删除在指定半径内邻居数量过少的点。

cl, ind = pcd.remove_radius_outlier(nb_points=16, radius=0.1)
filtered = pcd.select_by_index(ind)

均匀下采样

与体素滤波不同,均匀下采样基于点之间的最小距离进行筛选。

uniform_down = pcd.uniform_down_sample(every_k_points=5)

点云法线估计

法线是许多高级算法(配准、重建)的基础。

pcd.estimate_normals(
    search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30)
)

法线方向可以通过 pcd.normals 获取。可调用 orient_normals_towards_camera_locationorient_normals_consistent_tangent_plane 进行一致化。

可视化法线:

o3d.visualization.draw_geometries([pcd], point_show_normal=True)

特征点提取与描述

ISS 关键点检测

识别具有显著几何变化的点。

keypoints = o3d.geometry.keypoint.compute_iss_keypoints(
    pcd, salient_radius=0.1, non_max_radius=0.1, gamma_21=0.975, gamma_32=0.975
)

FPFH 特征描述子

用于点云配准的特征描述。

# 需先估计法线
pcd_fpfh = o3d.pipelines.registration.compute_fpfh_feature(
    pcd,
    o3d.geometry.KDTreeSearchParamHybrid(radius=0.25, max_nn=100)
)

点云配准(ICP)

将不同视角获取的点云对齐到统一坐标系。

全局配准(RANSAC)

基于 FPFH 特征进行快速初始对齐。

# 源点云和目标点云均需计算 FPFH
result = o3d.pipelines.registration.registration_ransac_based_on_feature_matching(
    source, target, source_fpfh, target_fpfh, mutual_filter=False,
    max_correspondence_distance=0.02,
    estimation_method=o3d.pipelines.registration.TransformationEstimationPointToPoint(),
    ransac_n=4,
    checkers=[o3d.pipelines.registration.CorrespondenceCheckerBasedOnEdgeLength(0.9),
              o3d.pipelines.registration.CorrespondenceCheckerBasedOnDistance(0.02)],
    criteria=o3d.pipelines.registration.RANSACConvergenceCriteria(4000000, 1000)
)

精细配准(ICP)

threshold = 0.02
reg_p2p = o3d.pipelines.registration.registration_icp(
    source, target, threshold, result.transformation,
    o3d.pipelines.registration.TransformationEstimationPointToPoint(),
    o3d.pipelines.registration.ICPConvergenceCriteria(max_iteration=2000)
)
print(reg_p2p.transformation)
source.transform(reg_p2p.transformation)

可选择 PointToPlane 提高精度(需要目标点云的法线)。

点云分割与聚类

平面分割(RANSAC)

plane_model, inliers = pcd.segment_plane(distance_threshold=0.01, ransac_n=3, num_iterations=1000)
[a, b, c, d] = plane_model
inlier_cloud = pcd.select_by_index(inliers)
outlier_cloud = pcd.select_by_index(inliers, invert=True)

DBSCAN 聚类

基于欧氏距离将点云分割为不同物体。

labels = pcd.cluster_dbscan(eps=0.02, min_points=10, print_progress=True)
max_label = labels.max()
colors = plt.get_cmap("tab20")(labels / (max_label if max_label>0 else 1))
colors[labels < 0] = 0
pcd.colors = o3d.utility.Vector3dVector(colors[:, :3])
o3d.visualization.draw_geometries([pcd])

实战:从点云到网格重建

泊松重建

需要点云具有法线。

pcd.estimate_normals()
# 方向可能需要重新定向
pcd.orient_normals_consistent_tangent_plane(100)
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(pcd, depth=9)
# 可选:根据密度移除低置信度顶点
vertices_to_remove = densities < np.quantile(densities, 0.01)
mesh.remove_vertices_by_mask(vertices_to_remove)
o3d.visualization.draw_geometries([mesh])

Alpha Shape

alpha = 0.03
mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_alpha_shape(pcd, alpha)
mesh.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh])