PCL专栏目录及须知
注意:区域生长分割,对于分割建筑物或地面的各个平面,有较好的效果。
1.区域生长分割
将具有法线或者曲率相似性的点云集合起来构成一簇聚类点。
(1)计算点云法线及曲率。
(2)根据点云的曲率值对输入点云进行排序,选择曲率最小的点作为初始种子点P(曲率最小处即为点云最平滑处)。
(3)搜索P邻域点,比较邻域点与当前种子点之间的法线夹角和曲率差,如果领域点同种子点的法线夹角小于法线阈值、曲率差小于曲率阈值,那么该邻域点同当前点P为一簇点。
(4)对P的所有邻域点执行(3)的搜索步骤,直到无法再往当前点云簇中添加点,记录当前点云簇。
(5)重复步骤(2)-(4),遍历计算所有点云点,得到所有点云簇。
即类似于欧式聚类,从初始点开始摸点云,摸出所有点云簇,只是判断法则从距离阈值变为了法线夹角和曲率差。
如下图:
假设我们计算得到的最小曲率初始点P在红色部分中,
1)我们首先通过法线夹角和曲率差,不停摸完附近点云,直到摸无可摸,得到红色簇的点云簇。
2)更换初始点P的位置(红色簇已聚类,被剔除,因此只能在其他点云中得到初始点P)。假设更换后的初始点P在蓝色簇中,再进行1)的步骤,摸出蓝色簇。
3)如此反复,直到得到所有点云点。
2.使用介绍
对于分割建筑物或地面的各个平面,有较好的效果,工作中经常使用。
3.关键函数
(1)法向量夹角的阈值,超过这个夹角,即认为不在一个平面上
segmentation.setSmoothnessThreshold(pcl::deg2rad(3.0));
(2)曲率的阈值,超过这个阈值,即认为不在一个平面上
segmentation.setCurvatureThreshold(1.0);
4.完整代码
#include <vector>
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/search/kdtree.h>
#include <pcl/common/angles.h>
#include <pcl/features/normal_3d_omp.h>
#include <pcl/segmentation/region_growing.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
int main(int )
{
/****************区域生长分割********************/
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); // 源点云
pcl::io::loadPCDFile("D:/code/csdn/data/JZDM.pcd", *cloud);
pcl::PointCloud<pcl::PointXYZ>::Ptr resultCloud(new pcl::PointCloud<pcl::PointXYZ>); // 结果点云
// 区域生长分割
// 法线和表面曲率估计
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
pcl::PointCloud <pcl::Normal>::Ptr normals(new pcl::PointCloud <pcl::Normal>);
pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> n;
n.setInputCloud(cloud);
n.setSearchMethod(tree);
n.setNumberOfThreads(6); // 设置线程数
n.setKSearch(20); // k近邻数目
n.compute(*normals);
// 区域生长分割
pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> segmentation;
segmentation.setMinClusterSize(50); // 一簇聚类最小点数
segmentation.setMaxClusterSize(1000000); // 一簇聚类最大点数
segmentation.setSearchMethod(tree);
segmentation.setNumberOfNeighbours(30); // K邻域搜索点个数
segmentation.setInputCloud(cloud);
segmentation.setInputNormals(normals);
segmentation.setSmoothnessThreshold(pcl::deg2rad(3.0)); // 法向量夹角的阈值,超过这个夹角,即认为不在一个平面上
segmentation.setCurvatureThreshold(1.0); // 曲率的阈值,超过这个阈值,即认为不在一个平面上
std::vector <pcl::PointIndices> clustersIndices; // 聚类结果
segmentation.extract(clustersIndices);
// 提取聚类后点云
std::vector<pcl::PointCloud<pcl::PointXYZ>::Ptr> clustreClouds;
for (size_t i = 0; i < clustersIndices.size(); i++)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudTemp(new pcl::PointCloud<pcl::PointXYZ>);
pcl::copyPointCloud(*cloud, clustersIndices[i], *cloudTemp);
clustreClouds.push_back(cloudTemp);
}
/****************展示********************/
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("EuclideanClusterExtraction"));
for (size_t i = 0; i < clustreClouds.size(); i++)
{
pcl::RGB rgb = pcl::getRandomColor(0, 255);
viewer->addPointCloud<pcl::PointXYZ>(clustreClouds.at(i), pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>(clustreClouds.at(i), rgb.r, rgb.g, rgb.b), "clustreClouds" + i);
}
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return (0);
}
5.结果展示
分割前的点云:
分割后的点云(分割出了各个平面):