9.pcl 点云分割

平面点云分割

#include <iostream>
#include <pcl/ModelCoefficients.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/sample_consensus/method_types.h> //随机参数估计方法头文件
#include <pcl/sample_consensus/model_types.h>  //模型定义头文件
#include <pcl/segmentation/sac_segmentation.h> //基于采样一致性分割的类的头文件

int main(int argc, char** argv)
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

    // 填充点云
    cloud->width = 15;
    cloud->height = 1;
    cloud->points.resize(cloud->width * cloud->height);

    // 生成数据,采用随机数填充点云的x,y坐标,都处于z为1的平面上
    for (size_t i = 0; i < cloud->points.size(); ++i)
    {
        cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
        cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
        cloud->points[i].z = 1.0;
    }

    // 设置几个局外点,即重新设置几个点的z值,使其偏离z为1的平面
    cloud->points[0].z = 2.0;
    cloud->points[3].z = -2.0;
    cloud->points[6].z = 4.0;

    std::cerr << "Point cloud data: " << cloud->points.size() << " points" << std::endl; //打印
    for (size_t i = 0; i < cloud->points.size(); ++i)
        std::cerr << "    " << cloud->points[i].x << " "
        << cloud->points[i].y << " "
        << cloud->points[i].z << std::endl;
    //创建分割时所需要的模型系数对象,coefficients及存储内点的点索引集合对象inliers
    pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
    pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
    // 创建分割对象
    pcl::SACSegmentation<pcl::PointXYZ> segmentation;  // 重命名变量,避免可能的命名冲突
    // 可选择配置,设置模型系数需要优化
    segmentation.setOptimizeCoefficients(true);
    // 必要的配置,设置分割的模型类型,所用的随机参数估计方法,距离阀值,输入点云
    segmentation.setModelType(pcl::SACMODEL_PLANE); //设置模型类型
    segmentation.setMethodType(pcl::SAC_RANSAC);    //设置随机采样一致性方法类型
    segmentation.setDistanceThreshold(0.01);        //设定距离阀值,距离阀值决定了点被认为是局内点是必须满足的条件
    //表示点到估计模型的距离最大值,

    segmentation.setInputCloud(cloud);
    //引发分割实现,存储分割结果到点几何inliers及存储平面模型的系数coefficients
    segmentation.segment(*inliers, *coefficients);

    if (inliers->indices.size() == 0)
    {
        PCL_ERROR("Could not estimate a planar model for the given dataset.");
        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 (size_t i = 0; i < inliers->indices.size(); ++i)
        std::cerr << inliers->indices[i] << "    " << cloud->points[inliers->indices[i]].x << " "
        << cloud->points[inliers->indices[i]].y << " "
        << cloud->points[inliers->indices[i]].z << std::endl;

    return (0);
}

在这里插入图片描述

提取圆柱和平面

// 引入必要的头文件
#include <pcl/ModelCoefficients.h>             // 用于存储模型系数(如平面方程、圆柱体参数等)
#include <pcl/io/pcd_io.h>                     // 提供PCD文件的读写功能
#include <pcl/point_types.h>                   // 定义各种点类型结构
#include <pcl/filters/extract_indices.h>       // 根据索引提取点云子集
#include <pcl/filters/passthrough.h>           // 直通滤波器,可以按坐标轴进行范围过滤
#include <pcl/features/normal_3d.h>            // 用于计算点云表面法线
#include <pcl/sample_consensus/method_types.h> // 定义随机采样一致性方法类型(如RANSAC)
#include <pcl/sample_consensus/model_types.h>  // 定义几何模型类型(如平面、圆柱体)
#include <pcl/segmentation/sac_segmentation.h> // 基于采样一致性的分割算法
#include <pcl/visualization/pcl_visualizer.h>  // 点云可视化工具

typedef pcl::PointXYZ PointT;  // 定义点类型别名,简化后续代码

