PCL—点云数据分割

news/2024/5/20 9:41:55 标签: 聚类, 算法

PCL—车辆点云数据分割

    • 一.算法原理
    • 二.代码实现
    • 三.结果展示

一.算法原理

欧式聚类是一种基于欧氏距离度量的聚类算法,过程如下:

1.首先选取种子点,利用kd-tree对种子点进行半径r邻域搜索,若邻域内存在点,则与种子点归为同一聚类簇Q;
2.在聚类簇Q中选取新的种子点,继续执行步骤1),若Q中点数不再增加,则Q聚类结束;
3.设置聚类点数阈值区间[Num_min, Num_max],若聚类簇Q中点数在阈值区间内,则保存聚类结果;
4.在剩余点云中选取新的种子点,继续执行以上步骤,直到遍历完成点云中所有点。

流程图如下:
在这里插入图片描述

二.代码实现

#include <iostream>
#include <boost/thread.hpp>
#include <pcl/ModelCoefficients.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/features/normal_3d.h>
#include <pcl/kdtree/kdtree.h>
#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>
#include <pcl/visualization/pcl_visualizer.h>

int color_bar[][3] =
{
    { 255,0,0},
    { 0,255,0 },
    { 0,0,255 },
    { 0,255,255 },
    { 255,255,0 },
    { 100,100,100 },
    { 255,0,255 }
};
int
main(int argc, char** argv)
{
    // Read in the cloud data
    pcl::PCDReader reader;
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>), cloud_f(new pcl::PointCloud<pcl::PointXYZ>);
    reader.read("D:\\.....\\03.pcd", *cloud);
    std::cout << "PointCloud before filtering has: " << cloud->points.size() << " data points." << std::endl; //*

    // Create the filtering object: downsample the dataset using a leaf size of 1cm
    pcl::VoxelGrid<pcl::PointXYZ> vg;
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
    vg.setInputCloud(cloud);
    vg.setLeafSize(0.01f, 0.01f, 0.01f);//设置体素栅格叶大小,向量参数leaf_ size 是体素栅格叶大小参数,每个元素分别表示体素在XYZ方向上的尺寸
    vg.filter(*cloud_filtered);
    // std::cout << "PointCloud after filtering has: " << cloud_filtered->points.size() << " data points." << std::endl; //*
    std::cout << "PointCloud after filtering has: " << cloud_filtered->points.size() << " data points." << std::endl; //*
    // Create the segmentation object for the planar model and set all the parameters
    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;
      seg.setOptimizeCoefficients(true);
      seg.setModelType(pcl::SACMODEL_PLANE);
      seg.setMethodType(pcl::SAC_RANSAC);
      seg.setMaxIterations(100);
      seg.setDistanceThreshold(0.02);

      int i = 0, nr_points = (int)cloud_filtered->points.size();
      while (cloud_filtered->points.size() > 0.9 * nr_points)
      {
          // Segment the largest planar component from the remaining cloud
          seg.setInputCloud(cloud_filtered);
          seg.segment(*inliers, *coefficients);
          if (inliers->indices.size() == 0)
          {
              std::cout << "Could not estimate a planar model for the given dataset." << std::endl;
              break;
          }

          // Extract the planar inliers from the input cloud
          pcl::ExtractIndices<pcl::PointXYZ> extract;
          extract.setInputCloud(cloud_filtered);
          extract.setIndices(inliers);
          extract.setNegative(false);

          // Write the planar inliers to disk
          extract.filter(*cloud_plane);
          std::cout << "PointCloud representing the planar component: " << cloud_plane->points.size() << " data points." << std::endl;

          // 移去平面局内点,提取剩余点云
          extract.setNegative(true);
          extract.filter(*cloud_f);
          cloud_filtered = cloud_f;        
      }
      // Creating the KdTree object for the search method of the extraction
    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.018); //设置近邻搜索的搜索半径为2cm
    ec.setMinClusterSize(6000);    //设置一个聚类需要的最少点数目为100
    ec.setMaxClusterSize(15000);  //设置一个聚类需要的最大点数目为25000
    ec.setSearchMethod(tree);     //设置点云的搜索机制
    ec.setInputCloud(cloud_filtered); //设置原始点云 
    ec.extract(cluster_indices);  //从点云中提取聚类

    // 可视化部分
    pcl::visualization::PCLVisualizer viewer("segmention");
    // 我们将要使用的颜色
    float bckgr_gray_level = 0.0;  // 黑色
    float txt_gray_lvl = 1.0 - bckgr_gray_level;
    int num = cluster_indices.size();

    //迭代访问点云索引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>);
        //创建新的点云数据集cloud_cluster,将所有当前聚类写入到点云数据集中
        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); //*
        
        
        
        //赋予显示点云的颜色
        pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_in_color_h(cloud,
            color_bar[j][0],
            color_bar[j][1],
            color_bar[j][2]);
        viewer.addPointCloud(cloud_cluster, cloud_in_color_h, std::to_string(j));

        pcl::io::savePCDFile("D:\\.....\\033.pcd", *cloud_cluster);

        j++;
    }
    //等待直到可视化窗口关闭。
    while (!viewer.wasStopped())
    {
        viewer.spinOnce(100);
        boost::this_thread::sleep(boost::posix_time::microseconds(100000));
    }
    return (0);
}

