随机采样一致性算法RANSAC

程序实例参考网址:
https://pcl.readthedocs.io/projects/tutorials/en/latest/random_sample_consensus.html?highlight=console#the-explanation
PCLAPI文档:
https://pointclouds.org/documentation/
应用主要是:
1、对点云进行分割,根据设定不同的几何模型(PCL中主要支持的模型有:空间平面、直线、二维或三维圆周、圆球、锥体),估计对应的几何模型参数,在一定允许误差范围内分割出在模型上的点云。
2、点云的配准对的剔除,见点云配准内容

简介

从样本中随即抽选一个样本子集——最小方差估计算法对这个子集计算模型参数——计算所有样本与该模型的偏差——使用一个设定好的阈值与偏差比较——偏差<阈值,样本点属于模型内样本点(inliers);偏差>阈值,模型外样本点(outliers)——记录下当前inliers的个数,重复这一过程,记录当前最佳模型参数(即inliers个数最多,对应的inliers称为best_ninliers)——每次迭代后计算:期望的误差率、best_ninliers、总样本个数、当前迭代次数,——计算迭代结束评判因子,决定是否结束迭代——结束迭代,最佳模型参数为最终模型参数的估计值
问题:
1、预先设定的阈值,模型如果抽象不好设定
2、RANSAC的迭代次数未知

RANSAC优缺点

RANSAC 的一个优点是它能够对模型参数进行鲁棒估计,即,即使数据集中存在大量异常值,它也可以高度准确地估计参数。 RANSAC 的一个缺点是计算这些参数所需的时间没有上限。当计算的迭代次数有限时,获得的解可能不是最佳的,甚至可能不是很好地拟合数据的解。通过这种方式,RANSAC 提供了一种权衡;通过计算更多的迭代次数,可以增加生成合理模型的概率。 RANSAC 的另一个缺点是它需要设置特定于问题的阈值。

RANSAC 只能为特定数据集估计一个模型。对于存在两个(或更多)模型时的任何一种模型方法,RANSAC 可能无法找到任何一个。

PCL中RANSAC算法实现类

class pcl::RandomSampleConsensus

应用实例

学习如何用RandomSampleConsensus类获得点云的拟合平面模型

初始化点云

  // initialize PointCloudspcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);//存储源点云pcl::PointCloud<pcl::PointXYZ>::Ptr final (new pcl::PointCloud<pcl::PointXYZ>);//存储提取的局内点inliers//填充点云数据,作为处理前的原始点云cloud->width    = 5000;//设置点云数目cloud->height   = 1;//设置为无序点云cloud->is_dense = false;cloud->points.resize (cloud->width * cloud->height);for (size_t i = 0; i < cloud->points.size (); ++i){if (pcl::console::find_argument (argc, argv, "-s") >= 0 || pcl::console::find_argument (argc, argv, "-sf") >= 0)//如果命令行参数为-s或者-sf,则用x^2+y^2+z^2=1设置一部分点云数据{cloud->points[i].x =  rand () / (RAND_MAX + 1.0);cloud->points[i].y =  rand () / (RAND_MAX + 1.0);if (i % 5 == 0)//1/5的点云数据被随机放置作为局外点cloud->points[i].z =  rand () / (RAND_MAX + 1.0);else if(i % 2 == 0)//另外4/5的点云数据z=+或-(1-x^2-y^2)开根号cloud->points[i].z =  sqrt( 1 - (cloud->points[i].x * cloud->points[i].x)- (cloud->points[i].y * cloud->points[i].y));elsecloud->points[i].z =  - sqrt( 1 - (cloud->points[i].x * cloud->points[i].x)- (cloud->points[i].y * cloud->points[i].y));}else//命令行参数未指定,则用x+y+z=1设置一部分点云数据,点云组成的菱形平面作为内点{cloud->points[i].x =  rand () / (RAND_MAX + 1.0);cloud->points[i].y =  rand () / (RAND_MAX + 1.0);if( i % 5 == 0)cloud->points[i].z = rand () / (RAND_MAX + 1.0);//此处点为局外点elsecloud->points[i].z = -1 * (cloud->points[i].x + cloud->points[i].y);//z=-(x+y)此处为局内点}}

