1、什么是随即一致性算法
??Random Sample Consensus的缩写是RANSAC,它是一种从一组包含异常值的数据中估计数学模型参数的迭代方法。这个算法是由Fischler和Bolles在1981年发表的。RANSAC算法假设我们正在查看的所有数据都由内值(正确值)和异常值组成。内置可以用带有一组特定参数值的模型(例如直线、面、圆柱等形状函数)来解释,而异常值在任何情况下都不适合该模型。另一个必要的假设是,有一个程序可以根据数据对所选模型的参数进行迭代最优估计。维基百科关于Ransac介绍点击这里。
??RANSAC算法的输入是一组观测数据值,一个能解释或拟合观测数据的参数化模型,以及一些置信度参数。对于点云的RANSAC来说,可以简单理解为,输入①一组点云、②某些特征形状的参数化模型以及③一些阈值(包括随机子集点数,迭优化代次数,置信度等)。 ??RANSAC通过迭代选择原始数据的随机子集来实现其目标。这些数据是假设的正确数据,然后这个假设会经过如下步骤被验证:
1.在原始数据中随机选取一个(最小)子集作为假设内点(正确的拟合点); 2.根据假设的内点拟合一个模型; 3.判断剩余的原始数据是否符合拟合的模型,将其分为内点和外点。如果内点过少则标记为无效迭代; 4.根据假设的内点和上一步划分的内点重新拟合模型; 5.计算所有内点的残差,根据残差和或者错误率重新评估模型。
??这个过程重复固定的次数,每次产生的要么是一个模型(因为被归类为内层的点太少而被拒绝),要么是一个带有相应误差度量的精化模型。在后一种情况下,如果精化模型的误差小于上次保存的模型,则保留它。
优点: ??RANSAC能够对模型参数进行鲁棒估计,即即使在数据集中存在大量异常值时,也可以对参数进行高度准确的估计。 缺点: ??RANSAC缺点是,计算参数所需的时间没有上限。当计算的迭代次数有限时,得到的解可能不是最优的,甚至可能不是适合数据的解。通过这种方式,RANSAC提供了一种取舍;通过计算更多的迭代次数,即牺牲计算时间增加产生一个合理的模型的可能性。其次是,它需要设置针对问题的阈值。而且,RANSAC只能对特定的数据集估计一个模型。对于存在两个(或更多)模型的单模型方法,RANSAC可能无法找到任何一个。
左边和右边的图片(来自[WikipediaRANSAC])展示了RANSAC算法在二维数据集上的简单应用。左边的图像是一个包含内值和异常值的数据集的可视化表示。右边的图片用红色显示了所有的异常值,用蓝色显示了内值。蓝线是RANSAC的成果。在这种情况下,我们试图拟合数据的模型是一条线,结果看起来非常好。
2、在PCL中如何使用RANSAC
??请看如下案例的代码及其注释:
#include <iostream>
#include <thread>
#include <pcl/console/parse.h>
#include <pcl/point_cloud.h>
#include <pcl/common/io.h>
#include <pcl/point_types.h>
#include <pcl/sample_consensus/ransac.h>
#include <pcl/sample_consensus/sac_model_plane.h>
#include <pcl/sample_consensus/sac_model_sphere.h>
#include <pcl/visualization/pcl_visualizer.h>
using namespace std::chrono_literals;
pcl::visualization::PCLVisualizer::Ptr
simpleVis (pcl::PointCloud<pcl::PointXYZ>::ConstPtr cloud)
{
pcl::visualization::PCLVisualizer::Ptr viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));
viewer->setBackgroundColor (0, 0, 0);
viewer->addPointCloud<pcl::PointXYZ> (cloud, "sample cloud");
viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");
viewer->initCameraParameters ();
return (viewer);
}
int
main(int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr final (new pcl::PointCloud<pcl::PointXYZ>);
cloud->width = 500;
cloud->height = 1;
cloud->is_dense = false;
cloud->points.resize (cloud->width * cloud->height);
for (pcl::index_t i = 0; i < static_cast<pcl::index_t>(cloud->size ()); ++i)
{
if (pcl::console::find_argument (argc, argv, "-s") >= 0 || pcl::console::find_argument (argc, argv, "-sf") >= 0)
{
(*cloud)[i].x = 1024 * rand () / (RAND_MAX + 1.0);
(*cloud)[i].y = 1024 * rand () / (RAND_MAX + 1.0);
if (i % 5 == 0)
(*cloud)[i].z = 1024 * rand () / (RAND_MAX + 1.0);
else if(i % 2 == 0)
(*cloud)[i].z = sqrt( 1 - ((*cloud)[i].x * (*cloud)[i].x)
- ((*cloud)[i].y * (*cloud)[i].y));
else
(*cloud)[i].z = - sqrt( 1 - ((*cloud)[i].x * (*cloud)[i].x)
- ((*cloud)[i].y * (*cloud)[i].y));
}
else
{
(*cloud)[i].x = 1024 * rand () / (RAND_MAX + 1.0);
(*cloud)[i].y = 1024 * rand () / (RAND_MAX + 1.0);
if( i % 2 == 0)
(*cloud)[i].z = 1024 * rand () / (RAND_MAX + 1.0);
else
(*cloud)[i].z = -1 * ((*cloud)[i].x + (*cloud)[i].y);
}
}
std::vector<int> inliers;
pcl::SampleConsensusModelSphere<pcl::PointXYZ>::Ptr
model_s(new pcl::SampleConsensusModelSphere<pcl::PointXYZ> (cloud));
pcl::SampleConsensusModelPlane<pcl::PointXYZ>::Ptr
model_p (new pcl::SampleConsensusModelPlane<pcl::PointXYZ> (cloud));
if(pcl::console::find_argument (argc, argv, "-f") >= 0)
{
pcl::RandomSampleConsensus<pcl::PointXYZ> ransac (model_p);
ransac.setDistanceThreshold (.01);
ransac.computeModel();
ransac.getInliers(inliers);
}
else if (pcl::console::find_argument (argc, argv, "-sf") >= 0 )
{
pcl::RandomSampleConsensus<pcl::PointXYZ> ransac (model_s);
ransac.setDistanceThreshold (.01);
ransac.computeModel();
ransac.getInliers(inliers);
}
pcl::copyPointCloud (*cloud, inliers, *final);
pcl::visualization::PCLVisualizer::Ptr viewer;
if (pcl::console::find_argument (argc, argv, "-f") >= 0 || pcl::console::find_argument (argc, argv, "-sf") >= 0)
viewer = simpleVis(final);
else
viewer = simpleVis(cloud);
while (!viewer->wasStopped ())
{
viewer->spinOnce (100);
std::this_thread::sleep_for(100ms);
} return 0;
}
【博主简介】 ??斯坦福的兔子,男,天津大学机械工程工学硕士。毕业至今从事光学三维成像及点云处理相关工作。因工作中使用的三维处理库为公司内部库,不具有普遍适用性,遂自学开源PCL库及其相关数学知识以备使用。谨此将自学过程与君共享。 博主才疏学浅,尚不具有指导能力,如有问题还请各位在评论处留言供大家共同讨论。
|