基于open3d的一些常见点云操作
Open3D 是一个强大的开源库,专门用于处理 3D 数据(如点云、网格等)。以下是 Open3D 中常见的点云操作总结:
1. 点云读取与写入
- 读取点云:支持常见格式(
.ply
,.pcd
,.xyz
,.obj
等)。pcd = o3d.io.read_point_cloud("file.ply")
- 写入点云:
o3d.io.write_point_cloud("output.ply", pcd)
2. 点云可视化
- 快速可视化:
o3d.visualization.draw_geometries([pcd])
- 自定义视图:支持调整背景色、点大小、视角等。
3. 点云预处理
- 下采样(降采样):
down_pcd = pcd.voxel_down_sample(voxel_size=0.05)
- 去除离群点:
- 统计离群点移除:
cl, ind = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
- 半径离群点移除:
cl, ind = pcd.remove_radius_outlier(nb_points=16, radius=0.05)
- 统计离群点移除:
- 滤波(平滑):
- 高斯滤波或均值滤波需自定义实现(Open3D 未直接提供)。
4. 点云配准(Registration)
- ICP(Iterative Closest Point):
trans_init = np.identity(4) # 初始变换矩阵 reg_result = o3d.pipelines.registration.icp( source, target, max_distance, trans_init, o3d.pipelines.registration.TransformationEstimationPointToPoint())
- 全局配准:基于特征描述子(如 FPFH)的 RANSAC 初始配准。
5. 点云分割与聚类
- 平面分割(RANSAC):
plane_model, inliers = pcd.segment_plane( distance_threshold=0.01, ransac_n=3, num_iterations=1000)
- DBSCAN 聚类:
labels = np.array(pcd.cluster_dbscan(eps=0.1, min_points=10))
6. 点云变换
- 旋转/平移/缩放:
pcd.rotate(rotation_matrix, center=(0, 0, 0)) pcd.translate((tx, ty, tz)) pcd.scale(scale_factor, center=(0, 0, 0))
- 法线估计:
pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))
7. 点云生成
- 从深度图生成:
pcd = o3d.geometry.PointCloud.create_from_depth_image( depth, intrinsic, extrinsic=(np.identity(4)))
- 从 RGB-D 数据生成:
pcd = o3d.geometry.PointCloud.create_from_rgbd_image(rgbd_image, intrinsic)
8. 点云特征提取
- FPFH 特征(用于配准或识别):
fpfh = o3d.pipelines.registration.compute_fpfh_feature( pcd, o3d.geometry.KDTreeSearchParamHybrid(radius=0.25, max_nn=100))
9. 点云与网格的转换
- 泊松重建(生成网格):
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(pcd, depth=9)
- 凸包重建:
hull, _ = pcd.compute_convex_hull()
10. 其他实用操作
- 点云拼接:
combined_pcd = pcd1 + pcd2
- 计算点云距离:
dist = pcd1.compute_point_cloud_distance(pcd2)
- KDTree 加速查询:
kdtree = o3d.geometry.KDTreeFlann(pcd)
11.具体操作实例
- 求解外包络
在 Open3D 中,计算点云的最小外包络(Bounding Volume)常用的三种方法包括 AABB(轴对齐包围盒)、OBB(有向包围盒) 和 最小外接球。以下是具体实现方法及代码示例:
1. AABB (Axis-Aligned Bounding Box)
特点:与坐标轴对齐的包围盒,计算速度快,但紧密性较差。
计算方法:直接取点云在 XYZ 轴上的最小/最大值。
代码:
import open3d as o3d
import numpy as np
# 读取点云
pcd = o3d.io.read_point_cloud("point_cloud.ply")
# 计算AABB
aabb = pcd.get_axis_aligned_bounding_box()
aabb.color = (1, 0, 0) # 设置为红色
# 可视化
o3d.visualization.draw_geometries([pcd, aabb])
2. OBB (Oriented Bounding Box)
特点:考虑点云的主方向,包围更紧密,但计算复杂度较高。
计算方法:基于 PCA(主成分分析)确定最优旋转方向。
代码:
# 计算OBB
obb = pcd.get_oriented_bounding_box()
obb.color = (0, 1, 0) # 设置为绿色
# 可视化
o3d.visualization.draw_geometries([pcd, obb])
手动实现PCA计算OBB(可选):
若需自定义,可通过 PCA 计算点云的主轴方向:
# 将点云转换为NumPy数组
points = np.asarray(pcd.points)
# 计算PCA
mean = np.mean(points, axis=0)
cov = np.cov((points - mean).T)
eigenvals, eigenvecs = np.linalg.eig(cov)
# 根据特征向量构造OBB的旋转矩阵
obb = pcd.get_oriented_bounding_box(rot=eigenvecs) # 需进一步调整
有时候手动计算的主方向并不是我们想要的长宽方向,这时候可以考虑将其投影到2d方向然后再来计算pca主方向,完整操作实例如下,
import open3d as o3d
import numpy as np
def align_pcd_by_projected_pca(pcd):
# Step 1: 转为 numpy
pts = np.asarray(pcd.points)
# Step 2: 计算法线方向(最小特征值方向)
center = np.mean(pts, axis=0)
pts_centered = pts - center
cov = np.cov(pts_centered.T)
eig_vals, eig_vecs = np.linalg.eigh(cov)
# 法线方向是最小特征值方向
normal = eig_vecs[:, 0] # 最小特征值方向
# Step 3: 投影点云到主平面(去掉法线方向)
normal = normal / np.linalg.norm(normal)
projection = pts - np.dot(pts - center, normal[:, None]) * normal
# Step 4: 对投影后的点做 PCA,获得长宽方向
proj_centered = projection - np.mean(projection, axis=0)
cov_proj = np.cov(proj_centered.T)
eig_vals_proj, eig_vecs_proj = np.linalg.eigh(cov_proj)
# 主方向:最大特征值方向
main_dir = eig_vecs_proj[:, 2] # 对应最大特征值
# 构造新坐标轴(main_dir, cross, normal)
x_axis = main_dir / np.linalg.norm(main_dir)
z_axis = normal
y_axis = np.cross(z_axis, x_axis)
y_axis = y_axis / np.linalg.norm(y_axis)
x_axis = np.cross(y_axis, z_axis) # 保正交
# 构造旋转矩阵 R(世界 -> 对齐空间)
R = np.stack([x_axis, y_axis, z_axis], axis=1)
# Step 5: 旋转点云
pcd_aligned = pcd.translate(-center)
pcd_aligned.rotate(R.T, center=(0, 0, 0))
# Step 6: 在旋转后的空间计算 OBB
obb = pcd_aligned.get_oriented_bounding_box()
# Step 7: 逆变换 OBB
obb.rotate(R, center=(0, 0, 0))
obb.translate(center)
return obb, R, center
# ==== 可视化 ====
if __name__ == "__main__":
# 读取点云
pcd = o3d.io.read_point_cloud("your_point_cloud.ply")
# 计算对齐后的 obb
obb, _, _ = align_pcd_by_projected_pca(pcd)
# 可视化结果
obb.color = [1, 0, 0]
o3d.visualization.draw_geometries([pcd, obb])
3. 最小外接球 (Minimum Bounding Sphere)
特点:球形的包围体积,适用于各向同性分布的点云。
计算方法:Open3D 未直接提供最小外接球 API,可借助 scipy.spatial
计算。
代码:
from scipy.spatial import Miniball
import numpy as np
# 将点云转换为NumPy数组
points = np.asarray(pcd.points)
# 计算最小外接球
mb = Miniball(points)
center = mb.center()
radius = np.sqrt(mb.squared_radius())
# 在Open3D中创建球体网格
sphere = o3d.geometry.TriangleMesh.create_sphere(radius=radius)
sphere.translate(center)
sphere.paint_uniform_color((0, 0, 1)) # 设置为蓝色
# 可视化
o3d.visualization.draw_geometries([pcd, sphere])
对比总结
方法 | 特点 | 计算复杂度 | Open3D支持 |
---|---|---|---|
AABB | 轴对齐,计算快,包围松散 | O(n) | 直接支持 (get_axis_aligned_bounding_box ) |
OBB | 方向自适应,包围紧密 | O(n^2) | 直接支持 (get_oriented_bounding_box ) |
最小外接球 | 球形包围,适合均匀分布 | O(n^4) | 需借助 scipy.spatial.Miniball |
可视化示例
# 同时显示三种包围盒
aabb.color = (1, 0, 0) # AABB红色
obb.color = (0, 1, 0) # OBB绿色
sphere = o3d.geometry.TriangleMesh.create_sphere(radius=radius).translate(center)
sphere.paint_uniform_color((0, 0, 1)) # 球蓝色
o3d.visualization.draw_geometries([pcd, aabb, obb, sphere])
注意事项
- 数据类型:Open3D 点云数据为
PointCloud
对象,与 NumPy 数组需通过np.asarray(pcd.points)
转换。 - 性能:大规模点云需结合降采样和并行计算优化,OBB 和最小外接球计算较慢,建议对下采样后的点云使用。
Open3D 的 API 简洁且功能丰富,适合快速开发 3D 点云处理流程。详细文档参考:Open3D官方文档。