三.结果展示

本实验用于将一个小车点云数据中的轮胎分离出来
原始点云
在这里插入图片描述

结果点云
在这里插入图片描述


http://www.niftyadmin.cn/n/782270.html

相关文章

Android开发之sharedpreferences 详解

SharedPreferences简介&#xff1a;做软件开发应该都知道&#xff0c;很多软件会有配置文件&#xff0c;里面存放这程序运行当中的各个属性值&#xff0c;由于其配置信息并不多&#xff0c;如果采用数据库来存放并不划算&#xff0c;因为数据库连接跟操作等耗时大大影响了程序的…

C# API 调用格式和参数类型

一、调用格式using System.Runtime.InteropServices; //引用此名称空间&#xff0c;简化后面的代码//使用DllImportAttribute特性来引入api函数&#xff0c;注意声明的是空方法&#xff0c;即方法体为空。[DllImport("user32.dll")]public static extern ReturnType …

切身体会设计系统时的前瞻性问题

&#xfeff;&#xfeff; &#xfeff;&#xfeff; 这几天遇到用户提出的一个新需求&#xff0c;要求在现有系统的基础上增加一种新的销售模式&#xff0c;其实也算不上新的销售模式&#xff0c; 只不过是由于收货方式有点区别&#xff0c;要在以前的价格基础上做订单时有个调…

SAP接口设计的扩展性考虑

由于现在的系统和SAP的接口出现了几次变更&#xff0c;因此需要对系统进行设计改造。由于系统中和SAP交互的接口不止一处&#xff0c;而且也是在不同的时间段进行开发&#xff0c;并由不同的人员来完成的&#xff0c;因此我在维护升级的过程中&#xff0c;发现了以前设计的可借…

REDISTEMPLATE如何注入到VALUEOPERATIONS

REDISTEMPLATE如何注入到VALUEOPERATIONS 今天看到Spring操作redis 是可以将redisTemplate注入到ValueOperations&#xff0c;避免了ValueOperations<String, Object> valueOperations redisTemplate.opsForValue(); 这样来获取ValueOperations&#xff1b; Resource(n…

UI框架(bootstrap)

一.响应式概念&#xff08;responsive web page&#xff09; 1.2010年提出一个网页可以兼容多种设备&#xff0c;而不是设备写一套网页 &#xff08;补充&#xff1a;常规的网站大部分是1200px;缩小会出现滚动条布局样式全部不会改变&#xff1b; 响应式和自适应的区别&…

浅谈最大似然估计

最大似然估计 今天我来给大家简单介绍一下最大似然估计。 首先在我们介绍最大似然估计之前先来看一看和它相关的似然函数。 其定义如下&#xff1a; 在统计学中&#xff0c;似然函数是一种关于统计模型参数的函数。给定输出x时&#xff0c;关于参数θ的似然函数L(θ|x)&#x…

.net for TCP服务端 客户端

关键代码 详细代码请看示例代码 Service //创建套接字 IPEndPoint ipe new IPEndPoint(IPAddress.Parse(ipaddress), port); //也可以使用IPAddress.Any&#xff0c;监听所有网络接口上的客户端活动 //https://docs.microsoft.com/en-us/dotnet/api/system.net.ipaddress.any?…