目录
必要的头文件
包含以下这些头文件,方便代码运行
#include <string>
#include <vector>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <cmath>
#include <thread>
#include <chrono>
// io
#include <pcl/io/pcd_io.h>
#include <pcl/io/openni2_grabber.h>
// transform
#include <pcl/common/common_headers.h>
#include <pcl/common/transforms.h>
#include <pcl/common/pca.h>
// filter
#include <pcl/segmentation/euclidean_cluster_comparator.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/features/normal_3d_omp.h>
// #include <pcl/filters/morphological_filter.h>
#include <pcl/filters/passthrough.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/radius_outlier_removal.h>
#include <pcl/filters/uniform_sampling.h>
#include <pcl/filters/crop_box.h>
#include <pcl/filters/convolution_3d.h>
#include <pcl/filters/project_inliers.h>
#include <pcl/filters/model_outlier_removal.h>
// segment
#include <pcl/ModelCoefficients.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/sample_consensus/ransac.h>
#include <pcl/sample_consensus/sac_model_line.h>
#include <pcl/sample_consensus/sac_model_plane.h>
#include <pcl/sample_consensus/sac_model_circle.h>
#include <pcl/sample_consensus/sac_model_circle3d.h>
#include <pcl/segmentation/region_growing.h>
#include <pcl/segmentation/segment_differences.h>
// feature
#include <pcl/common/centroid.h>
#include <pcl/common/geometry.h>
#include <pcl/common/intersections.h>
#include <pcl/features/boundary.h>
#include <pcl/features/moment_of_inertia_estimation.h>
#include <pcl/features/normal_3d.h>
#include <pcl/features/don.h>
// time
#include <pcl/common/time.h>
#include <pcl/console/time.h>
#include <pcl/console/parse.h>
// kdtree
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/kdtree/impl/kdtree_flann.hpp>
typedef pcl::PointXYZ PCLPoint;//单个点
typedef pcl::PointCloud<pcl::PointXYZ> PCLPointCloud;//点云
欧式聚类(Euclidean Clustering)
原理
点云欧几里得聚类(Euclidean Clustering)是一种将点云数据分割成不同簇的算法,其原理基于欧氏距离度量。以下是该算法的基本原理:
-
选择种子点:首先从点云数据中选择一个点作为种子点。这个点可以是随机选择的,也可以根据某种启发式方法选择。
-
确定邻域:确定种子点的邻域,即与种子点距离在指定阈值内的所有点。这个阈值通常称为聚类半径,用来控制聚类的紧密程度。
-
扩展簇:将种子点的邻域中的点加入到同一个簇中,并且对这些点进行标记,表示它们已经被分配到了某个簇。
-
递归扩展:对于每个已经加入簇的点,重复步骤 2 和步骤 3,以递归的方式扩展簇,直到所有的点都被标记为属于某个簇。
-
选择新的种子点:从未被分配到任何簇的点中选择一个新的种子点,然后重复步骤 2 到步骤 4,直到所有的点都被分配到了簇中。
-
结束条件:当所有的点都被分配到了簇中,或者种子点的选择过程无法找到新的种子点时,算法结束。
通过这样的过程,点云中的数据会被分割成不同的簇,每个簇代表着空间中的一个密集区域。这种算法通常适用于点云数据中存在明显的聚集结构的情况,但对于噪声较多或者簇之间重叠的情况,可能需要额外的处理方法。
代码
void EuclideanSeg(const PCLPointCloud::Ptr &cloud_in, double threshold_dis, std::vector<pcl::PointIndices> &cloud_clusters)
{
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree_euc(new pcl::search::KdTree<pcl::PointXYZ>);
tree_euc->setInputCloud(cloud_in); // 创建点云索引向量,用于存储实际的点云信息
pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
ec.setClusterTolerance(threshold_dis); // 设置近邻搜索的搜索半径
ec.setMinClusterSize(1); // 设置一个聚类需要的最少点数目为1
ec.setMaxClusterSize(500000000); // 设置一个聚类需要的最大点数目为500000000
ec.setSearchMethod(tree_euc); // 设置点云的搜索机制
ec.setInputCloud(cloud_in);
ec.extract(cloud_clusters); // 从点云中提取聚类,并将点云索引保存在cluster_indices中
}
使用示例
以下示例使用欧式聚类将点云分为多个聚类并读取每个聚类
std::vector<pcl::PointIndices> clusters;
PCLPointCloud::Ptr cloud_in(new PCLPointCloud);
EuclideanSeg(cloud_in, 5, clusters);//距离阈值
for (size_t i = 0; i < clusters.size(); i++)
{
PCLPointCloud::Ptr cloud_cluster(new PCLPointCloud); // 创建一个cloud_cluster指针读取每个聚类
for (std::vector<int>::const_iterator pit = clusters[i].indices.begin(); pit != clusters[i].indices.end(); pit++) // 创建一个迭代器pit以访问聚类的每一个点
cloud_cluster->points.push_back(boundary_cloud1->points[*pit]);
cloud_cluster->width = cloud_cluster->points.size();
cloud_cluster->height = 1;
// 后续处理
}
体素下采样(Voxel Downsampling)
原理
体素下采样(Voxel Downsampling)是一种常用的点云数据降采样方法,其原理基于将点云数据转换为体素网格,并在每个体素中保留一个代表性的点。以下是该方法的基本原理:
-
定义体素网格:将点云数据空间划分为立方体体素网格。每个体素是一个立方体区域,其大小由用户定义的参数决定。通常,体素的边长会根据点云密度和需要的下采样程度进行选择。
-
将点云数据映射到体素网格:对于每个体素,确定其包含的点云数据。这可以通过将点云中的每个点根据其位置映射到相应的体素中来实现。如果一个体素中包含多个点,则可以根据某种规则(如选择距离体素中心最近的点)来选择代表性点。
-
保留代表性点:在每个体素中保留一个代表性点,通常选择距离体素中心最近的点。这样可以有效地降低点云数据的密度,同时保留了重要的特征。
-
生成下采样后的点云:将所有体素中保留的代表性点组合起来,形成下采样后的点云数据。
通过体素下采样,可以有效地降低点云数据的密度,减少数据量,加快后续处理步骤的速度,并且在一定程度上保留了原始点云数据的特征。
体素下采样的执行时间比均匀采样的更快。
代码
void voxelDownSample(const PCLPointCloud::Ptr &cloud_in, float size_leaf, PCLPointCloud::Ptr &cloud_out)
{
pcl::VoxelGrid<PointXYZ> vg;
vg.setInputCloud(cloud_in);
vg.setLeafSize(size_leaf, size_leaf, size_leaf); // 下采样大小
vg.setDownsampleAllData(true);
vg.filter(*cloud_out);
}
使用示例
PCLPointCloud::Ptr cloud(new PCLPointCloud);
voxelDownSample(cloud, 1.5, cloud);
均匀采样(uniform Sampling)
原理
点云均匀采样(uniform Sampling)是一种常用的降采样方法,其原理是从原始点云中均匀地选取一部分点,以减少数据量但尽可能地保留原始点云的形状和结构特征。以下是该方法的基本原理:
-
确定采样密度:首先,根据需要的点云密度或者降采样比例,确定需要保留的点的数量。这个数量可以是根据经验确定的固定值,也可以根据点云的大小和复杂度来动态调整。
-
计算采样间隔:根据确定的采样密度和原始点云的大小,计算出采样间隔。采样间隔是指在每个维度上,相邻采样点之间的距离。
-
均匀采样:从原始点云中以计算的采样间隔为步长,均匀地选取点。可以在每个维度上依次遍历原始点云,并根据计算的采样间隔选取一个点,或者使用更高效的采样算法来实现均匀采样。
-
生成下采样后的点云:将选取的点组合起来,形成下采样后的点云数据。
通过点云的均匀采样,可以在减少数据量的同时,尽可能地保留原始点云的形状和结构特征。这种方法通常适用于需要在保留关键特征的同时降低点云密度的情况,比如在点云数据的预处理或者传输过程中。
代码
void uniformSampling(const PCLPointCloud::Ptr &cloud_in, float R, PCLPointCloud::Ptr &cloud_out)
{
pcl::UniformSampling<pcl::PointXYZ> form; // 创建均匀采样对象
form.setInputCloud(cloud_in); // 设置输入点云
form.setRadiusSearch(R); // 设置半径大小
form.filter(*cloud_out);
}
使用示例
PCLPointCloud::Ptr cloud(new PCLPointCloud);
uniformSampling(cloud, 1.5, cloud);
随机一致性采样分割(SACSegmentation)
SACSegmentation是一种常用的点云分割算法,它是基于RANSAC(Random Sample Consensus)的方法实现的。以下是SACSegmentation的基本原理:
-
随机采样:首先从原始点云中随机选择一定数量的点作为样本集。这些点被用来拟合一个模型,以估计点云中的结构或特征。
-
模型拟合:利用采样的点集拟合一个模型,这个模型通常可以用来描述点云中的某种几何结构,比如平面、圆柱体、球体等。在SACSegmentation中,可以根据需要选择不同的模型进行拟合。
-
内点检验:根据拟合的模型,计算每个点到模型的距离,并将距离小于某个阈值的点标记为内点。这些内点被认为是符合模型的点,可以被用来进一步优化模型。
-
模型评估:评估拟合的模型的质量,通常使用内点的数量或者内点与总点数之比来衡量模型的拟合程度。如果模型质量达到预先设定的阈值,则认为该模型是可接受的。
-
迭代优化:如果拟合的模型质量不满足要求,可以通过增加迭代次数,重新采样点集,并重新拟合模型来进行优化。
-
输出分割结果:最终输出符合条件的模型及其对应的内点,这些模型和内点可以被用来分割原始点云,将其划分为不同的部分或者提取感兴趣的特征。
通过SACSegmentation算法,可以有效地识别出点云中的特定几何结构,并将其分割成不同的部分,为后续的点云处理或者分析提供基础。
在PCL中,SACSegmentation支持多种模型的拟合,以适用于不同类型的点云数据。以下是一些常见的模型:
-
平面模型(SACMODEL_PLANE):拟合一个平面模型,用于描述点云中的平面结构,比如地面或墙壁。
-
直线模型(SACMODEL_LINE):拟合一条直线模型,通常用于描述点云中的线状结构,比如道路或管道。
-
圆柱体模型(SACMODEL_CYLINDER):拟合一个圆柱体模型,用于描述点云中的圆柱体结构,比如管道或柱状物体。
-
球体模型(SACMODEL_SPHERE):拟合一个球体模型,用于描述点云中的球体结构,比如球形物体或球形容器。
-
圆锥体模型(SACMODEL_CONE):拟合一个圆锥体模型,用于描述点云中的圆锥体结构,比如锥形物体或者漏斗。
-
特定方向的平面模型(SACMODEL_PARALLEL_PLANE):拟合一个特定方向的平面模型,用于描述点云中特定方向上的平面结构。
代码
void fitModel(const PCLPointCloud::Ptr &cloud_in, const int &modelType, double dist_threshold, bool save_outliers,
pcl::ModelCoefficients::Ptr &model_coefficients, PCLPointCloud::Ptr &cloud_out)
{
pcl::PointIndices::Ptr sacs_inliers(new pcl::PointIndices);
pcl::SACSegmentation<PointXYZ> sacs;
sacs.setOptimizeCoefficients(true);
sacs.setModelType(modelType);//设置模型类型
sacs.setMethodType(pcl::SAC_PROSAC);//使用SAC_PROSAC的随机采样方式,速度更快
sacs.setMaxIterations(1000);//最大迭代次数
sacs.setDistanceThreshold(dist_threshold);//距离阈值
sacs.setInputCloud(cloud_in);
sacs.segment(*sacs_inliers, *model_coefficients);//分割内点后,也保存模型的系数
//设置是保存内点还是外点
pcl::ExtractIndices<PointXYZ> ei;
ei.setInputCloud(cloud_in);
ei.setIndices(sacs_inliers);
ei.setNegative(save_outliers);
ei.filter(*cloud_out);
}
使用示例
PCLPointCloud::Ptr cloud(new PCLPointCloud);//输入的点云
pcl::ModelCoefficients::Ptr plane_coefficients(new pcl::ModelCoefficients);//模型系数
// 拟合平面
fitModel(cloud, pcl::SACMODEL_PLANE, 10.0f, false, plane_coefficients, cloud);
根据模型系数过滤模型
有时候,我们拟合模型时使用的距离系数可能太小,导致过滤后的点云数量不足。为了方便对过滤后的点云进行后续处理,我们可以在拟合模型得到模型系数之后,用一个较大的距离系数来进行模型过滤。
代码
void ModelFilter(const PCLPointCloud::Ptr &cloud_in, const pcl::SacModel modelType, double dist_threshold, bool save_outliers,
pcl::ModelCoefficients::Ptr &model_coefficients, PCLPointCloud::Ptr &cloud_out)
{
pcl::ModelOutlierRemoval<PCLPoint> filter; // 创建模型滤波器对象
filter.setModelCoefficients(*model_coefficients); // 为模型对象添加模型系数
filter.setThreshold(dist_threshold); // 设置判断是否为模型内点的阈值
filter.setModelType(modelType); // 设置模型类别
filter.setInputCloud(cloud_in); // 输入待滤波点云
filter.setNegative(save_outliers); // 默认false,提取模型内点;true,提取模型外点
filter.filter(*cloud_out); // 执行模型滤波,保存滤波结果于cloud_filtered
}
使用示例
PCLPointCloud::Ptr cloud(new PCLPointCloud);//输入的点云
PCLPointCloud::Ptr plane_cloud(new PCLPointCloud);//平面点云
pcl::ModelCoefficients::Ptr plane_coefficients(new pcl::ModelCoefficients);//模型系数
// 拟合平面
fitModel(cloud, pcl::SACMODEL_PLANE, 1.0f, false, plane_coefficients, plane_cloud);
//过滤平面
ModelFilter(cloud, pcl::SACMODEL_PLANE, 10.0f, false, plane_coefficients, plane_cloud);
点云投影
有时候我们也需要将点云投影到模型点云上,此时可以使用点云投影。
代码
void projectModel(const PCLPointCloud::Ptr &cloud_in, const int &modelType, const pcl::ModelCoefficients::Ptr &model_coefficients,
PCLPointCloud::Ptr &cloud_out)
{
pcl::ProjectInliers<PCLPoint> proj; // 创建投影滤波器对象
proj.setModelType(modelType); // 设置对象对应的投影模型
proj.setInputCloud(cloud_in); // 设置输入点云
proj.setModelCoefficients(model_coefficients); // 设置模型对应的系数
proj.filter(*cloud_out);
}
使用示例
PCLPointCloud::Ptr cloud(new PCLPointCloud);//输入的点云
PCLPointCloud::Ptr plane_cloud(new PCLPointCloud);//平面点云
pcl::ModelCoefficients::Ptr plane_coefficients(new pcl::ModelCoefficients);//模型系数
// 拟合平面
fitModel(cloud, pcl::SACMODEL_PLANE, 1.0f, false, plane_coefficients, plane_cloud);
//过滤平面
ModelFilter(cloud, pcl::SACMODEL_PLANE, 10.0f, false, plane_coefficients, plane_cloud);
//投影平面
projectModel(plane_cloud, pcl::SACMODEL_PLANE, plane_coefficients, plane_cloud);
边界估计(BoundaryEstimation)
PCL中的BoundaryEstimation是用于估计点云数据中点的边界性质的算法。它主要用于识别点云中的边界点,即位于物体表面上的点,以及点云的边界(即物体与背景之间的边界)。以下是其基本原理:
-
法向估计:首先,对点云中的每个点进行法向估计。法向估计是通过计算每个点周围的邻域点来估计该点的法向量,常用的方法包括最小二乘法、主成分分析(PCA)等。
-
曲率估计:基于估计得到的法向量,可以计算每个点的曲率。曲率是描述点云局部形状变化程度的一个度量,通常与法向量一起用来判断点是否位于边界上。
-
边界点识别:根据法向量和曲率信息,识别点云中的边界点。边界点通常具有以下特征:
- 法向量方向与表面法线方向的夹角较大;
- 曲率较大,表明点云在该位置存在明显的形状变化;
- 距离点云边界较近。
-
边界的提取:通过识别出的边界点,可以进一步提取点云的边界。这可以通过将边界点连接起来或者通过一些特定的边界提取算法来实现。
通过BoundaryEstimation,可以有效地识别出点云中的边界性质,这对于点云的分割、物体识别和场景理解等任务都是非常有帮助的。
代码
void calBoundaries(const PCLPointCloud::Ptr &cloud_in, int K, float angle_thred, PCLPointCloud::Ptr &boundary_cloud)
{
pcl::PointCloud<pcl::Normal>::Ptr normals_1(new pcl::PointCloud<pcl::Normal>);
pcl::PointCloud<pcl::Boundary>::Ptr boundaries_1(new pcl::PointCloud<pcl::Boundary>);
PCLPointCloud::Ptr cloud(new PCLPointCloud);
// 计算法向量
calNormals(cloud_in, 30, 0.01, 'K', normals_1);
boundaries_1->resize(cloud_in->size()); // 初始化大小
pcl::BoundaryEstimation<PCLPoint, pcl::Normal, pcl::Boundary> boundary_estimation; // 声明一个BoundaryEstimation类
boundary_estimation.setInputCloud(cloud_in); // 设置输入点云
boundary_estimation.setInputNormals(normals_1); // 设置输入法线
pcl::search::KdTree<PCLPoint>::Ptr kdtree_ptr(new pcl::search::KdTree<PCLPoint>);
boundary_estimation.setSearchMethod(kdtree_ptr); // 设置搜寻k近邻的方式
boundary_estimation.setKSearch(K); // 设置k近邻数量
boundary_estimation.setAngleThreshold(M_PI * angle_thred); // 设置角度阈值,大于阈值为边界
boundary_estimation.compute(*boundaries_1); // 计算点云边界,结果保存在boundaries中
// 将结果转为pointxyz
for (size_t i = 0; i < cloud_in->points.size(); i++)
{
if (boundaries_1->points[i].boundary_point != 0)
{
cloud->points.push_back(cloud_in->points[i]);
}
}
cloud->height = 1;
cloud->width = cloud->points.size();
pcl::copyPointCloud(*cloud, *boundary_cloud);
}
使用示例
PCLPointCloud::Ptr cloud(new PCLPointCloud);//输入的点云
PCLPointCloud::Ptr boundary_cloud(new PCLPointCloud);//边界点云
calBoundaries(plane_cloud, 40.0, 0.6, boundary_cloud);
直通滤波(passthrough filter)
直通滤波(PassThrough Filtering)是一种在点云处理中常用的滤波方法,用于过滤掉点云中不在指定范围内的点。其原理是根据给定的轴和范围,保留点云中在指定范围内的点,而将超出范围的点过滤掉。
代码
void passthrough(const PCLPointCloud::Ptr &cloud_in, std::vector<float> limit, PCLPointCloud::Ptr &cloud_out)
{
pcl::PassThrough<PointXYZ> pt;
PCLPointCloud::Ptr cloud_ptz_ptr(new PCLPointCloud);
pt.setInputCloud(cloud_in);
pt.setFilterFieldName("x");
pt.setFilterLimits(limit[0], limit[1]);
pt.filter(*cloud_ptz_ptr);
PCLPointCloud::Ptr cloud_ptx_ptr(new PCLPointCloud);
pt.setInputCloud(cloud_ptz_ptr);
pt.setFilterFieldName("y");
pt.setFilterLimits(limit[2], limit[3]);
pt.filter(*cloud_ptx_ptr);
pt.setInputCloud(cloud_ptx_ptr);
pt.setFilterFieldName("z");
pt.setFilterLimits(limit[4], limit[5]);
pt.filter(*cloud_out);
}
使用示例
float x_min = 0.00;
float x_max = 5.0;
float y_min = 0.00;
float y_max = 5.0;
float z_min = 0.00;
float z_max = 5.0;
PCLPointCloud::Ptr cloud(new PCLPointCloud);//输入的点云
std::vector<float> limit{x_min, x_max, y_min, y_max, z_min, z_max,};
passthrough(cloud, limit, cloud);
法向量估计(NormalEstimationOMP)
NormalEstimation是一种用于估计点云数据中每个点的法向量的算法。法向量是描述点云表面几何形状的重要属性,对于后续的点云处理和分析非常重要。以下是NormalEstimation的基本原理:
-
确定邻域:对于每个点,NormalEstimation首先确定其周围的邻域。通常采用的方式是以该点为中心,选取一定半径内的所有点作为邻域。这个半径可以是固定的,也可以根据点云的密度和特征来自适应地调整。
-
法向量估计:对于每个点的邻域,利用邻域内的点来估计该点的法向量。常用的估计方法包括最小二乘法(Least Squares)、主成分分析(PCA)等。最小二乘法是一种常用的方法,通过拟合邻域内点的最佳平面来估计法向量,使得拟合平面与邻域内点的平均方差最小。
-
法向量归一化:得到法向量后,通常需要对其进行归一化,使其长度为1。这样可以确保法向量的方向信息不受缩放的影响,更好地描述点云表面的几何形状。
-
输出法向量:对于每个点,NormalEstimation输出其估计得到的法向量。
NormalEstimation的输出结果可以用于后续的点云处理和分析,比如点云分割、曲面重建、特征提取等。法向量可以提供关于点云表面局部形状的重要信息,对于理解点云数据的几何结构和进行相应的处理具有重要作用。
代码
void calNormals(const PCLPointCloud::Ptr &cloud_in, int k, float r, char mode, pcl::PointCloud<pcl::Normal>::Ptr &normal_out)
{
pcl::NormalEstimationOMP<PointXYZ, pcl::Normal> ne;
pcl::search::KdTree<PointXYZ>::Ptr tree(new pcl::search::KdTree<PointXYZ>);
// Calculate all the normals of the entire surface
ne.setInputCloud(cloud_in);
ne.setNumberOfThreads(8);
ne.setSearchMethod(tree);
if (mode == 'K')//K近邻模式
{
ne.setKSearch(k);
ne.compute(*normal_out);
}
else if (mode == 'R')//半径模式
{
ne.setRadiusSearch(r);
ne.compute(*normal_out);
}
}
使用示例
pcl::PointCloud<pcl::Normal>::Ptr normals (new pcl::PointCloud<pcl::Normal>);//法向量
PCLPointCloud::Ptr cloud(new PCLPointCloud);//输入的点云
calNormals(cloud, 30, 0.01, 'K', normals);
radiusOutlierRemove
RadiusOutlierRemoval是一种常用的点云滤波方法,用于去除点云数据中的离群点。其原理是基于点云中每个点的邻域信息,通过设定一个半径,将每个点周围半径范围内的点数与预先设定的阈值进行比较,从而判断该点是否为离群点。以下是RadiusOutlierRemoval的基本原理:
-
指定半径和邻域点数量阈值:首先需要指定一个半径大小和一个邻域点数量阈值。半径决定了每个点的邻域范围,而邻域点数量阈值则用于判断一个点是否为离群点。
-
计算邻域点数量:对于点云中的每个点,计算其在指定半径范围内的邻域点数量。
-
判断离群点:将每个点的邻域点数量与设定的阈值进行比较。如果邻域点数量小于阈值,则将该点标记为离群点。
-
生成过滤后的点云:将除离群点以外的点组合起来,形成过滤后的点云数据。
通过RadiusOutlierRemoval,可以有效地去除点云数据中的离群点,提高点云数据的质量和准确性。这种方法通常用于点云数据的预处理阶段,以提高后续处理步骤的效率和准确性。
代码
void radiusOutlierRemove(const PCLPointCloud::Ptr &cloud_in, float radius, int min_neighbor, PCLPointCloud::Ptr &cloud_out)
{
// Remove isolated points
pcl::RadiusOutlierRemoval<PointXYZ> ror;
ror.setInputCloud(cloud_in);
ror.setRadiusSearch(radius);
ror.setMinNeighborsInRadius(min_neighbor);
ror.filter(*cloud_out);
}
使用示例
PCLPointCloud::Ptr cloud(new PCLPointCloud);//输入的点云
radiusOutlierRemove(cloud, 0.5, 10, cloud);
区域生长分割(growingSegmentation)
Region Growing(区域生长)是一种用于分割点云数据的算法,其原理是从种子点开始,根据一定的生长准则逐步扩展区域,将与种子点相似的邻域点添加到同一簇中。以下是Region Growing的基本原理:
-
选择种子点:从点云数据中选择一个种子点作为初始种子。种子点通常是根据一定的条件选择的,比如高曲率或者颜色差异较大的点。
-
生长准则:定义生长准则,根据该准则确定哪些邻域点可以加入到当前生长的区域中。常见的生长准则包括:
- 邻域点与当前区域中的点之间的相似性:可以根据点之间的距离、法向量的一致性、颜色的相似性等来判断。
- 邻域点的局部曲率:对于曲率较小的点,可能更容易加入当前区域。
-
生长过程:从种子点开始,根据生长准则逐步扩展区域,将满足条件的邻域点添加到当前生长的区域中。这个过程可以是递归的,即在每次生长的过程中继续考虑已经加入区域的点的邻域。
-
停止条件:定义生长过程的停止条件,通常包括生长到达边界、区域内点数量达到一定阈值或者生长过程不再满足生长准则等。
-
分割结果:将所有被加入到区域中的点组合起来,形成分割后的点云簇。
通过Region Growing,可以将点云数据分割成具有连续性和相似性的区域,从而方便后续的点云分析和处理。这种方法通常用于识别点云中的物体、表面、或者其他具有连续性的结构。
代码
void growingSegmentation(const PCLPointCloud::Ptr &cloud_in, float smoothThred, float curveThred, PCLPointCloud::Ptr &cloud_out)
{
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>); // 法线特征
calNormals(cloud_in, 30, 0.04, 'K', cloud_normals);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;
reg.setMinClusterSize(50);
reg.setMaxClusterSize(1000000);
reg.setSearchMethod(tree);
reg.setNumberOfNeighbours(10);
reg.setInputCloud(cloud_in);
// reg.setIndices (indices);
reg.setInputNormals(cloud_normals);
reg.setSmoothnessThreshold(smoothThred / 180.0 * M_PI);
reg.setCurvatureThreshold(curveThred);
std::vector<pcl::PointIndices> clusters;
reg.extract(clusters);
sort(clusters.begin(), clusters.end(), compareCloud);
std::cout << "Number of clusters: " << clusters.size() << std::endl;
std::cout << "First cluster has " << clusters[0].indices.size() << " points." << std::endl;
PCLPointCloud::Ptr cloud_cluster(new PCLPointCloud);
for (std::vector<int>::const_iterator pit = clusters[0].indices.begin(); pit != clusters[0].indices.end(); pit++)
cloud_cluster->points.push_back(cloud_in->points[*pit]);
pcl::copyPointCloud(*cloud_cluster, *cloud_out);
}
使用示例
PCLPointCloud::Ptr cloud(new PCLPointCloud);//输入的点云
growingSegmentation(cloud,15.0,10.0,cloud);