创建 RandomSampleConsensus 对象并计算合适的模型

 std::vector<int> inliers;//存储局内点集合的点的索引向量// created RandomSampleConsensus object and compute the appropriated modelpcl::SampleConsensusModelSphere<pcl::PointXYZ>::Ptrmodel_s(new pcl::SampleConsensusModelSphere<pcl::PointXYZ> (cloud));//针对球模型的对象pcl::SampleConsensusModelPlane<pcl::PointXYZ>::Ptrmodel_p (new pcl::SampleConsensusModelPlane<pcl::PointXYZ> (cloud));//针对平面模型的对象if(pcl::console::find_argument (argc, argv, "-f") >= 0)//命令行输入的参数为-f时,随机估算对应的平面模型,并存储估计的局内点{pcl::RandomSampleConsensus<pcl::PointXYZ> ransac (model_p);//RANSAC,对象为平面ransac.setDistanceThreshold (.01);//设置与平面距离小于0.01的点作为局内点考虑ransac.computeModel();//执行随机参数估计ransac.getInliers(inliers);//存储估计所得的局内点}else if (pcl::console::find_argument (argc, argv, "-sf") >= 0 )//命令行输入的参数为-sf时,随机估算对应的球面模型,并存储估计的局内点{pcl::RandomSampleConsensus<pcl::PointXYZ> ransac (model_s);//RANSAC,对象为球面ransac.setDistanceThreshold (.01);//与球面距离小于0.01的点作为局内点考虑ransac.computeModel();//执行随机参数估计ransac.getInliers(inliers);//存储估计所得的局内点}

最后,将局内点复制到final点云中,在三维窗口中显示原始点云或者估计得到的局内点组成的点云

 pcl::copyPointCloud<pcl::PointXYZ>(*cloud, inliers, *final);// creates the visualization object and adds either our orignial cloud or all of the inliers// depending on the command line arguments specified.boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer;if (pcl::console::find_argument (argc, argv, "-f") >= 0 || pcl::console::find_argument (argc, argv, "-sf") >= 0)viewer = simpleVis(final);//命令行参数为-f或者-sf时,显示最终结果elseviewer = simpleVis(cloud);//显示原始点云

segmentation

segmentation平面分割

使用到的头文件

#include <pcl/sample_consensus/method_types.h>//随机参数估计方法头文件
#include <pcl/sample_consensus/model_types.h>//模型定义头文件
#include <pcl/segmentation/sac_segmentation.h>//基于采样一致性分割的类的头文件

预先准备数据

     pcl::PointCloud<pcl::PointXYZ> cloud;
//填充点云数据cloud.width  = 15;cloud.height = 1;cloud.points.resize (cloud.width * cloud.height);
//生成数据
for (size_t i = 0; i < cloud.points.size (); ++i){cloud.points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);cloud.points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);cloud.points[i].z = 1.0;}
//设置几个局外点
cloud.points[0].z = 2.0;
cloud.points[3].z = -2.0;
cloud.points[6].z = 4.0;
//打印点云坐标值std::cerr << "Point cloud data: " << cloud.points.size () <<" points" << std::endl;
for (size_t i = 0; i < cloud.points.size (); ++i)std::cerr << "    " << cloud.points[i].x << " "
<< cloud.points[i].y << " "
<< cloud.points[i].z << std::endl;

分割

