[点云分割] 平面分割

一、介绍

SACSegmentation(Sample Consensus Segmentation)是PCL中的一个分割算法,用于从点云中识别出具有相同几何形状的模型。该算法使用采样一致性(Sample Consensus)方法,通过迭代地随机采样一组数据点,并拟合模型,然后评估拟合模型与数据点的一致性,最终找到最佳的模型参数。

SACSegmentation算法的基本原理如下:

  1. 随机采样:从输入的点云中随机选择一组数据点作为采样集。

  2. 模型拟合:根据采样集,使用最小二乘或其他方法拟合一个几何模型(如平面、球体、圆柱体等)。

  3. 评估一致性:计算所有点与拟合模型之间的距离,并将距离小于阈值的点划分为内点(inliers),距离大于阈值的点划分为外点(outliers)。

  4. 重复迭代:重复执行步骤1至3,直到达到预设的迭代次数或满足停止条件。

  5. 选择最佳模型:根据内点的数量或其他评估指标选择最佳的模型作为分割结果。

SACSegmentation算法具有很好的鲁棒性和通用性,可以用于分割各种几何形状的模型。通过调整阈值和迭代次数等参数,可以控制算法的精度和速度。

在PCL中,通过设置SACSegmentation对象的参数,如模型类型、方法类型、距离阈值等,以及输入点云数据,调用segment()方法即可执行分割算法,并获得分割结果,包括模型系数和内点索引等。

总而言之,SACSegmentation算法是一种常用的基于采样一致性的点云分割算法,可用于识别点云中的几何模型,为后续的点云处理和分析提供了基础。

二、代码

#include <iostream>
#include <chrono>

#include <pcl/ModelCoefficients.h> // 模型系数的定义
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h> // 各种点云数据类型
#include <pcl/sample_consensus/method_types.h> // 包含用于采样一致性算法的不同方法的定义,如RANSAC、MSAC等
#include <pcl/sample_consensus/model_types.h> // 包含用于采样一致性算法的不同模型的定义,如平面、球体、圆柱体
#include <pcl/segmentation/sac_segmentation.h> // 包含用于分割点云的采样一致性算法(SACSegmentation)的定义,用于识别点云的几何模型
#include <pcl/filters/extract_indices.h> // 包含用于从点云中提取特定索引的函数和类,用于根据索引提取点云中的子集
#include <pcl/visualization/pcl_visualizer.h> // 包含了用于可视化点云的函数和类,用于在3D视窗中现实点云数据

int main(){
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

    // Fill the cloud data
    cloud->width = 100000;
    cloud->height = 1;
    cloud->points.resize(cloud->width * cloud->height);

    // generate the data
    for(auto& point: *cloud)
    {
        point.x = 1024 * rand() / (RAND_MAX + 1.0f);
        point.y = 1024 * rand() / (RAND_MAX + 1.0f);
        point.z = 1.0;
    }

    // Set a few outlierss
    for(int i=0; i< 1000; i++)
    {
        (*cloud)[i].z = 50 * rand() / (RAND_MAX + 1.0f);
    }

    std::cerr << "Point cloud data:" << cloud->size() << " points" << std::endl;
    for (const auto& point: *cloud)
        std::cerr << "   " << point.x << " "
                  << point.y << " "
                  << point.z << std::endl;

    pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
    pcl::PointIndices::Ptr inliers(new pcl::PointIndices);

    // Create the segmentation object
    pcl::SACSegmentation<pcl::PointXYZ> seg;

    auto start = std::chrono::high_resolution_clock::now();
    // Optional
    seg.setOptimizeCoefficients(true);

    // Mandatory
    seg.setModelType(pcl::SACMODEL_PLANE);
    seg.setMethodType(pcl::SAC_RANSAC);
    seg.setDistanceThreshold(0.01);

    seg.setInputCloud(cloud);
    seg.segment(*inliers, *coefficients);

    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double, std::milli> duration = end - start;
    std::printf("segment cost time:%.2f ms\n", duration.count());

    if(inliers->indices.size() ==0)
    {
        PCL_ERROR("Cloud not estimate a planar model for the given dataset.\n");
        return(-1);
    }

//    std::cerr << "Model coefficients: " << coefficients->values[0] << " "
//                                        << coefficients->values[1] << " "
//                                        << coefficients->values[2] << " "
//                                        << coefficients->values[3] << std::endl;

//    std::cerr << "Model inliers: " << inliers->indices.size() << std::endl;
//    for (const auto& idx: inliers->indices)
//        std::cerr << idx << "   " << cloud->points[idx].x << " "
//                                  << cloud->points[idx].y << " "
//                                  << cloud->points[idx].z << std::endl;


    // extract inliers for visualization
    pcl::PointCloud<pcl::PointXYZ>::Ptr inlierCloud(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::ExtractIndices<pcl::PointXYZ> extract;

    extract.setInputCloud(cloud);
    extract.setIndices(inliers);
    extract.setNegative(false); // 提取inliers
    extract.filter(*inlierCloud);


    // visualization
    pcl::visualization::PCLVisualizer vis("cloud visualization");

    int v0(0);
    vis.createViewPort(0.0, 0.0, 0.5, 1.0, v0);
    vis.addCoordinateSystem(0.5,"reference0", v0);
    vis.setBackgroundColor(0.05, 0.05, 0.05, v0);

    vis.addPointCloud(cloud, "cloud0", v0);
    vis.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 4.0, "cloud0", v0);


    int v1(1);
    vis.createViewPort(0.5, 0, 1, 1, v1);
    vis.addCoordinateSystem(0.5, "reference1", v1);
    vis.setBackgroundColor(0,0,0,v1);

    vis.addPointCloud(inlierCloud, "cloud1", v1);
    vis.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 4.0, "cloud1", v1);


    while(!vis.wasStopped())
    {
        vis.spinOnce(100);
    }

    return(0);

}

