You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Python的RANSAC单平面检测:建筑屋顶分割问题求助

建筑屋顶点云分割需求

使用RANSAC检测建筑屋顶时,多个屋顶被识别成了单个平面,我需要实现每个建筑屋顶的单独分割。

数据说明

  • 原始点云数据:原始点云
  • 当前RANSAC输出结果:当前RANSAC结果
  • 预期分割效果:预期分割效果

已尝试代码

import os
import open3d as o3d
import numpy as np

test_data_dir = 'M:\\lidar\\Test\\'
point_cloud_file_name = 'boutlier.txt'
point_cloud_file_path = os.path.join(test_data_dir, point_cloud_file_name)

pcd = o3d.io.read_point_cloud(point_cloud_file_path,format="xyz")

plane_model, inliers = pcd.segment_plane(distance_threshold=2.5,
                                         ransac_n=3,
                                         num_iterations=1000)

inlier_cloud = pcd.select_by_index(inliers)
inlier_cloud.paint_uniform_color([0.5, 0, 0.5])

outlier_cloud = pcd.select_by_index(inliers, invert=True)

o3d.visualization.draw_geometries([inlier_cloud])

解决方案

要实现单个屋顶的分割,可在RANSAC基础上结合区域生长聚类或迭代式RANSAC+聚类的方法,以下是具体实现方案:

方法1:迭代RANSAC提取平面后聚类

先反复用RANSAC提取平面,直到剩余点云数量低于阈值,再对每个平面内的点云做聚类分离不同屋顶:

import os
import open3d as o3d
import numpy as np

test_data_dir = 'M:\\lidar\\Test\\'
point_cloud_file_name = 'boutlier.txt'
point_cloud_file_path = os.path.join(test_data_dir, point_cloud_file_name)

pcd = o3d.io.read_point_cloud(point_cloud_file_path, format="xyz")
remaining_cloud = pcd
planes = []
# 迭代终止条件:剩余点云少于总点数的5%
while len(remaining_cloud.points) > 0.05 * len(pcd.points):
    plane_model, inliers = remaining_cloud.segment_plane(
        distance_threshold=0.8,  # 缩小距离阈值,降低误判
        ransac_n=3,
        num_iterations=10000
    )
    inlier_cloud = remaining_cloud.select_by_index(inliers)
    planes.append(inlier_cloud)
    remaining_cloud = remaining_cloud.select_by_index(inliers, invert=True)

# 对每个平面点云做DBSCAN聚类,分离独立屋顶
colored_clouds = []
colors = [[1,0,0], [0,1,0], [0,0,1], [1,1,0], [1,0,1], [0,1,1]]
color_idx = 0
for plane in planes:
    plane.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=1.0, max_nn=30))
    # 调整eps和min_points适配点云密度
    labels = np.array(plane.cluster_dbscan(eps=1.5, min_points=50, print_progress=False))
    max_label = labels.max()
    for label in range(max_label + 1):
        cluster = plane.select_by_index(np.where(labels == label)[0])
        cluster.paint_uniform_color(colors[color_idx % len(colors)])
        colored_clouds.append(cluster)
        color_idx += 1

# 添加剩余非平面点云
remaining_cloud.paint_uniform_color([0.5,0.5,0.5])
colored_clouds.append(remaining_cloud)

o3d.visualization.draw_geometries(colored_clouds)

方法2:先聚类再对每个簇做平面检测

先通过DBSCAN把点云分成不同区域簇,再对每个簇单独做RANSAC平面检测,过滤非平面簇:

import os
import open3d as o3d
import numpy as np

test_data_dir = 'M:\\lidar\\Test\\'
point_cloud_file_name = 'boutlier.txt'
point_cloud_file_path = os.path.join(test_data_dir, point_cloud_file_name)

pcd = o3d.io.read_point_cloud(point_cloud_file_path, format="xyz")
# 全局聚类分离不同建筑区域
pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=1.0, max_nn=30))
labels = np.array(pcd.cluster_dbscan(eps=2.0, min_points=100, print_progress=False))
max_label = labels.max()

colored_clouds = []
colors = [[1,0,0], [0,1,0], [0,0,1], [1,1,0], [1,0,1], [0,1,1]]
color_idx = 0
for label in range(max_label + 1):
    cluster = pcd.select_by_index(np.where(labels == label)[0])
    # 对簇做平面检测,判断是否为屋顶
    plane_model, inliers = cluster.segment_plane(
        distance_threshold=0.5,
        ransac_n=3,
        num_iterations=5000
    )
    # 保留平面点占比超70%的簇(判定为屋顶)
    if len(inliers) > 0.7 * len(cluster.points):
        cluster.paint_uniform_color(colors[color_idx % len(colors)])
        colored_clouds.append(cluster)
        color_idx += 1

o3d.visualization.draw_geometries(colored_clouds)

参数调整建议

  • distance_threshold:根据点云精度调整,值越小判定越严格,避免相邻屋顶被归为同一平面
  • eps(DBSCAN参数):控制聚类邻域半径,点云密度大则调小,密度小则调大
  • min_points(DBSCAN参数):过滤噪声小簇,根据屋顶最小点数设置

内容的提问来源于stack exchange,提问作者Purple_Ad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 09:43:32