//创建分割时所需要的模型系数对象coefficients及存储内点的点索引集合对象inliers
pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
//创建分割对象pcl::SACSegmentation<pcl::PointXYZ> seg;//可选设置,设置模型系数需要优化seg.setOptimizeCoefficients (true);
//必须设置seg.setModelType (pcl::SACMODEL_PLANE);//设置分割的模型类型为平面seg.setMethodType (pcl::SAC_RANSAC);//所用参数估计方法为RANSACseg.setDistanceThreshold (0.01);//设置距离阈值小于0.01m为局内点seg.setInputCloud (cloud.makeShared ());//输入原始点云
//引发分割实现,并存储分割结果到点集合inliers及存储平面模型系数seg.segment (*inliers, *coefficients);
if (inliers->indices.size () == 0){PCL_ERROR ("Could not estimate a planar model for the given dataset.");
return (-1);}//打印平面模型参数ax+by+cz+d=0形式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 (size_t i = 0; i < inliers->indices.size (); ++i)std::cerr << inliers->indices[i] << "    " <<cloud.points[inliers->indices[i]].x << " "
<<cloud.points[inliers->indices[i]].y << " "
<<cloud.points[inliers->indices[i]].z << std::endl;
return (0);

segmentation 圆柱体分割

任务:
1、估计每个点的表面法线
2、分割出平面模型,存储
3、分割出圆柱体模型,存储

  // All the objects neededpcl::PCDReader reader;//PCD文件读取对象pcl::PassThrough<PointT> pass;//直通滤波对象pcl::NormalEstimation<PointT, pcl::Normal> ne;//法线估计对象pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg;//分割对象 pcl::PCDWriter writer;//PCD文件写入对象pcl::ExtractIndices<PointT> extract;//点提取对象pcl::ExtractIndices<pcl::Normal> extract_normals;pcl::search::KdTree<PointT>::Ptr tree (new pcl::search::KdTree<PointT> ());// Datasetspcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);pcl::PointCloud<PointT>::Ptr cloud_filtered (new pcl::PointCloud<PointT>);pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);pcl::PointCloud<PointT>::Ptr cloud_filtered2 (new pcl::PointCloud<PointT>);pcl::PointCloud<pcl::Normal>::Ptr cloud_normals2 (new pcl::PointCloud<pcl::Normal>);pcl::ModelCoefficients::Ptr coefficients_plane (new pcl::ModelCoefficients), coefficients_cylinder (new pcl::ModelCoefficients);pcl::PointIndices::Ptr inliers_plane (new pcl::PointIndices), inliers_cylinder (new pcl::PointIndices);// Read in the cloud datareader.read ("table_scene_mug_stereo_textured.pcd", *cloud);std::cerr << "PointCloud has: " << cloud->points.size () << " data points." << std::endl;// Build a passthrough filter to remove spurious NaNspass.setInputCloud (cloud);pass.setFilterFieldName ("z");pass.setFilterLimits (0, 1.5);pass.filter (*cloud_filtered);std::cerr << "PointCloud after filtering has: " << cloud_filtered->points.size () << " data points." << std::endl;
  // Estimate point normalsne.setSearchMethod (tree);ne.setInputCloud (cloud_filtered);ne.setKSearch (50);ne.compute (*cloud_normals);// Create the segmentation object for the planar model and set all the parametersseg.setOptimizeCoefficients (true);seg.setModelType (pcl::SACMODEL_NORMAL_PLANE);//设置分割模型为平面seg.setNormalDistanceWeight (0.1);seg.setMethodType (pcl::SAC_RANSAC);seg.setMaxIterations (100);seg.setDistanceThreshold (0.03);seg.setInputCloud (cloud_filtered);seg.setInputNormals (cloud_normals);// Obtain the plane inliers and coefficientsseg.segment (*inliers_plane, *coefficients_plane);std::cerr << "Plane coefficients: " << *coefficients_plane << std::endl;

抽取分割点云并保存

  // Extract the planar inliers from the input cloudextract.setInputCloud (cloud_filtered);extract.setIndices (inliers_plane);extract.setNegative (false);// Write the planar inliers to diskpcl::PointCloud<PointT>::Ptr cloud_plane (new pcl::PointCloud<PointT> ());extract.filter (*cloud_plane);std::cerr << "PointCloud representing the planar component: " << cloud_plane->points.size () << " data points." << std::endl;writer.write ("table_scene_mug_stereo_textured_plane.pcd", *cloud_plane, false);// Remove the planar inliers, extract the restextract.setNegative (true);extract.filter (*cloud_filtered2);extract_normals.setNegative (true);extract_normals.setInputCloud (cloud_normals);extract_normals.setIndices (inliers_plane);extract_normals.filter (*cloud_normals2);