int main(int argc, char** argv)
{
    // 创建各种处理对象
    pcl::PCDReader reader;                                    // 用于读取PCD文件
    pcl::PassThrough<PointT> pass;                            // 直通滤波器对象
    pcl::NormalEstimation<PointT, pcl::Normal> ne;            // 法线估计对象
    pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg; // 基于法线的分割对象
    pcl::PCDWriter writer;                                    // PCD文件写入对象
    pcl::ExtractIndices<PointT> extract;                      // 点提取对象
    pcl::ExtractIndices<pcl::Normal> extract_normals;         // 法线提取对象
    pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>()); // KD树用于近邻搜索

    // 定义各种点云数据结构
    pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>); // 原始点云
    pcl::PointCloud<PointT>::Ptr cloud_filtered(new pcl::PointCloud<PointT>); // 滤波后的点云
    pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>); // 点云法线
    pcl::PointCloud<PointT>::Ptr cloud_filtered2(new pcl::PointCloud<PointT>); // 去除平面后的点云
    pcl::PointCloud<pcl::Normal>::Ptr cloud_normals2(new pcl::PointCloud<pcl::Normal>); // 去除平面后的法线
    // 模型系数和内点索引
    pcl::ModelCoefficients::Ptr coefficients_plane(new pcl::ModelCoefficients); // 平面模型系数
    pcl::ModelCoefficients::Ptr coefficients_cylinder(new pcl::ModelCoefficients); // 圆柱模型系数
    pcl::PointIndices::Ptr inliers_plane(new pcl::PointIndices); // 平面内点索引
    pcl::PointIndices::Ptr inliers_cylinder(new pcl::PointIndices); // 圆柱内点索引

    // 读取点云数据文件
    reader.read("../table_scene_mug_stereo_textured.pcd", *cloud);
    std::cerr << "PointCloud has: " << cloud->points.size() << " data points." << std::endl; // 输出点云大小

    // 直通滤波,将Z轴不在(0,1.5)范围的点过滤掉
    pass.setInputCloud(cloud); // 设置输入点云
    pass.setFilterFieldName("z"); // 设置过滤的坐标轴
    pass.setFilterLimits(0, 1.5); // 设置过滤范围
    pass.filter(*cloud_filtered); // 执行过滤,结果存入cloud_filtered
    std::cerr << "PointCloud after filtering has: " << cloud_filtered->points.size() << " data points." << std::endl;

    // 计算点云法线
    ne.setSearchMethod(tree); // 设置搜索方法为KD树
    ne.setInputCloud(cloud_filtered); // 设置输入点云
    ne.setKSearch(50); // 设置K近邻搜索的K值为50
    ne.compute(*cloud_normals); // 计算法线,结果存入cloud_normals

    // 设置平面分割参数
    seg.setOptimizeCoefficients(true); // 优化模型系数
    seg.setModelType(pcl::SACMODEL_NORMAL_PLANE); // 设置模型类型为法线平面
    seg.setNormalDistanceWeight(0.1); // 设置法线距离权重
    seg.setMethodType(pcl::SAC_RANSAC); // 设置随机采样一致性方法
    seg.setMaxIterations(100); // 设置最大迭代次数
    seg.setDistanceThreshold(0.03); // 设置距离阈值
    seg.setInputCloud(cloud_filtered); // 设置输入点云
    seg.setInputNormals(cloud_normals); // 设置输入法线
    // 执行平面分割
    seg.segment(*inliers_plane, *coefficients_plane); // 分割结果存入inliers_plane和coefficients_plane
    std::cerr << "Plane coefficients: " << *coefficients_plane << std::endl; // 输出平面系数

    // 提取平面点云
    extract.setInputCloud(cloud_filtered); // 设置输入点云
    extract.setIndices(inliers_plane); // 设置内点索引
    extract.setNegative(false); // false表示提取内点(平面上的点)

    // 保存平面点云
    pcl::PointCloud<PointT>::Ptr cloud_plane(new pcl::PointCloud<PointT>());
    extract.filter(*cloud_plane); // 执行提取,结果存入cloud_plane
    std::cerr << "PointCloud representing the planar component: " << cloud_plane->points.size() << " data points." << std::endl;
    writer.write("table_scene_mug_stereo_textured_plane.pcd", *cloud_plane, false); // 保存平面点云到文件

    // 提取非平面点云(用于后续圆柱体分割)
    extract.setNegative(true); // true表示提取外点(非平面点)
    extract.filter(*cloud_filtered2); // 执行提取,结果存入cloud_filtered2
    // 同样提取非平面点的法线
    extract_normals.setNegative(true);
    extract_normals.setInputCloud(cloud_normals);
    extract_normals.setIndices(inliers_plane);
    extract_normals.filter(*cloud_normals2); // 结果存入cloud_normals2

    // 设置圆柱体分割参数
    seg.setOptimizeCoefficients(true); // 优化模型系数
    seg.setModelType(pcl::SACMODEL_CYLINDER); // 设置模型类型为圆柱体
    seg.setMethodType(pcl::SAC_RANSAC); // 设置随机采样一致性方法
    seg.setNormalDistanceWeight(0.1); // 设置法线距离权重
    seg.setMaxIterations(10000); // 设置最大迭代次数
    seg.setDistanceThreshold(0.05); // 设置距离阈值
    seg.setRadiusLimits(0, 0.1); // 设置圆柱体半径范围
    seg.setInputCloud(cloud_filtered2); // 设置输入点云(非平面点)
    seg.setInputNormals(cloud_normals2); // 设置输入法线

    // 执行圆柱体分割
    seg.segment(*inliers_cylinder, *coefficients_cylinder); // 分割结果存入inliers_cylinder和coefficients_cylinder
    std::cerr << "Cylinder coefficients: " << *coefficients_cylinder << std::endl; // 输出圆柱体系数

    // 提取圆柱体点云
    extract.setInputCloud(cloud_filtered2); // 设置输入点云
    extract.setIndices(inliers_cylinder); // 设置内点索引
    extract.setNegative(false); // false表示提取内点(圆柱体上的点)
    pcl::PointCloud<PointT>::Ptr cloud_cylinder(new pcl::PointCloud<PointT>());
    extract.filter(*cloud_cylinder); // 执行提取,结果存入cloud_cylinder
    
    // 检查是否找到圆柱体
    if (cloud_cylinder->points.empty())
        std::cerr << "Can't find the cylindrical component." << std::endl;
    else
    {
        std::cerr << "PointCloud representing the cylindrical component: " << cloud_cylinder->points.size() << " data points." << std::endl;
        writer.write("table_scene_mug_stereo_textured_cylinder.pcd", *cloud_cylinder, false); // 保存圆柱体点云到文件
    }
    
    // 创建可视化对象
    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("three 三窗口 "));
    
    // 创建三个视口
    int v1(0); // 左侧视口
    int v2(1); // 右下视口
    int v3(2); // 右上视口
    viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1); // 设置左侧视口坐标
    viewer->createViewPort(0.5, 0.0, 1.0, 0.5, v2); // 设置右下视口坐标
    viewer->createViewPort(0.5, 0.5, 1.0, 1.0, v3); // 设置右上视口坐标

    // 在三个视口中分别显示不同的点云
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_out_red(cloud, 255, 0, 0); // 设置原始点云为红色
    viewer->addPointCloud(cloud, cloud_out_red, "cloud_out1", v1); // 在左侧视口显示原始点云

    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_out_green(cloud, 0, 255, 0); // 设置平面点云为绿色
    viewer->addPointCloud(cloud_plane, cloud_out_green, "cloud_out2", v2); // 在右下视口显示平面点云

    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_out_blue(cloud, 0, 0, 255); // 设置圆柱体点云为蓝色
    viewer->addPointCloud(cloud_cylinder, cloud_out_blue, "cloud_out3", v3); // 在右上视口显示圆柱体点云

    // 阻塞式显示,直到用户关闭窗口
    viewer->spin();
    
    // 非阻塞式显示方式(被注释掉了)
    // while (!viewer->wasStopped())
    // {
    //     viewer->spinOnce();
    // }
    
    return (0); // 程序正常结束
}

