open3d 平面分割

关键代码:

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])

open3d 平面分割_第1张图片

升级版

open3d 平面分割_第2张图片

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)

同一平面,距离太远的聚类成不同类别。

纯分割点云

open3d 平面分割_第3张图片

 分割点云+未被分割点云。

open3d 平面分割_第4张图片

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)

你可能感兴趣的:(open3d,python)