ExtractIndices基于某一分割算法提取点云中的一个子集

class pcl::ExtractIndices< PointT >
例程详见:
https://pointclouds.org/documentation/classpcl_1_1_extract_indices.html#details

pcl::ExtractIndices<PointType> eifilter (true); // Initializing with true will allow us to extract the removed indices
eifilter.setInputCloud (cloud_in);
eifilter.setIndices (indices_in);
eifilter.filter (*cloud_out);
// The resulting cloud_out contains all points of cloud_in that are indexed by indices_in
indices_rem = eifilter.getRemovedIndices ();
// The indices_rem array indexes all points of cloud_in that are not indexed by indices_in
eifilter.setNegative (true);
eifilter.filter (*indices_out);
// Alternatively: the indices_out array is identical to indices_rem
eifilter.setNegative (false);
eifilter.setUserFilterValue (1337.0);
eifilter.filterDirectly (cloud_in);
// This will directly modify cloud_in instead of creating a copy of the cloud
// It will overwrite all fields of the filtered points by the user value: 1337

欧式聚类

概念

聚类是指把不同物体的点云分别组合聚集起来, 从而能让你跟踪汽车, 行人等多个目标. 其中一种对点云数据进行分组和聚类的方法称为欧氏聚类.

欧式聚类是指将距离紧密度高的点云聚合起来. 为了有效地进行最近邻搜索, 可以使用 KD-Tree 数据结构, 这种结构平均可以加快从 o (n)到 o (log (n))的查找时间. 这是因为Kd-Tree允许你更好地分割你的搜索空间. 通过将点分组到 KD-Tree 中的区域中, 您可以避免计算可能有数千个点的距离, 因为你知道它们不会被考虑在一个紧邻的区域中.

欧氏聚类对象 ec 具有距离容忍度. 在这个距离之内的任何点都将被组合在一起. 它还有用于表示集群的点数的 min 和 max 参数. 如果一个集群真的很小, 它可能只是噪音, min参数会限制使用这个集群. 而max参数允许我们更好地分解非常大的集群, 如果一个集群非常大, 可能只是许多其他集群重叠, 最大容差可以帮助我们更好地解决对象检测. 欧式聚类的最后一个参数是 Kd-Tree. Kd-Tree是使用输入点云创建和构建的, 这些输入云点是初始点云过滤分割后得到障碍物点云.

实例

