在激光雷达的特征提取中,对整帧的点云数据进行分割是至关重要的,但是非常明显的是在3D场景中,捕获的点云通常是稀疏且非结构化的,分割有可能误分割或者漏分割。今天我们来看一下22年的论文《FEC: Fast Euclidean Clustering for Point Cloud Segmentation》,这篇文章中提出一种新颖的快速欧几里得聚类算法,同时据作者说易于实现,具体只有40行的CPP代码。目前作者代码还没有开源,说是accept后就开源。虽然这篇文章并没有被大量的关注,但是从小代码,即插即用等特点,这给我们可以通过文章学习它的详细思想以及算法的可能性。
从整篇文章来看,该算法在现有工作中使用的聚类方案之上应用了逐点方案。文中在一开始就提到相比于传统的聚类方法,速度提升了100多倍

由于深度学习对点云的学习分割仍然还是比较耗资源的,所以使用传统的形态学分割仍然存在广泛的应用,通过对点云完成聚类,以区分不同的语义标签以及具有相同语义标签的各种实例。文中提到目前点云分割分为三大类:
而文中的方法是属于聚类方法的一种变种,针对现有工作中应用的聚类方案使用逐点聚类。与之前的算法对比,实现了类似的质量,但比现有工作加快了100倍。
首先文中提到会先对地面点进行滤除,虽然说是地面滤除,但是如果说我们需要实际上提取车道线点时候,可以直接使用地面提取,然后再对地面的特征点进行提取。文中提到使用了cloth simulation filter来提取和滤除接地点云。

CSF csf;
//step 1 Input the point cloud
csf.readPointsFromFile(terr_pointClouds_filepath);
clock_t start, end;
start = clock();
//In real application, the point cloud may be provided by main program
//csf.setPointCloud(pc);//pc is PointCloud class
//step 2 parameter setting
csf.params.bSloopSmooth = ss;
csf.params.class_threshold = atof(class_threshold.c_str());
csf.params.cloth_resolution = atof(cloth_resolution.c_str());
csf.params.interations = atoi(iterations.c_str());
csf.params.rigidness = atoi(rigidness.c_str());
csf.params.time_step = atof(time_step.c_str());
//step3 do filtering
std::vector<int> groundIndexes, offGroundIndexes;
if (argc == 2 && strcmp(argv[1], "-c")==0)
{
std::cout << "Export cloth enabled." << std::endl;
csf.do_filtering(groundIndexes, offGroundIndexes, true);
}
else
{
csf.do_filtering(groundIndexes, offGroundIndexes, false);
}
end = clock();
double dur = (double)(end - start);
printf("Use Time:%f\n", (dur / CLOCKS_PER_SEC));
csf.savePoints(groundIndexes,"ground.txt");
csf.savePoints(offGroundIndexes, "non-ground.txt");
与EC类似,我们使用欧几里得(L2)距离度量来测量无组织点云的接近度,并将相似性分组到同一聚类中,可以描述为
m i n ∥ P i − P i ′ ∥ 2 ⩾ d t h min\|\mathbf{P}_i - \mathbf{P}_{i'}\|_2 \geqslant d_{th} min∥Pi−Pi′∥2⩾dth
伪代码步骤为:

流程整体来说还是比较清楚的,首先会将 P i \mathbf{P}_i Pi的label全部置零,然后分割的 s e g L a b segLab segLab初始化为1,然后遍历所有的点,当发现遍历的点中label为0,则去找最近的关联点 P N N \mathbf{P}_{NN} PNN。如果存在有点不为0,则找到最小的label值,这就代表存在联通。如果不存在则赋值,然后对 P N N \mathbf{P}_{NN} PNN遍历,如果找到其他的label大于 m i n S e g L a b minSegLab minSegLab的值,则再次遍历所有的带你,然后将所有其他的label值,重置为 m i n S e g L a b minSegLab minSegLab的值。
文中提到本文的算法使用逐点方案,该逐点方案的输入编号顺序与EC和EG中使用的聚类方案相反,FEC易于部署,在C++中仅需40行代码。

下面是具体的代码片段,通过简单的处理即可完成代码的配置和运行