一、介绍
SACSegmentation(Sample Consensus Segmentation)是PCL中的一个分割算法,用于从点云中识别出具有相同几何形状的模型。该算法使用采样一致性(Sample Consensus)方法,通过迭代地随机采样一组数据点,并拟合模型,然后评估拟合模型与数据点的一致性,最终找到最佳的模型参数。
SACSegmentation算法的基本原理如下:
-
随机采样:从输入的点云中随机选择一组数据点作为采样集。
-
模型拟合:根据采样集,使用最小二乘或其他方法拟合一个几何模型(如平面、球体、圆柱体等)。
-
评估一致性:计算所有点与拟合模型之间的距离,并将距离小于阈值的点划分为内点(inliers),距离大于阈值的点划分为外点(outliers)。
-
重复迭代:重复执行步骤1至3,直到达到预设的迭代次数或满足停止条件。
-
选择最佳模型:根据内点的数量或其他评估指标选择最佳的模型作为分割结果。
SACSegmentation算法具有很好的鲁棒性和通用性,可以用于分割各种几何形状的模型。通过调整阈值和迭代次数等参数,可以控制算法的精度和速度。
在PCL中,通过设置SACSegmentation对象的参数,如模型类型、方法类型、距离阈值等,以及输入点云数据,调用segment()方法即可执行分割算法,并获得分割结果,包括模型系数和内点索引等。
总而言之,SACSegmentation算法是一种常用的基于采样一致性的点云分割算法,可用于识别点云中的几何模型,为后续的点云处理和分析提供了基础。
二、代码
#include <iostream>
#include <chrono>
#include <pcl/ModelCoefficients.h> // 模型系数的定义
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h> // 各种点云数据类型
#include <pcl/sample_consensus/method_types.h> // 包含用于采样一致性算法的不同方法的定义,如RANSAC、MSAC等
#include <pcl/sample_consensus/model_types.h> // 包含用于采样一致性算法的不同模型的定义,如平面、球体、圆柱体
#include <pcl/segmentation/sac_segmentation.h> // 包含用于分割点云的采样一致性算法(SACSegmentation)的定义,用于识别点云的几何模型
#include <pcl/filters/extract_indices.h> // 包含用于从点云中提取特定索引的函数和类,用于根据索引提取点云中的子集
#include <pcl/visualization/pcl_visualizer.h> // 包含了用于可视化点云的函数和类,用于在3D视窗中现实点云数据
int main(){
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// Fill the cloud data
cloud->width = 100000;
cloud->height = 1;
cloud->points.resize(cloud->width * cloud->height);
// generate the data
for(auto& point: *cloud)
{
point.x = 1024 * rand() / (RAND_MAX + 1.0f);
point.y = 1024 * rand() / (RAND_MAX + 1.0f);
point.z = 1.0;
}
// Set a few outlierss
for(int i=0; i< 1000; i++)
{
(*cloud)[i].z = 50 * rand() / (RAND_MAX + 1.0f);
}
std::cerr << "Point cloud data:" << cloud->size() << " points" << std::endl;
for (const auto& point: *cloud)
std::cerr << " " << point.x << " "
<< point.y << " "
<< point.z << std::endl;
pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
// Create the segmentation object
pcl::SACSegmentation<pcl::PointXYZ> seg;
auto start = std::chrono::high_resolution_clock::now();
// Optional
seg.setOptimizeCoefficients(true);
// Mandatory
seg.setModelType(pcl::SACMODEL_PLANE);
seg.setMethodType(pcl::SAC_RANSAC);
seg.setDistanceThreshold(0.01);
seg.setInputCloud(cloud);
seg.segment(*inliers, *coefficients);
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> duration = end - start;
std::printf("segment cost time:%.2f ms\n", duration.count());
if(inliers->indices.size() ==0)
{
PCL_ERROR("Cloud not estimate a planar model for the given dataset.\n");
return(-1);
}
// std::cerr << "Model coefficients: " << coefficients->values[0] << " "
// << coefficients->values[1] << " "
// << coefficients->values[2] << " "
// << coefficients->values[3] << std::endl;
// std::cerr << "Model inliers: " << inliers->indices.size() << std::endl;
// for (const auto& idx: inliers->indices)
// std::cerr << idx << " " << cloud->points[idx].x << " "
// << cloud->points[idx].y << " "
// << cloud->points[idx].z << std::endl;
// extract inliers for visualization
pcl::PointCloud<pcl::PointXYZ>::Ptr inlierCloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::ExtractIndices<pcl::PointXYZ> extract;
extract.setInputCloud(cloud);
extract.setIndices(inliers);
extract.setNegative(false); // 提取inliers
extract.filter(*inlierCloud);
// visualization
pcl::visualization::PCLVisualizer vis("cloud visualization");
int v0(0);
vis.createViewPort(0.0, 0.0, 0.5, 1.0, v0);
vis.addCoordinateSystem(0.5,"reference0", v0);
vis.setBackgroundColor(0.05, 0.05, 0.05, v0);
vis.addPointCloud(cloud, "cloud0", v0);
vis.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 4.0, "cloud0", v0);
int v1(1);
vis.createViewPort(0.5, 0, 1, 1, v1);
vis.addCoordinateSystem(0.5, "reference1", v1);
vis.setBackgroundColor(0,0,0,v1);
vis.addPointCloud(inlierCloud, "cloud1", v1);
vis.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 4.0, "cloud1", v1);
while(!vis.wasStopped())
{
vis.spinOnce(100);
}
return(0);
}