pcl::EuclideanClusterExtractionpcl::PointXYZ类
采用欧式聚类对三维点云进行分割
预先的步骤是:读入一个PCD文件,进行滤波处理,然后RANSAC平面模型分割处理,提取出点云所有在平面上的点集,存储

  // Read in the cloud datapcl::PCDReader reader;pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>), cloud_f (new pcl::PointCloud<pcl::PointXYZ>);reader.read ("table_scene_lms400.pcd", *cloud);std::cout << "PointCloud before filtering has: " << cloud->points.size () << " data points." << std::endl; //*// Create the filtering object: downsample the dataset using a leaf size of 1cmpcl::VoxelGrid<pcl::PointXYZ> vg;pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);vg.setInputCloud (cloud);vg.setLeafSize (0.01f, 0.01f, 0.01f);vg.filter (*cloud_filtered);std::cout << "PointCloud after filtering has: " << cloud_filtered->points.size ()  << " data points." << std::endl; //*// Create the segmentation object for the planar model and set all the parameterspcl::SACSegmentation<pcl::PointXYZ> seg;pcl::PointIndices::Ptr inliers (new pcl::PointIndices);pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane (new pcl::PointCloud<pcl::PointXYZ> ());pcl::PCDWriter writer;seg.setOptimizeCoefficients (true);seg.setModelType (pcl::SACMODEL_PLANE);seg.setMethodType (pcl::SAC_RANSAC);seg.setMaxIterations (100);seg.setDistanceThreshold (0.02);int i=0, nr_points = (int) cloud_filtered->points.size ();while (cloud_filtered->points.size () > 0.3 * nr_points){// Segment the largest planar component from the remaining cloudseg.setInputCloud (cloud_filtered);seg.segment (*inliers, *coefficients);if (inliers->indices.size () == 0){std::cout << "Could not estimate a planar model for the given dataset." << std::endl;break;}// Extract the planar inliers from the input cloudpcl::ExtractIndices<pcl::PointXYZ> extract;extract.setInputCloud (cloud_filtered);extract.setIndices (inliers);extract.setNegative (false);// Write the planar inliers to diskextract.filter (*cloud_plane);std::cout << "PointCloud representing the planar component: " << cloud_plane->points.size () << " data points." << std::endl;// Remove the planar inliers, extract the restextract.setNegative (true);extract.filter (*cloud_f);cloud_filtered = cloud_f;}
  // Creating the KdTree object for the search method of the extraction创建一个kd树作为提取点云时的搜索方法,再创建一个点云索引向量,用于存储实际的点云索引信息,每个检测到的点云聚类被保存在这里,如cluster_indices[0]包含点云中第一个聚类包含的点集的所有索引pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);tree->setInputCloud (cloud_filtered);std::vector<pcl::PointIndices> cluster_indices;//创建点云索引向量pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;ec.setClusterTolerance (0.02); // 设置近邻搜索半径为2cmec.setMinClusterSize (100);//设置一个聚类需要的最少点数目为100ec.setMaxClusterSize (25000);//设置一个聚类需要的最大点数目为25000ec.setSearchMethod (tree);//设置点云的搜索机制ec.setInputCloud (cloud_filtered);ec.extract (cluster_indices);//从点云中提取聚类,并将点云索引保存在cluster_indices

ClusterTolerance 搜索半径需要通过测试来设置一个合适的聚类搜索半径

 //迭代访问点云索引cluster_indices,直到分割出所有聚类int j=0;for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin (); it != cluster_indices.end (); ++it){pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster (new pcl::PointCloud<pcl::PointXYZ>);//创建新的点云数据集cloud_cluster,将所有当前聚类写入到点云数据集中for (std::vector<int>::const_iterator pit = it->indices.begin (); pit != it->indices.end (); pit++)cloud_cluster->points.push_back (cloud_filtered->points[*pit]); //*cloud_cluster->width = cloud_cluster->points.size ();cloud_cluster->height = 1;cloud_cluster->is_dense = true;std::cout << "PointCloud representing the Cluster: " << cloud_cluster->points.size () << " data points." << std::endl;std::stringstream ss;ss << "cloud_cluster_" << j << ".pcd";writer.write<pcl::PointXYZ> (ss.str (), *cloud_cluster, false); //*j++;}

