4.sample consensus 采样一致性

1.RANSAC原理

PCL 中以随机采样一致性算法( RANSAC) 为核心,实现了五种类似于RANSAC的随机参数估计算法,例如随机采样一致性估计(RANSAC ) 、最大似然一致性估计 (MLESAC ) 、最小中值方差一致性估计 ( LMEDS )等,所有的估计参数算法都符合一致性准则。利用RANSAC可以实现点云分割,目前 PCL 中支持的几何模型分割有空间平面、直线、二维或三维圆、圆球、锥体等 。 RANSAC的另一应用就是点云的配准对的剔除

RANSAC从样本中随机抽选出一个样本子集,使用最小方差估计算法对这个子集计算模型参数,然后计算所有样本与该模型的偏差,再使用一个预先设定好的阈值与偏差比较,当偏差小于阈值时,该样本点属于模型内样本点 ( inliers),或称内部点、局内点或内点,否则为模型外样本点(outliers),或称外部点、局外点或外点,记录下当前的 inliers 的个数,然后重复这一过程。每一次重复都记录当前最佳的模型参数,所谓最佳即是inliers的个数最多 ,此时对应的inliers个数为 best_ninliers 。 每次迭代的末尾都会根据期望的误差率、 best_ninliers、总样本个数、当前迭代次数,计算一 个迭代结束评判因子,据此决定是否迭代结束。迭代结束后,最佳模型参数就是最终的模型参数估计值 。

RANSAC理论上可以剔除outliers的影响,并得到全局最优的参数估计。但是RANSAC 有两个问题,首先在每次迭代中都要区分 inliers 和 outlieres,因此需要事先设定阈值,当模型具有明显的物理意义时,这个阈值还比较容易设定,但是若模型比较抽象时,阈值就不那么容易设定了。而且固定阈值不适用于样本动态变化的应用;第二个问题是,RANSAC的迭代次数是运行期决定的,不能预知迭代的确切次数(当然迭代次数的范围是可以预测的)。除此之外, RANSAC 只能从一个特定数据集中估计一个模型,当两个(或者更多个)模型存在时,RANSAC 同时找到多个模型。

另:

最小中值法(LMedS)

LMedS的做法很简单,就是从样本中随机抽出N个样本子集,使用最大似然(通常是最小二乘)对每个子集计算模型参数和该模型的偏差,记录该模型参数及子集中所有样本中偏差居中的那个样本的偏差(即Med偏差),最后选取N个样本子集中Med偏差最小的所对应的模型参数作为我们要估计的模型参数。

应用举例:先分割平面模型,再对平面外的点云分割圆柱模型,即能分割出杯子模型:

