基于 PCL 实现三维点云欧式聚类

PCL: Point Cloud Library,集成大量点云处理算法的 C++ 库
Euclidean Cluster - 欧式聚类

基础知识

什么是欧式聚类

将点云分成若干个子点云(类),使得对于每个类中的任意一点,都存在类中的另一点与其欧式距离小于一个给定的阈值。

来源】Abstractly, euclidean clustering groups points into clusters such that for any two points in a cluster, there exists a chain of points also within that cluster between both points such that the projected distance between subsequent points in the chain is less than some threshold.

欧式聚类和 K-Means 聚类的区别

欧式聚类旨在判断点与点之间的距离是否超出阈值,其效果如下图所示。此时上下两部分之间的距离正好超过阈值,因此被判为两类,如果在上下两部分之间增加一些点进行衔接,那么欧式聚类会将其聚为一类。
在这里插入图片描述

K-Means 旨在寻找中心点,找到中心点后,所有点云中的点自动分类到距离自己最近的中心点,K-Means 聚成两类(k=2)的效果如下图所示。
在这里插入图片描述

图片来源

PCL 代码实现

实现三维点云欧式聚类不用 PCL 也可以,Github 上有一些速度更快的欧式聚类算法(因为使用近似算法导致速度变快),例如【这个】,用法和 PCL 类似。

C++ 代码

引入头文件:

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/search/kdtree.h>
#include <pcl/segmentation/extract_clusters.h>

待聚类的点云数据指针:

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

指针数据可以直接读取 .pcd 文件获得:

pcl::PCDReader reader;
reader.read("table_scene_lms400.pcd", *cloud);

也可以通过接收 ROS 消息并转换类型得到:

#include <sensor_msgs/PointCloud2.h>

template<typename T>
pcl::PointCloud<T> cloudmsg2cloud(sensor_msgs::PointCloud2 cloudmsg) {
    pcl::PointCloud<T> cloudresult;
    pcl::fromROSMsg(cloudmsg,cloudresult);
    return cloudresult;
}

void callbackNode(const sensor_msgs::PointCloud2::ConstPtr &msg) {
	*cloud = cloudmsg2cloud<pcl::PointXYZ>(*msg);
}

定义 KdTree 并将点云数据指针输入其中:

pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
tree->setInputCloud(cloud);

定义用于存放聚类结果的向量容器如下,类似于一个二维数组,cluster_indices[0] 表示第一个点云类中所有点的索引,通过索引可以在点云数据指针 cloud 中找到这个点(例如 (*cloud)[idx],其中 idx 是索引)。

std::vector<pcl::PointIndices> cluster_indices;

使用 PCL 欧式聚类算法的主体:

pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
ec.setClusterTolerance(0.02); // 单位为 m
ec.setMinClusterSize(1);
ec.setMaxClusterSize(25000);
ec.setSearchMethod(tree);
ec.setInputCloud(cloud);
ec.extract(cluster_indices);

使用聚类结果的示例:

int j = 0;
for (const auto& cluster : cluster_indices) {  // 对于向量容器中的每一个点云类 cluster
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster (new pcl::PointCloud<pcl::PointXYZ>);
    for (const auto& idx : cluster.indices) {  // 对于点云类中的每一个点的索引 idx
        cloud_cluster->push_back((*cloud)[idx]);  // 把同一个类的点合成到一个新的点云数据指针 cloud_cluster 中
    }
    cloud_cluster->width = cloud_cluster->size ();
    cloud_cluster->height = 1;
    cloud_cluster->is_dense = true;

    std::cout << "PointCloud representing the Cluster: " << cloud_cluster->size () << " data points." << std::endl;
    std::stringstream ss;
    ss << std::setw(4) << std::setfill('0') << j;
    pcl::PCDWriter writer;
    writer.write<pcl::PointXYZ> ("cloud_cluster_" + ss.str () + ".pcd", *cloud_cluster, false);  // 输出到 .pcd 文件
    j++;
  }

参数解析

最重要的参数就是欧式聚类的阈值,即 ec.setClusterTolerance(0.02) 中的 0.02。减小这个值会导致原本聚成一类的点云被聚为多个类,增大这个值会导致原本聚为多个类的点云被聚为一个类。

ec.setMinClusterSize(1)ec.setMaxClusterSize(25000) 分别规定了最少聚类数量和最大聚类数量,一般不会特意调整。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值