PCL:RANSAC圆柱体拟合(详细注释,对新手友好!)
Posted 借我十斤肉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PCL:RANSAC圆柱体拟合(详细注释,对新手友好!)相关的知识,希望对你有一定的参考价值。
1 RANSAC随机采样一致性算法介绍
RANSAC是一种随机参数估计方法。RANSAC从样本中随机抽选出一个样本子集,使用最小方差估计算法对这个子集计算模型参数,然后计算所有样本与该模型的偏差,再使用一个预先设定好的阈值与偏差比较,当偏差小于阈值时,该样本点属于模型内样本点(inliers),简称内点,否则为模型外样本点(outliers),简称外点。记录下当前内点inliers的个数,然后重复这一过程。每一次重复,都记录当前最佳的模型参数,即inliers的个数最多,此时对应inliers个数为best_ninliers。每次迭代的末尾,都会根据期望的误差率、best_ninliers、总样本个数、当前迭代次数,计算一个迭代结束评判因子。迭代结束后,最佳模型参数就是最终的模型参数估计值。
2 RANSAC圆柱体拟合
2.1 SACMODEL_CYLINDER模型
PCL提供了SACMODEL_CYLINDER模型,定义为圆柱体模型,共设置7个参数,从点云中分割提取的内点都处在估计参数对应的圆柱体上或距离圆柱体的距离在一定范围内。
参数名 | 参数意义 |
---|---|
point_on_axis.x | 轴线上一点的X坐标 |
point_on_axis.y | 轴线上一点的Y坐标 |
point_on_axis.z | 轴线上一点的Z坐标 |
axis_direction.x | 轴线方向向量的X分量 |
axis_direction.y | 轴线方向向量的Y分量 |
axis_direction.z | 轴线方向向量的Z分量 |
radius | 圆柱体半径 |
2.2 代码实现
RANSAC圆柱体拟合所需关键头文件
#include <pcl/filters/extract_indices.h>
#include <pcl/features/normal_3d.h>
#include <pcl/segmentation/sac_segmentation.h>
RANSAC圆柱体拟合完整代码:
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h> // 法向量估计
#include <pcl/segmentation/sac_segmentation.h> // 模型分割
#include <pcl/filters/extract_indices.h> // 索引提取
#include <pcl/visualization/cloud_viewer.h> // 可视化
using namespace std;
typedef pcl::PointXYZ PointT;
int main()
{ //---------------------------
//----------加载点云----------
//---------------------------
pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
pcl::PCDReader reader;
reader.read("1.pcd", *cloud);
cout << "\\t\\t-----加载点云详情-----" << endl;
cout << *cloud << endl;
//---------------------------
//----------法线估计----------
//---------------------------
cout << "->正在计算法线..." << endl;
pcl::NormalEstimation<PointT, pcl::Normal> ne; // 创建法向量估计对象
pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>());
ne.setSearchMethod(tree); // 设置搜索方式
ne.setInputCloud(cloud); // 设置输入点云
ne.setKSearch(50); // 设置K近邻搜索点的个数
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
ne.compute(*cloud_normals); // 计算法向量,并将结果保存到cloud_normals中
//-----------------------------
//----------圆柱体分割----------
//-----------------------------
cout << "->正在圆柱体分割..." << endl;
pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg; // 创建圆柱体分割对象
seg.setInputCloud(cloud); // 设置输入点云:待分割点云
seg.setOptimizeCoefficients(true); // 设置对估计的模型系数需要进行优化
seg.setModelType(pcl::SACMODEL_CYLINDER); // 设置分割模型为圆柱体模型
seg.setMethodType(pcl::SAC_RANSAC); // 设置采用RANSAC算法进行参数估计
seg.setNormalDistanceWeight(0.1); // 设置表面法线权重系数
seg.setMaxIterations(10000); // 设置迭代的最大次数
seg.setDistanceThreshold(0.05); // 设置内点到模型距离的最大值
seg.setRadiusLimits(3.0, 4.0); // 设置圆柱模型半径的范围
seg.setInputNormals(cloud_normals); // 设置输入法向量
pcl::PointIndices::Ptr inliers_cylinder(new pcl::PointIndices); // 保存分割结果
pcl::ModelCoefficients::Ptr coefficients_cylinder(new pcl::ModelCoefficients); // 保存圆柱体模型系数
seg.segment(*inliers_cylinder, *coefficients_cylinder); // 执行分割,将分割结果的索引保存到inliers_cylinder中,同时存储模型系数coefficients_cylinder
cout << "\\n\\t\\t-----圆柱体系数-----" << endl;
cout<<"轴线一点坐标:("<< coefficients_cylinder->values[0]<<", "
<< coefficients_cylinder->values[1]<<", "
<< coefficients_cylinder->values[2]<<")"
<<endl;
cout << "轴线方向向量:(" << coefficients_cylinder->values[3] << ", "
<< coefficients_cylinder->values[4] << ", "
<< coefficients_cylinder->values[5] << ")"
<< endl;
cout << "圆柱体半径:" << coefficients_cylinder->values[6] << endl;
//-------------------------------
//----------提取分割结果----------
//-------------------------------
cout << "->正在提取分割结果..." << endl;
pcl::ExtractIndices<PointT> extract; // 创建索引提取点对象
extract.setInputCloud(cloud); // 设置输入点云:待分割点云
extract.setIndices(inliers_cylinder); // 设置内点索引
extract.setNegative(false); // 默认false,提取圆柱体内点;true,提取圆柱体外点
pcl::PointCloud<PointT>::Ptr cloud_cylinder(new pcl::PointCloud<PointT>());
extract.filter(*cloud_cylinder); // 执行滤波,并将结果点云保存到cloud_cylinder中
//-------------------------------
//----------保存分割结果----------
//-------------------------------
if (!cloud_cylinder->points.empty())
{
pcl::PCDWriter writer;
writer.write("cylinder.pcd", *cloud_cylinder, true);
cout << "->圆柱体模型点云个数:" << cloud_cylinder->size() << endl;
}
else
{
PCL_ERROR("未提取出圆柱体模型点!\\a\\n");
}
//--------------------------------------------
//----------可视化分割结果(可选操作)----------
//--------------------------------------------
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("可视化分割结果"));
///视口1:原始点云
int v1;
viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1); //设置第一个视口在X轴、Y轴的最小值、最大值,取值在0-1之间
viewer->setBackgroundColor(0, 0, 0, v1);
viewer->addText("original_point_cloud", 10, 10, "v1 text", v1);
viewer->addPointCloud<PointT>(cloud, "original_point_cloud", v1);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "original_point_cloud", v1);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "original_point_cloud", v1);
///视口2:分割后点云
int v2;
viewer->createViewPort(0.5, 0.0, 1.0, 1.0, v2);
viewer->setBackgroundColor(0.3, 0.3, 0.3, v2);
viewer->addText("segment_point_cloud", 10, 10, "v2 text", v2);
pcl::visualization::PointCloudColorHandlerCustom<PointT> set_color(cloud_cylinder, 0, 255, 0);
viewer->addPointCloud<PointT>(cloud_cylinder, set_color, "segment_point_cloud", v2);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "segment_point_cloud", v2);
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}
输出结果:
-----加载点云详情-----
points[]: 64482
width: 64482
height: 1
is_dense: 1
sensor origin (xyz): [0, 0, 0] / orientation (xyzw): [0, 0, 0, 1]
->正在计算法线...
->正在圆柱体分割...
-----圆柱体系数-----
轴线一点坐标:(48.8421, 17.7188, 6.55213)
轴线方向向量:(0.0060639, 0.00549096, -0.999967)
圆柱体半径:3.366
->正在提取分割结果...
->圆柱体模型点云个数:46017
可视化结果:
3 相关链接
4 测试数据下载
提取码:pdx6
以上是关于PCL:RANSAC圆柱体拟合(详细注释,对新手友好!)的主要内容,如果未能解决你的问题,请参考以下文章