五. 使用ExtractIndices滤波器从一个点云中提取一个子集
基于某一分割算法提取点云中的一个子集
代码如下:
#include <iostream>
#include <pcl/ModelCoefficients.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/filters/extract_indices.h>
int
main(int argc, char** argv)
{
pcl::PCLPointCloud2::Ptr cloud_blob(new pcl::PCLPointCloud2), cloud_filtered_blob(new pcl::PCLPointCloud2);//申明滤波前后的点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>), cloud_p(new pcl::PointCloud<pcl::PointXYZ>), cloud_f(new pcl::PointCloud<pcl::PointXYZ>);
// 读取PCD文件
pcl::PCDReader reader;
reader.read("table_scene_lms400.pcd", *cloud_blob);
std::cerr << "PointCloud before filtering: " << cloud_blob->width * cloud_blob->height << " data points." << std::endl; //统计滤波前的点云个数
/**********************************************************************************************************
从输入的.PCD 文件载入数据后,创建一个VoxelGrid滤波器对数据进行下采样,在这里进行下才样是为了加速处理过程,
越少的点意味着分割循环中处理起来越快
**********************************************************************************************************/
pcl::VoxelGrid<pcl::PCLPointCloud2> sor; //体素栅格下采样对象
sor.setInputCloud(cloud_blob); //原始点云
sor.setLeafSize(0.01f, 0.01f, 0.01f); //设置采样体素大小
sor.filter(*cloud_filtered_blob); //保存
// 转换为模板点云
pcl::fromPCLPointCloud2(*cloud_filtered_blob, *cloud_filtered);
std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height << " data points." << std::endl;
// 保存下采样后的点云存入磁盘
pcl::PCDWriter writer;
writer.write<pcl::PointXYZ>("table_scene_lms400_downsampled.pcd", *cloud_filtered, false);
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); //设置用哪个随机参数估计方法
seg.setMaxIterations(1000); //设置最大迭代次数
seg.setDistanceThreshold(0.01); //判断是否为模型内点的距离阀值
// 设置ExtractIndices的实际参数
pcl::ExtractIndices<pcl::PointXYZ> extract; //创建点云提取对象
int i = 0, nr_points = (int)cloud_filtered->points.size();
// 当还有30%原始点云数据时
while (cloud_filtered->points.size() > 0.3 * nr_points)
{
// 为了处理点云包含的多个模型,在一个循环中执行该过程并在每次模型被提取后,保存剩余的点进行迭代
seg.setInputCloud(cloud_filtered);
seg.segment(*inliers, *coefficients);
if (inliers->indices.size() == 0)
{
std::cerr << "Could not estimate a planar model for the given dataset." << std::endl;
break;
}
// 分离内层
extract.setInputCloud(cloud_filtered);
extract.setIndices(inliers);
extract.setNegative(false);
extract.filter(*cloud_p);
std::cerr << "PointCloud representing the planar component: " << cloud_p->width * cloud_p->height << " data points." << std::endl;
std::stringstream ss;
ss << "table_scene_lms400_plane_" << i << ".pcd";
writer.write<pcl::PointXYZ>(ss.str(), *cloud_p, false);
// 创建滤波器对象
extract.setNegative(true);
extract.filter(*cloud_f);
cloud_filtered.swap(cloud_f);
i++;
}
return (0);
}
输出打印结果:
可以看出,从输出点云中分割出两个平面模型的点云数据。
并且运行之后在当前目录下有3个点云文件产生,分别为table_scene_lms400_downsampled.pcd、table_scene_lms400_plane_0、table_scene_lms400_plane_1,它们储存了下采样后的点云数据,以及分割得到的两个平面模型对应的点云数据。
六. 使用ConditionalRemoval 或RadiusOutlinerRemoval移除离群点
如何在滤波模块使用几种不同的方法移除离群点:对于ConditionalRemoval滤波器,可以一次删除满足对输入的点云设定的一个或多个条件指标的所有的数据点;对于RadiusOutlinerRemoval滤波器,它可以删除在输入点云一定范围内没有至少达到足够多近邻的所有数据点。
(这两种滤波器具体作用机理可参考其他文章)
#include <iostream>
#include <pcl/point_types.h>
#include <pcl/filters/radius_outlier_removal.h>
#include <pcl/filters/conditional_removal.h>
int
main(int argc, char** argv)
{
srand(time(0));
//确保用户输入正确的命令行参数
if (argc != 2)
{
std::cerr << "please specify command line arg '-r' or '-c'" << std::endl;
exit(0);
}
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
// 填入点云数据
cloud->width = 5; //设置点云宽度或数量,此处为数量
cloud->height = 1; //设置点云高度或标准,此处为无序点云
cloud->points.resize(cloud->width * cloud->height);
for (size_t i = 0; i < cloud->points.size(); ++i) //填充数据
{
cloud->points[i].x = 10 * rand() / (RAND_MAX + 1.0f) ;
cloud->points[i].y = 10 * rand() / (RAND_MAX + 1.0f) ;
cloud->points[i].z = 10 * rand() / (RAND_MAX + 1.0f) ;
}
//RadiusOutlierRemoval滤波器——半径滤波
if (strcmp(argv[1], "-r") == 0) //用户需指定“-r”作为命令行参数,代码才会执行
{
pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem; // 创建滤波器
outrem.setInputCloud(cloud); // 设置输入点云
outrem.setRadiusSearch(0.8); // 设置在0.8半径的范围内找邻近点
outrem.setMinNeighborsInRadius(2); // 创建滤波器设置查询点的邻近点集数小于2的删除
outrem.filter(*cloud_filtered); // 执行条件滤波,存储结果到cloud_filtered
}
//ConditionalRemoval滤波器——条件限定滤波
else if (strcmp(argv[1], "-c") == 0) //用户需指定“ -c”作为命令行参数,代码才会执行
{
// 创建环境
pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond(new
pcl::ConditionAnd<pcl::PointXYZ>()); //创建条件定义对象
//为条件定义对象添加比较算子
range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::GT, 0.0))); //添加在Z字段上大于0的比较算子
range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::LT, 0.8))); //添加在Z字段上小于0.8的比较算子
// 创建滤波器并用条件定义对象初始化
pcl::ConditionalRemoval<pcl::PointXYZ> condrem(range_cond);
condrem.setInputCloud(cloud); //设置输入点云
condrem.setKeepOrganized(true); //设置输入点云结构
condrem.filter(*cloud_filtered); // 执行条件滤波,存储结果到cloud_filtered
}
/*else {
std::cerr << "please specify command line arg '-r' or '-c'" << std::endl;
exit(0);
} */
//输出滤波前的点云
std::cerr << "Cloud before filtering: " << 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;
// 显示滤波后的点云
std::cerr << "Cloud after filtering: " << std::endl;
for (size_t i = 0; i < cloud_filtered->points.size(); ++i)
std::cerr << " " << cloud_filtered->points[i].x << " "
<< cloud_filtered->points[i].y << " "
<< cloud_filtered->points[i].z << std::endl;
return (0);
}
运行结果:
注:直接在VS中编译运行程序可能无法输入-r/c指令,需要在cmd.exe下运行对应的exe文件
RadiusOutlinerRemoval比较适合去除单个的离群点 ConditionalRemoval 比较灵活,可以根据用户设置的条件灵活过滤。