当前位置: 首页>前端>正文

点云实例分割代码实现 点云分割的五种方法

1.点云切割概述

点云分割是根据空间,几何和纹理等特征对点云进行划分,使得同一划分内的点云拥有相似的特征,点云的有效分割往往是许多应用的前提,例如逆向工作,CAD领域对零件的不同扫描表面进行分割,然后才能更好的进行空洞修复曲面重建,特征描述和提取,进而进行基于3D内容的检索,组合重用等。

2.PCL中常用的点云分割方法

Plane model segmentation (平面模型分割):这个算法能够把地面等一些平面给分割出来,方便后面的物体的点云分割。
Cylinder model segmentation(圆柱模型分割):这个算法能够把一些圆柱体分割出来,方便后面的物体的点云分割。
Euclidean Cluster Extraction (欧几里德聚类提取):这个算法通俗来讲就是先从点云中找出一个点p0,然后寻找p0周围最近的n个点,如果这n个点与p0之间的距离小于预先设定的阈值,那么就把这个点取出,依次重复。
Region growing segmentation (区域蔓延分割):对于普通点云,其可由法线、曲率估计算法获得其法线和曲率值。通过法线和曲率来判断某点是否属于该类,向周边蔓延直至完成。
Progressive Morphological Filter(渐进形态过滤器):这个算法本身用于处理高空获取的激光雷达数据,把地面与非地面的物体分割,也就是可以将地面分割出来使其他物体悬空,为下一步打基础。

3.几种分割的使用实例

3.1.简单平面分割

这个算法分割点云的平面的原理就是,通过改变平面模型(ax+by+cz+d=0)的参数,找出哪一组的参数能使得这个模型一定程度上拟合最多的点。这个程度就是由distancethreshold这个参数来设置。那找到这组参数后,这些能够被拟合的点就是平面的点。总体感受就是这个算法如果理解的不好调参有点复杂。
代码:

#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>   //基于采样一致性分割的类的头文件
#include <pcl/filters/extract_indices.h>
#include <pcl/visualization/cloud_viewer.h>

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

  // 读取一个PCD文件
   if (pcl::io::loadPCDFile<pcl::PointXYZ>("d:/2.pcd", *cloud) != 0)
   {
       return -1;
   }


  //创建分割时所需要的模型系数对象,coefficients及存储内点的点索引集合对象inliers
  pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
  pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
  // 创建分割对象
  pcl::SACSegmentation<pcl::PointXYZ> seg;
  // 可选择配置,设置模型系数需要优化
  seg.setOptimizeCoefficients (true);
  // 必要的配置,设置分割的模型类型,所用的随机参数估计方法,距离阀值,输入点云
  seg.setModelType (pcl::SACMODEL_PLANE);   //设置模型类型
  seg.setMethodType (pcl::SAC_RANSAC);      //设置随机采样一致性方法类型
  seg.setDistanceThreshold (5);    //设定距离阀值,距离阀值决定了点被认为是局内点是必须满足的条件
                                       //表示点到估计模型的距离最大值,

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

  if (inliers->indices.size () == 0)
  {
    return (-1);
  }

   pcl::ExtractIndices<pcl::PointXYZ> extract;        //创建点云提取对象
  extract.setInputCloud (cloud);
      extract.setIndices (inliers);
      extract.setNegative (false);
   extract.filter (*cloud);

   //点云可视化
   pcl::visualization::CloudViewer viewer("sa");

       viewer.showCloud(cloud);
        while (!viewer.wasStopped()){
        }

  return (0);
}

处理前:

点云实例分割代码实现 点云分割的五种方法,点云实例分割代码实现 点云分割的五种方法_pcl,第1张

处理后:

点云实例分割代码实现 点云分割的五种方法,点云实例分割代码实现 点云分割的五种方法_qt_02,第2张

要的就是这种效果,这样就可以将地面分割出去,使车辆悬空,然后通过后面的进一步处理将货车显示出来

3.2.圆柱模型分割

圆柱模型分割的具体原理与平面模型分割类似,如果理解了平面模型分割那么圆柱模型分割也就容易理解了,这里就不再详述。

3.3.欧几里德聚类提取

这个算法通俗来讲就是先从点云中找出一个点p0,然后寻找p0周围最近的n个点,如果这n个点与p0之间的距离小于预先设定的阈值,那么就把这个点取出,依次重复,最终分割出多个点云。
代码:

#include <pcl/io/pcd_io.h>
#include <pcl/segmentation/extract_clusters.h>
#include <QDebug>
#include <pcl/visualization/cloud_viewer.h>

#include <iostream>

