点云数据
本教程阐述了基本的点云用法。
随需要的文件链接
1. 显示点云
import open3d as o3d
import numpy as np
print("Load a ply point cloud, print it, and render it")
pcd = o3d.io.read_point_cloud("fragment.ply")
print(pcd)
print(np.asarray(pcd.points))
o3d.visualization.draw_geometries([pcd])
read_point_cloud:
该方法用于读取点云,它会根据扩展名对文件进行解码。所支持的文件类型
draw_geometries:
查看点云,可以通过移动鼠标来从不同的角度点云。
它看起来像一个密集的曲面,但实际上是一个渲染为曲面的点云。GUI支持各种键盘功能。例如,-键减小点(曲面)的大小。
注:按H键为GUI打印出键盘指令的完整列表。有关可视化GUI的更多信息,请参阅可视化和自定义可视化。
2. 体素下采样
体素下采样使用常规体素栅格从输入点云创建均匀下采样点云。它通常用作许多点云处理任务的预处理步骤。该算法分两步操作:
- 将点固定为体素。
- 每个被占用的体素通过平均内部的所有点来生成正好一个点。
print("Downsample the point cloud with a voxel of 0.05")
downpcd = pcd.voxel_down_sample(voxel_size=0.05)
o3d.visualization.draw_geometries([downpcd])
对体素为0.05的点云进行下采样。
3. 顶点正态估计
点云的另一个基本操作是点正态估计。按N键查看点法线。键-和+可用于控制法线的长度。
print("Recompute the normal of the downsampled point cloud")
downpcd.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))
o3d.visualization.draw_geometries([downpcd],
point_show_normal=True)
estimate_normals:
计算每个点的法线。该函数查找相邻点并使用协方差分析计算相邻点的主轴。该方法的两个关键参数radius=0.1和max_nn=30指定搜索半径和最大最近邻。它的搜索半径为10cm,最多只考虑30个邻居,节省了计算时间。
4. 访问估计顶点法线
可以从downpcd
的normals
变量中检索估计的法向量。
print("Print a normal vector of the 0th point")
print(downpcd.normals[0])
可以通过np.asaray
将法向量转换为numpy数组。
print("Print the normal vectors of the first 10 points")
print(np.asarray(downpcd.normals)[:10, :])
5. 裁剪点云
加载多边形体积并使用它裁剪原始点云
print("Load a polygon volume and use it to crop the original point cloud")
vol = o3d.visualization.read_selection_polygon_volume(
"cropped.json")
chair = vol.crop_point_cloud(pcd)
o3d.visualization.draw_geometries([chair])
read_selection_polygon_volume:
读取指定多边形选择区域的json文件。
vol.crop_point_cloud(pcd):
过滤出点。只剩下椅子了。
6. 绘制点云
print("Paint chair")
chair.paint_uniform_color([1, 0.706, 0])
o3d.visualization.draw_geometries([chair])
paint_uniform_color:
将所有点绘制为统一的颜色。颜色在RGB空间,[0,1]范围内。
7. 点云距离
Open3D提供了方法compute_point_cloud_distance
来计算源文件和目标文件间的距离,它为源点云中的每个点计算到目标点云中最近点的距离。在下面的示例中,我们使用函数来计算两点云之间的差异。请注意,此方法也可用于计算两点云之间的倒角距离。
import open3d as o3d
import numpy as np
# Load data
pcd = o3d.io.read_point_cloud("test_data/fragment.ply")
vol = o3d.visualization.read_selection_polygon_volume(
"test_data/Crop/cropped.json")
chair = vol.crop_point_cloud(pcd)
dists = pcd.compute_point_cloud_distance(chair)
dists = np.asarray(dists)
ind = np.where(dists > 0.01)[0]
pcd_without_chair = pcd.select_by_index(ind)
o3d.visualization.draw_geometries([pcd_without_chair])
8. 边界体积
点云几何体类型与Open3D中的所有其他几何体类型一样具有边界体积。当前,Open3D实现了一个AxisAlignedBoundingBox
和一个OrientedBoundingBox
,也可用于裁剪几何体。
aabb = chair.get_axis_aligned_bounding_box()
aabb.color = (1, 0, 0)
obb = chair.get_oriented_bounding_box()
obb.color = (0, 1, 0)
o3d.visualization.draw_geometries([chair, aabb, obb])
9. 密度的聚类算法簇
给定一个来自深度传感器的点云,我们希望将本地点云簇分组在一起。为此,我们可以使用聚类算法。Open3D实现了DBSCAN,这是一种基于密度的聚类算法。该算法在cluster_dbscan
中实现,需要两个参数:eps
定义到簇内邻居的距离,min_points
定义形成簇所需的最小点数。函数返回labels,其中labelmin_points
表示noise。
import open3d as o3d
import numpy as np
import matplotlib.pyplot as plt
pcd = o3d.io.read_point_cloud("test_data/fragment.ply")
with o3d.utility.VerbosityContextManager(
o3d.utility.VerbosityLevel.Debug) as cm:
labels = np.array(
pcd.cluster_dbscan(eps=0.02, min_points=10, print_progress=True))
max_label = labels.max()
print(f"point cloud has {max_label + 1} clusters")
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])
10. 平面分割
Open3D还支持使用RANSAC从点云分割几何原语。要在点云中找到支撑度最大的平面,可以使用segment_plane
.该。法有三个参数:distance_threshold
定义了一个点到被认为是内联的估计平面的最大距离,ransac_n
定义了随机采样以估计平面的点的数量,num_iterations
定义了随机平面的采样和验证频率。然后函数将平面返回为(a,b,c,d),这样平面上的每个点(x,y,z)的ax+by+cz+d=0。函数还返回内部点的索引列表。
import open3d as o3d
import numpy as np
import matplotlib.pyplot as plt
pcd = o3d.io.read_point_cloud("test_data/fragment.pcd")
plane_model, inliers = pcd.segment_plane(distance_threshold=0.01,
ransac_n=3,
num_iterations=1000)
[a, b, c, d] = plane_model
print(f"Plane equation: {a:.2f}x + {b:.2f}y + {c:.2f}z + {d:.2f} = 0")
inlier_cloud = pcd.select_by_index(inliers)
inlier_cloud.paint_uniform_color([1.0, 0, 0])
outlier_cloud = pcd.select_by_index(inliers, invert=True)
o3d.visualization.draw_geometries([inlier_cloud, outlier_cloud])