#pragma once
#include<pcl/point_types.h>
#include<pcl/io/pcd_io.h>
#include<pcl/visualization/pcl_visualizer.h>
#include<pcl/segmentation/sac_segmentation.h>
#include<pcl/filters/passthrough.h>//直通滤波头文件
#include<pcl/features/normal_3d.h>
#include<pcl/features/normal_3d_omp.h>
#include<pcl/ModelCoefficients.h>
#include<pcl/filters/extract_indices.h>//按索引过滤头文件
#include<pcl/segmentation/extract_clusters.h>
void MySegmentation()
{
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cylinder(new pcl::PointCloud<pcl::PointXYZ>);

	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered2(new pcl::PointCloud<pcl::PointXYZ>);

	pcl::PointCloud<pcl::Normal>::Ptr cloud_normal(new pcl::PointCloud<pcl::Normal>);
	pcl::PointCloud<pcl::Normal>::Ptr cloud_normal2(new pcl::PointCloud<pcl::Normal>);


	pcl::io::loadPCDFile("table_scene_mug_stereo_textured.pcd", *cloud);
	//直通滤波,保留z在0到1.5的点云
	pcl::PassThrough<pcl::PointXYZ>::Ptr pass(new pcl::PassThrough<pcl::PointXYZ>);
	pass->setInputCloud(cloud);
	pass->setFilterFieldName("z");
	pass->setFilterLimits(0, 1.5);
	pass->filter(*cloud_filtered);
	//估计法线
	pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal>::Ptr ne(new pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal>);
	ne->setInputCloud(cloud_filtered);
	ne->setNumberOfThreads(8);
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
	ne->setSearchMethod(tree);
	ne->setRadiusSearch(0.02f);
	ne->compute(*cloud_normal);

	//平面分割
	pcl::ModelCoefficients::Ptr plane_coefficient(new pcl::ModelCoefficients), cylinder_coefficient(new pcl::ModelCoefficients);
	pcl::PointIndices::Ptr plane_indices(new pcl::PointIndices),cylinder_indices(new pcl::PointIndices);
	pcl::SACSegmentationFromNormals<pcl::PointXYZ, pcl::Normal> seg;
	seg.setInputCloud(cloud_filtered);
	seg.setInputNormals(cloud_normal);
	seg.setOptimizeCoefficients(true);
	seg.setModelType(pcl::SACMODEL_PLANE);
	seg.setNormalDistanceWeight(0.1);
	seg.setMethodType(pcl::SAC_RANSAC);
	seg.setMaxIterations(100);
	seg.setDistanceThreshold(0.03);
	seg.segment(*plane_indices, *plane_coefficient);

	//按照索引提取平面上的点
	pcl::ExtractIndices<pcl::PointXYZ>ex;
	ex.setInputCloud(cloud_filtered);
	ex.setIndices(plane_indices);
	ex.filter(*cloud_plane);//可将cloud_plane保存

	//除去平面上的点
	ex.setNegative(true);
	ex.filter(*cloud_filtered2);//不含平面上的点,用于进一步分割

	pcl::ExtractIndices<pcl::Normal> exno;
	exno.setInputCloud(cloud_normal);
	exno.setNegative(true);
	exno.setIndices(plane_indices);
	exno.filter(*cloud_normal2);

	seg.setOptimizeCoefficients(true);
	seg.setMethodType(pcl::SACMODEL_CYLINDER);
	seg.setInputCloud(cloud_filtered2);
	seg.setInputNormals(cloud_normal2);
	seg.setRadiusLimits(0, 0.1);
	seg.setDistanceThreshold(0.05);
	seg.setMaxIterations(10000);
	seg.setNormalDistanceWeight(0.1);
	seg.segment(*cylinder_indices, *cylinder_coefficient);

	//提取圆柱内的点云
	ex.setIndices(cylinder_indices);
	ex.setInputCloud(cloud_filtered2);
	ex.setNegative(false);
	ex.filter(*cloud_cylinder);

	if (cloud_cylinder->points.empty())
	std::cerr << "Can't find the cylindrical component." << std::endl;
	else
	{
		std::cerr << "PointCloud representing the cylindrical component: " << cloud_cylinder->points.size() << " data points." << std::endl;
		pcl::io::savePCDFile("table_scene_mug_stereo_textured_cylinder.pcd", *cloud_cylinder); // 分割得到的平面
	}
	//可视化
	pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("segmentation"));
	viewer->setBackgroundColor(0, 0, 0);
	int v1(0), v2(1), v3(2);
	viewer->createViewPort(0, 0, 0.5, 1, v1);
	viewer->createViewPort(0.5, 0, 1, 0.5, v2);
	viewer->createViewPort(0.5, 0.5, 1, 1, v3);

	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> v1_color(cloud,0, 0, 255);
	viewer->addPointCloud(cloud, v1_color,"out1",v1);
	
	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> v2_color(cloud, 0, 255,0);
	viewer->addPointCloud(cloud_plane, v2_color, "out2", v2);

	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> v3_color(cloud, 255,0, 0 );
	viewer->addPointCloud(cloud_cylinder, v3_color, "out3", v3);

	while (!viewer->wasStopped())
	{
		viewer->spinOnce();

	}


}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值