PCL点云分割(3)

时间:2022-11-04 22:25:00

(1)Euclidean分割

欧几里德分割法是最简单的。检查两点之间的距离。如果小于阈值,则两者被认为属于同一簇。它的工作原理就像一个洪水填充算法:在点云中的一个点被“标记”则表示为选择在一个的集群中。然后,它像病毒一样扩散到其他足够近的点,从这些点到更多点,直到没有新的添加为止。这样,就是一个初始化的新的群集,并且该过程将以剩余的无标记点再次进行。

在PCL中,Euclidean分割法如下:

#include <pcl/io/pcd_io.h>
#include <pcl/segmentation/extract_clusters.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>(argv[1], *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 (small values may cause objects to be divided
    // in several clusters, whereas big values may join objects in a same cluster).
    clustering.setClusterTolerance(0.02);
    // 设置聚类的小点数和最大点云数
    clustering.setMinClusterSize(100);
    clustering.setMaxClusterSize(25000);
    clustering.setSearchMethod(kdtree);
    clustering.setInputCloud(cloud);
    std::vector<pcl::PointIndices> clusters;
    clustering.extract(clusters);

    // For every cluster...
    int currentClusterNum = 1;
    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() <= 0)
            break;
        std::cout << "Cluster " << currentClusterNum << " has " << cluster->points.size() << " points." << std::endl;
        std::string fileName = "cluster" + boost::to_string(currentClusterNum) + ".pcd";
        pcl::io::savePCDFileASCII(fileName, *cluster);

        currentClusterNum++;
    }
}

那么 比如我要分割一张点云文件可视化如下

PCL点云分割(3)

那么可以看到结果

PCL点云分割(3)

很明显每一个英文字幕都会被提取出来,比如我们显示其中的一个聚类

PCL点云分割(3)

当然我们把点云中的所有聚类生成了一个个的单独文件,那么有人在我的微信公众号后台提问,如何把所有的聚类的结果,在一个可视化窗口中显示呢?所以该如何解决,个人理解就是虽然聚类的结果分解出来了,但是,每一个聚类对象相对与原来点云的坐标以及性质都没有改变所以我们直接就把聚类的结果相加就可以实现了,那么我们只需要在结尾的地方添加如下几行代码

  *add_cloud+=*cloud_cluster;
  pcl::io::savePCDFileASCII("add_cloud.pcd",*add_cloud);

当然前面要声明add_cloud的数据格式,所以我们就看一下显示的试验结果。我们用table_scene_lms400.pcd文件来查看结果

PCL点云分割(3)

                                                              原始PCD文件可视化的结果

那么我们只想看除去两个平面的其他聚类,所以我们只需要添加两行代码即可,可视化的结果如下

PCL点云分割(3)

 (2)Conditional Euclidean分割

条件欧几里德分割的工作方式与(1)所示的标准的欧几里德分割方法基本一样,条件分割除了要距离检查,点还需要满足一个特殊的,可以自定义的要求的限制,这样它们被添加到一个集群。此条件是用户指定的。它归结为如下:对于每一对点(第一个点,作为种子点,第二个点,作为候选点,是一个临近点的选择与计算比较等操作)将会有自定义函数。此函数具有一定的特性:这两个点具有副本,以便我们可以执行我们自己的选择计算的平方距离
函数并返回布尔值。如果值为true,则可以将候选添加到群集。如果假,它不会被添加,即使它通过距离检查。

#include <pcl/io/pcd_io.h>
#include <pcl/segmentation/conditional_euclidean_clustering.h>

#include <iostream>

// 如果这个函数返回的是真,这这个候选点将会被加入聚类中
bool
customCondition(const pcl::PointXYZ& seedPoint, const pcl::PointXYZ& candidatePoint, float squaredDistance)
{
    // Do whatever you want here.做你想做的条件的筛选
    if (candidatePoint.y < seedPoint.y)  //如果候选点的Y的值小于种子点的Y值(就是之前被选择为聚类的点),则不满足条件,返回假
        return false;

    return true;
}

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

    if (pcl::io::loadPCDFile<pcl::PointXYZ>(argv[1], *cloud) != 0)
    {
        return -1;
    }

    pcl::ConditionalEuclideanClustering<pcl::PointXYZ> clustering;
    clustering.setClusterTolerance(0.02);
    clustering.setMinClusterSize(100);
    clustering.setMaxClusterSize(25000);
    clustering.setInputCloud(cloud);
        //设置每次检测一对点云时的函数
    clustering.setConditionFunction(&customCondition);
    std::vector<pcl::PointIndices> clusters;
    clustering.segment(clusters);

    int currentClusterNum = 1;
    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() <= 0)
            break;
        std::cout << "Cluster " << currentClusterNum << " has " << cluster->points.size() << " points." << std::endl;
        std::string fileName = "cluster" + boost::to_string(currentClusterNum) + ".pcd";
        pcl::io::savePCDFileASCII(fileName, *cluster);

        currentClusterNum++;
    }
}

 关于条件聚类在这里就不再赘述,等用到了在回来研究研究吧,

谢谢暂时就到这里了********************

PCL点云分割(3)