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_location 或 orient_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])