### 使用Open3D进行点云数据的平面分割平面间距离测量 #### 平面分割方法概述 为了实现点云数据的平面分割,通常采用RANSAC (Random Sample Consensus) 算法来拟合平面模型。该算法能够有效地从嘈杂的数据集中找到最佳拟合参数[^2]。 ```python import open3d as o3d import numpy as np # 加载点云文件 pcd = o3d.io.read_point_cloud("path_to_pcd_file.ply") # 初始化变量存储多个平面的结果 planes = [] inliers_all = [] for i in range(3): # 假设最多提取三个不同的平面 plane_model, inliers = pcd.segment_plane(distance_threshold=0.01, ransac_n=3, num_iterations=1000) planes.append(plane_model) # 创建一个新的点云集仅包含内点用于下一轮迭代 inlier_cloud = pcd.select_by_index(inliers) inliers_all.extend(inliers) # 移除已识别平面上的点以便继续寻找其他潜在平面 pcd = pcd.select_by_index(inliers, invert=True) print(f"Planes found: {len(planes)}") ``` 上述代码展示了如何通过循环调用`segment_plane()`函数多次执行分层式的平面检测过程。每次成功分离出一个平面之后都会更新原始点云集合,从而确保后续操作只针对剩余未分类的部分进行处理[^4]。 #### 计算两平面间的最短距离 一旦获得了两个独立的平面方程\[ax + by + cz + d = 0\],就可以利用向量代数原理求解它们之间最小垂直间距: 假设给定两个平面\(P_1\) 和 \(P_2\) 的标准形式分别为 \((a_1,b_1,c_1,d_1)\),\((a_2,b_2,c_2,d_2)\): 如果这两个平面平行,则可以通过选取其中一个平面上任意一点并计算它到另一个平面的距离作为两者之间的距离;如果不平行则交集为空因此不存在实际意义下的“距离”。 对于非平行情况,这里提供一种简化方案——取第一个平面上任选的一组坐标(x,y,z),带入第二个平面表达式得到z值差绝对值得近似结果: ```python def distance_between_planes(plane1_coeffs, plane2_coeffs): a1, b1, c1, d1 = plane1_coeffs a2, b2, c2, d2 = plane2_coeffs if abs(a1*a2+b1*b2+c1*c2)/(np.sqrt(a1**2+b1**2+c1**2)*np.sqrt(a2**2+b2**2+c2**2)) >= 0.99: point_on_plane1 = [-b1*d1/(a1**2+b1**2), -a1*d1/(a1**2+b1**2), 0] dist = abs(a2*point_on_plane1[0]+b2*point_on_plane1[1]+c2*point_on_plane1[2]+d2)/np.sqrt(a2**2+b2**2+c2**2) else: dist = None return dist ``` 此部分逻辑实现了当且仅当所考虑的两个平面几乎完全平行时才返回有效数值的情况。否则认为二者相交而不具备定义上的固定间隔。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值