在这里插入图片描述

欧式聚类

// 引入必要的头文件
#include <pcl/ModelCoefficients.h>             // 用于存储模型系数
#include <pcl/point_types.h>                   // 定义各种点类型
#include <pcl/io/pcd_io.h>                     // PCD文件读写功能
#include <pcl/filters/extract_indices.h>       // 点云索引提取
#include <pcl/filters/voxel_grid.h>            // 体素网格滤波
#include <pcl/features/normal_3d.h>            // 法线特征计算
#include <pcl/kdtree/kdtree.h>                 // KD树数据结构
#include <pcl/sample_consensus/method_types.h> // 随机采样一致性方法
#include <pcl/sample_consensus/model_types.h>  // 几何模型类型
#include <pcl/segmentation/sac_segmentation.h> // 基于采样一致性的分割
#include <pcl/segmentation/extract_clusters.h> // 欧式聚类提取

/******************************************************************************
 打开点云数据,并对点云进行滤波重采样预处理,然后采用平面分割模型对点云进行分割处理
 提取出点云中所有在平面上的点集,并将其存盘
******************************************************************************/
int main(int argc, char** argv)
{
    // 读取点云数据
    pcl::PCDReader reader;
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>), cloud_f(new pcl::PointCloud<pcl::PointXYZ>);
    reader.read("../table_scene_lms400.pcd", *cloud); // 读取点云文件
    std::cout << "PointCloud before filtering has: " << cloud->points.size() << " data points." << std::endl; // 输出原始点云大小

    // 创建体素网格下采样对象:使用1cm的叶子大小对数据集进行下采样
    pcl::VoxelGrid<pcl::PointXYZ> vg; // VoxelGrid类在输入点云数据上创建3D体素网格(将体素网格视为一组空间中的微小3D框)
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
    vg.setInputCloud(cloud); // 设置输入点云
    vg.setLeafSize(0.01f, 0.01f, 0.01f);  // 设置体素大小为1cm³
    vg.filter(*cloud_filtered); // 执行滤波,结果存入cloud_filtered
    std::cout << "PointCloud after filtering has: " << cloud_filtered->points.size() << " data points." << std::endl; // 输出滤波后点云大小
    
    // 创建平面模型分割对象并设置参数
    pcl::SACSegmentation<pcl::PointXYZ> seg;
    pcl::PointIndices::Ptr inliers(new pcl::PointIndices); // 存储内点索引
    pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients); // 存储模型系数
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane(new pcl::PointCloud<pcl::PointXYZ>()); // 存储平面点云

    pcl::PCDWriter writer; // 创建PCD文件写入对象
    seg.setOptimizeCoefficients(true); // 优化模型系数
    seg.setModelType(pcl::SACMODEL_PLANE);    // 设置分割模型为平面
    seg.setMethodType(pcl::SAC_RANSAC);       // 使用RANSAC方法
    seg.setMaxIterations(100);                // 最大迭代次数为100
    seg.setDistanceThreshold(0.02);           // 设置距离阈值为2cm

    // 循环提取平面,直到剩余点云数量小于原始点云的30%
    int i = 0, nr_points = (int)cloud_filtered->points.size();
    while (cloud_filtered->points.size() > 0.3 * nr_points) // 当剩余点云数量大于原始点云的30%时继续循环
    {
        // 从剩余点云中分割最大的平面组件
        seg.setInputCloud(cloud_filtered); // 设置输入点云
        seg.segment(*inliers, *coefficients); // 执行分割,结果存入inliers和coefficients
        if (inliers->indices.size() == 0) // 如果没有找到平面
        {
            std::cout << "Could not estimate a planar model for the given dataset." << std::endl;
            break;
        }

        // 提取平面点云
        pcl::ExtractIndices<pcl::PointXYZ> extract;
        extract.setInputCloud(cloud_filtered);
        extract.setIndices(inliers);
        extract.setNegative(false); // false表示提取内点(平面上的点)

        // 获取与平面相关的点
        extract.filter(*cloud_plane); // 执行提取,结果存入cloud_plane
        std::cout << "PointCloud representing the planar component: " << cloud_plane->points.size() << " data points." << std::endl;

        // 移除平面内点,提取剩余点云
        extract.setNegative(true); // true表示提取外点(非平面点)
        extract.filter(*cloud_f); // 执行提取,结果存入cloud_f
        *cloud_filtered = *cloud_f; // 更新cloud_filtered为剩余点云
    }

    // 为提取算法的搜索方法创建KdTree对象
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
    tree->setInputCloud(cloud_filtered); // 设置输入点云

    // 欧式聚类处理
    std::vector<pcl::PointIndices> cluster_indices; // 存储聚类结果的索引
    pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;   // 创建欧式聚类对象
    ec.setClusterTolerance(0.02);                     // 设置近邻搜索的搜索半径为2cm
    ec.setMinClusterSize(100);                 // 设置一个聚类需要的最少点数为100
    ec.setMaxClusterSize(25000);               // 设置一个聚类需要的最大点数为25000
    ec.setSearchMethod(tree);                    // 设置点云的搜索机制为KdTree
    ec.setInputCloud(cloud_filtered);           // 设置输入点云
    ec.extract(cluster_indices);           // 执行聚类,结果存入cluster_indices
    
    // 迭代访问点云索引cluster_indices,处理每个聚类
    int j = 0;
    for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin(); it != cluster_indices.end(); ++it)
    {
        // 为当前聚类创建新的点云
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster(new pcl::PointCloud<pcl::PointXYZ>);
        // 将当前聚类中的所有点添加到新点云中
        for (std::vector<int>::const_iterator pit = it->indices.begin(); pit != it->indices.end(); ++pit)
            cloud_cluster->points.push_back(cloud_filtered->points[*pit]);
        
        // 设置点云属性
        cloud_cluster->width = cloud_cluster->points.size();
        cloud_cluster->height = 1;
        cloud_cluster->is_dense = true;

        // 输出当前聚类信息并保存到文件
        std::cout << "PointCloud representing the Cluster: " << cloud_cluster->points.size() << " data points." << std::endl;
        std::stringstream ss;
        ss << "../cloud_cluster_" << j << ".pcd"; // 生成文件名
        writer.write<pcl::PointXYZ>(ss.str(), *cloud_cluster, false); // 保存聚类点云到文件
        j++; // 聚类计数器递增
    }

    return (0);
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值