关键代码:
plane_model, inliers = pcd.segment_plane(distance_threshold=0.01, ransac_n=3, num_iterations=1000)
point_cloud_plane_segmenation.py
import numpy as np
import open3d as o3d
def get_best_distance_threshold(point_cloud):
"""
Calculates the best distance threshold value for a given point cloud.
Args:
point_cloud (open3d.geometry.PointCloud): Point cloud to calculate threshold for.
Returns:
float: Best distance threshold value.
"""
distances = point_cloud.compute_nearest_neighbor_distance()
mean_dist = np.mean(distances)
std_dist = np.std(distances)
threshold = mean_dist + 0.5 * std_dist
return threshold
if __name__ == "__main__":
# 1. read
sample_pcd_data = o3d.data.PCDPointCloud()
pcd = o3d.io.read_point_cloud(sample_pcd_data.path)
# Flip it, otherwise the pointcloud will be upside down.
pcd.transform([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]])
# 2. 平面分割
d_threshold = get_best_distance_threshold(pcd)
"""
Segments a plane in the point cloud using the RANSAC algorithm.
distance_threshold (default 0.01). Max distance a point can be from the plane model, and still be considered an inlier.
ransac_n (default 3). Number of initial points to be considered inliers in each iteration.
num_iterations (default 100). Maximum number of iterations.
return: Tuple of the plane model ax + by + cz + d = 0
and the indices of the plane inliers on the same device as the point cloud.
"""
segment_models, inliers = pcd.segment_plane(distance_threshold=d_threshold*3, ransac_n=3, num_iterations=1000)
segments = pcd.select_by_index(inliers)
segments.paint_uniform_color([1, 0, 0])
o3d.visualization.draw([pcd, segments])
升级版
def refined_ransac(pcd):
segments = {} # 小平面点云集合
max_plane_idx = 20
rest = pcd
d_threshold = get_best_distance_threshold(pcd)
for i in range(max_plane_idx):
colors = plt.get_cmap("tab20")(i) # (0.12156862745098039, 0.4666666666666667, 0.7058823529411765, 1.0)
# ransac找最像平面的点云
_, inliers = rest.segment_plane(distance_threshold=d_threshold*3, ransac_n=3, num_iterations=1000)
segments[i] = rest.select_by_index(inliers)
# # 在此平面内聚类:距离太远的,将分为不同类别。剔除最大size的后作为保留点云。
# labels = np.array(segments[i].cluster_dbscan(eps=d_threshold * 10, min_points=10))
# candidates = [len(np.where(labels == j)[0]) for j in np.unique(labels)] # 不同聚类点云中点个数
# best_candidate = int(np.array(np.unique(labels)[np.where(candidates == np.max(candidates))[0]])[0]) # 点云size最大的聚类
# print("the best candidate is: ", best_candidate)
# rest = rest.select_by_index(inliers, invert=True) + segments[i].select_by_index(
# list(np.where(labels != best_candidate)[0]))
rest = rest.select_by_index(inliers, invert=True)
# 最大size聚类点云作为当前平面分割结果。
# segments[i] = segments[i].select_by_index(list(np.where(labels == best_candidate)[0]))
segments[i].paint_uniform_color(list(colors[:3]))
print("pass", i + 1, "/", max_plane_idx, "done.")
# 可视化分割点云,还有一些没有分割成功的点云rest
o3d.visualization.draw_geometries([segments[i] for i in range(max_plane_idx)] + [rest])
return rest, segments, max_plane_idx
# applying ransac on the point cloud and dbscan on each segment and colorizing them
rest, segments, max_plane_idx = refined_ransac(pcd)
同一平面,距离太远的聚类成不同类别。
纯分割点云
分割点云+未被分割点云。
def refined_ransac(pcd):
segments = {} # 小平面点云集合
max_plane_idx = 20
rest = pcd
d_threshold = get_best_distance_threshold(pcd)
for i in range(max_plane_idx):
colors = plt.get_cmap("tab20")(i) # (0.12156862745098039, 0.4666666666666667, 0.7058823529411765, 1.0)
# ransac找最像平面的点云
_, inliers = rest.segment_plane(distance_threshold=d_threshold*3, ransac_n=3, num_iterations=1000)
segments[i] = rest.select_by_index(inliers)
# 在此平面内聚类:距离太远的,将分为不同类别。剔除最大size的后作为保留点云。
labels = np.array(segments[i].cluster_dbscan(eps=d_threshold * 10, min_points=10))
candidates = [len(np.where(labels == j)[0]) for j in np.unique(labels)] # 不同聚类点云中点个数
best_candidate = int(np.array(np.unique(labels)[np.where(candidates == np.max(candidates))[0]])[0]) # 点云size最大的聚类
print("the best candidate is: ", best_candidate)
rest = rest.select_by_index(inliers, invert=True) + segments[i].select_by_index(
list(np.where(labels != best_candidate)[0]))
# rest = rest.select_by_index(inliers, invert=True)
# 最大size聚类点云作为当前平面分割结果。
segments[i] = segments[i].select_by_index(list(np.where(labels == best_candidate)[0]))
segments[i].paint_uniform_color(list(colors[:3]))
print("pass", i + 1, "/", max_plane_idx, "done.")
# 可视化分割点云,还有一些没有分割成功的点云rest
o3d.visualization.draw_geometries([segments[i] for i in range(max_plane_idx)] + [rest])
return rest, segments, max_plane_idx
# applying ransac on the point cloud and dbscan on each segment and colorizing them
rest, segments, max_plane_idx = refined_ransac(pcd)