点云PCL学习笔记-分割segmentation-RANSAC随机采样一致性算法欧式聚类提取相关推荐

  1. PCL 点云分割与分类 Segmentation RANSAC随机采样一致性 平面模型分割 欧氏距离分割 区域聚类分割算法 最小分割算法 超体聚类 渐进式形态学滤波器

    点云分割 博文末尾支持二维码赞赏哦 _ 点云分割是根据空间,几何和纹理等特征对点云进行划分, 使得同一划分内的点云拥有相似的特征,点云的有效分割往往是许多应用的前提, 例如逆向工作,CAD领域对零件的 ...

  2. PCL学习:随机采样一致性算法(RANSAC)

    此文是在他人的文章上进行了补充完善.另外代码部分是在Ziv Yaniv的c++实现上重新实现了一次,加了中文注释,修正了一个错误.便于理解算法实现. 随机采样一致性算法,RANSAC是"RA ...

  3. RANSAC(随机采样一致算法)原理及openCV代码实现

    <RANSAC(随机采样一致算法)原理及openCV代码实现> 原文: http://www.lai18.com/content/1046939.html  本文转自:http://b ...

  4. PCL采样一致性算法

    在计算机视觉领域广泛的使用各种不同的采样一致性参数估计算法用于排除错误的样本,样本不同对应的应用不同,例如剔除错误的配准点对,分割出处在模型上的点集,PCL中以随机采样一致性算法(RANSAC)为核心 ...

  5. PCL点云库学习笔记 点云的欧式聚类

    欧式聚类详解(点云数据处理) 欧式聚类是一种基于欧氏距离度量的聚类算法.基于KD-Tree的近邻查询算法是加速欧式聚类算法的重要预处理方法. KD-Tree最近邻搜索 Kd-树是K-dimension ...

  6. 点云库PCL学习笔记 -- 点云滤波Filtering -- 3. StatisticalOutlierRemoval 统计滤波器

    点云库PCL学习笔记 -- 点云滤波Filtering -- 3.StatisticalOutlierRemoval 统计滤波器 StatisticalOutlierRemoval 统计滤波器相关简介 ...

  7. PCL学习笔记(二):PCL官方教程学习

    PCL学习笔记(二):PCL官方教程学习 PCD文件制作 Features 表面法线提取 Keypoints 提取NARF关键点 KdTree Range Image How to create a ...

  8. 【点云处理技术之PCL】点云分割算法1——平面模型分割、圆柱模型分割和欧式聚类提取(含欧式聚类原理)

    文章目录 1. 平面分割 2. 圆柱分割 3. 欧式聚类分割 1. 平面分割 下列中,先随机创建了z=1.0的随机点,然后改变其中3个点的z值.最后,使用SACMODEL_PLANE平面模型对它进行拟 ...

  9. PCL学习笔记5-sample consensus采样一致性算法

    PCL几种采样方法 - Being_young - 博客园 PCL常见采样方法 下采样 Downsampling 通过构造一个三维体素栅格,用每个体素内所有点的重心近似该体素其他点,达到滤波的效果,这 ...

最新文章

  1. PowerDesigner导入SQL生成数据模型
  2. BZOJ4197 [Noi2015]寿司晚宴 【状压dp】
  3. 字典占内存大吗_微博热搜稳占第一!「美团大数据杀熟」是真的吗?
  4. 清除vc6工程中的vss
  5. 【MM配置】 MM组织架构的配置
  6. ASP.NET AJAX文档-ASP.NET AJAX 概述[翻译](1)
  7. mysql反应慢_MySQL反应慢排查思路
  8. SAP Spartacus 页面 cx-storefront 的填充逻辑
  9. 如何确保sublime text每次启动时不会自动打开以前打开的文档
  10. CSS3笔记之定位篇(一)relative
  11. Vue本地图片循环加载显示不出来,vue img标签 :src地址拼接
  12. lighttpd在proxy-core下path_info为空的修复
  13. Linux 线程控制
  14. 数组循环移动 空间复杂度O(1)
  15. Electron —— Cannot find module ‘jquery.min.js’(II)
  16. hadoop mapper从源码开始 详解
  17. Linux中/etc/rc开头文件详细解释
  18. 机器人机构学基础(朱大昌)第三章部分习题答案
  19. html 中加载字体太慢,css字体文件包太大无法引入怎么处理?
  20. JS 实现blob与base64互转

热门文章

  1. 人脸识别实战5-人脸识别模型训练环境搭建及模型训练
  2. 优酷发布2018世界杯战略 阿里云将提供全程技术保障
  3. 【精彩回顾】第二届微医前端技术沙龙(附PPT下载)
  4. input 框背景提示文字
  5. 在生活当中过得不如意也就算了
  6. 【知识分享】搜索引擎
  7. 《叶问》第1期--知数堂技术小贴士
  8. coreldraw 导入面料_CDR真强!支持导入的文件格式这么多
  9. mplayer-php,Mplayer使用及快捷键
  10. 论大规模分布式系统缓存设计策略