Open3D 是一个强大的开源库,专门用于处理 3D 数据(如点云、网格等)。以下是 Open3D 中常见的点云操作总结:
.ply
, .pcd
, .xyz
, .obj
等)。pcd = o3d.io.read_point_cloud("file.ply")
o3d.io.write_point_cloud("output.ply", pcd)
o3d.visualization.draw_geometries([pcd])
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)
trans_init = np.identity(4) # 初始变换矩阵
reg_result = o3d.pipelines.registration.icp(
source, target, max_distance, trans_init,
o3d.pipelines.registration.TransformationEstimationPointToPoint())
plane_model, inliers = pcd.segment_plane(
distance_threshold=0.01, ransac_n=3, num_iterations=1000)
labels = np.array(pcd.cluster_dbscan(eps=0.1, min_points=10))
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))
pcd = o3d.geometry.PointCloud.create_from_depth_image(
depth, intrinsic, extrinsic=(np.identity(4)))
pcd = o3d.geometry.PointCloud.create_from_rgbd_image(rgbd_image, intrinsic)
fpfh = o3d.pipelines.registration.compute_fpfh_feature(
pcd, o3d.geometry.KDTreeSearchParamHybrid(radius=0.25, max_nn=100))
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(pcd, depth=9)
hull, _ = pcd.compute_convex_hull()
combined_pcd = pcd1 + pcd2
dist = pcd1.compute_point_cloud_distance(pcd2)
kdtree = o3d.geometry.KDTreeFlann(pcd)
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 计算点云的主轴方向:
# 将点云转换为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])
PointCloud
对象,与 NumPy 数组需通过 np.asarray(pcd.points)
转换。Open3D 的 API 简洁且功能丰富,适合快速开发 3D 点云处理流程。详细文档参考:Open3D官方文档。