int
main(int argc, char** argv)
{
    // 申明存储点云的对象.
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

    // 读取一个PCD文件
    if (pcl::io::loadPCDFile<pcl::PointXYZ>("d:/2.pcd", *cloud) != 0)
    {
        return -1;
    }
    // 建立kd-tree对象用来搜索
    pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree(new pcl::search::KdTree<pcl::PointXYZ>);
    kdtree->setInputCloud(cloud);

    // Euclidean 聚类对象.
    pcl::EuclideanClusterExtraction<pcl::PointXYZ> clustering;
    // 设置聚类的最小值 2cm
    clustering.setClusterTolerance(0.2);
    // 设置聚类的小点数和最大点云数
    clustering.setMinClusterSize(1000);
    //clustering.setMaxClusterSize(25000);
    clustering.setSearchMethod(kdtree);
    clustering.setInputCloud(cloud);
    std::vector<pcl::PointIndices> clusters;
    clustering.extract(clusters);
    // For every cluster...
    for (std::vector<pcl::PointIndices>::const_iterator i = clusters.begin(); i != clusters.end(); ++i)
    {
        //添加所有的点云到一个新的点云中
        pcl::PointCloud<pcl::PointXYZ>::Ptr cluster(new pcl::PointCloud<pcl::PointXYZ>);
        for (std::vector<int>::const_iterator point = i->indices.begin(); point != i->indices.end(); point++)
        cluster->points.push_back(cloud->points[*point]);
        cluster->width = cluster->points.size();
        cluster->height = 1;
        cluster->is_dense = true;
        //实验预先知道了最大的点云数量,偷懒就不排序查找点云数量最大的点云了
        if(cluster->points.size()>10000){
            //处理后点云显示
            pcl::visualization::CloudViewer viewer("PCL滤波");
            viewer.showCloud(cluster);
            while (!viewer.wasStopped()){
            }
        }
    }
}

处理前:

点云实例分割代码实现 点云分割的五种方法,点云实例分割代码实现 点云分割的五种方法_点云_03,第3张

处理后:

点云实例分割代码实现 点云分割的五种方法,点云实例分割代码实现 点云分割的五种方法_点云_04,第4张

效果很明显了,这已经很接近想要的结果了,但是目前默认的是货车的点云数量最多,所以很多地方还不够严谨。更好的实现方法应该是与其他处理结合。

3.4.区域蔓延分割

区域生长算法直观感觉上和欧几里德算法相差不大,都是从一个点出发,最终占领整个被分割区域。欧几里德算法是通过距离远近,来判断该点是否为要找的点,而区域蔓延分割算法是通过曲率、法线方向的来判断的。对于普通点云,先按照点的曲率值对点进行排序,找到最小曲率值点,并把它添加到种子点集,从种子的位置出发,开始往四周搜索点,然后比对点于点之间的曲率和法线方向,如果差距小于阈值就视为同一个簇。如果一个簇无法再蔓延,在剩下的点云里再找曲率小的面播种,然后继续重复直到遍历完毕。
代码:

#include <iostream>
#include <vector>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/search/search.h>
#include <pcl/search/kdtree.h>
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/region_growing.h>

int
main (int argc, char** argv)
{
    //读取PCL文件
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
  if ( pcl::io::loadPCDFile <pcl::PointXYZ> ("d:/2.pcd", *cloud) == -1)
  {
    std::cout << "Cloud reading failed." << std::endl;
    return (-1);
  }


  //创建法线估计对象,估计点云法向量
  pcl::search::Search<pcl::PointXYZ>::Ptr tree = boost::shared_ptr<pcl::search::Search<pcl::PointXYZ> > (new pcl::search::KdTree<pcl::PointXYZ>);
  pcl::PointCloud <pcl::Normal>::Ptr normals (new pcl::PointCloud <pcl::Normal>);
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_estimator;
  normal_estimator.setSearchMethod (tree);
  normal_estimator.setInputCloud (cloud);
  normal_estimator.setKSearch (50);
  normal_estimator.compute (*normals);


  //创建区域绵延分割对象
  pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;
  //设置最小点簇的点数min
  reg.setMinClusterSize (50);
  //reg.setMaxClusterSize (1000000);
  reg.setSearchMethod (tree);
  reg.setNumberOfNeighbours (30);
  //输入点云
  reg.setInputCloud (cloud);

  reg.setInputNormals (normals);
  //设置法线角度差
  reg.setSmoothnessThreshold (3.0 / 180.0 * M_PI);
  //设置曲率阈值
  reg.setCurvatureThreshold (1.0);

  std::vector <pcl::PointIndices> clusters;
  //切割处理保存到clusters中
  reg.extract (clusters);


  pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = reg.getColoredCloud ();
  pcl::visualization::CloudViewer viewer ("Cluster viewer");
  viewer.showCloud(colored_cloud);
  while (!viewer.wasStopped ())
  {
  }

  return (0);
}

处理前:

点云实例分割代码实现 点云分割的五种方法,点云实例分割代码实现 点云分割的五种方法_点云实例分割代码实现_05,第5张

处理后:

点云实例分割代码实现 点云分割的五种方法,点云实例分割代码实现 点云分割的五种方法_点云实例分割代码实现_06,第6张

4.总结

通过滤波和分割的学习,对分离出车辆已经有了一个实际不知是否可行的思路,具体思路如下:
1.首先由于点云数据中点的数量很大,做一些处理时耗时较多,所以第一步是使用体素滤波,实现下采样,即在保留点云原有形状的基础上减少点的数量 减少点云数据,以提高后面对点云处理的速度。
2.通过随机采样一致性(前面多出用到)分割地面,将地面从点云中分离出来,这时候地面上的物体就悬空了
3.使用统计滤波取出离散点
4.物体悬空后就更好分离了,现在就构建Kdtree通过欧几里得聚类提取获取路面上不同物体,然后车辆就提取了出来。



https://www.xamrdz.com/web/2rb1963491.html

相关文章: