PCL滤波—从一个点云中提取一个子集——ExtractIndices滤波器——索引滤波器
索引滤波的官方教程添加链接描述
使用该方法结合降采样和统计滤波可以很好的完成地面分离。
此处是索引滤波头文件
#include <pcl/filters/extract_indices.h>
此处是重点,距离阈值的参数影响计算速度和地面分割精度,不同模型的参数不一样,一般是0.01-0.2之间取值
seg.setMaxIterations(100); //设置最大迭代次数
seg.setDistanceThreshold(0.15); //设置判断是否为模型内点的距离阈值
此处是判断保留内点还是反向选取,false是保留内点,true是反选
extract.setIndices(inliers);
extract.setNegative(false);
extract.filter(*cloud_p);
这里加了一个循环结构,当还有60%原始点云数据时退出循环
while (cloud_filtered->points.size() > 0.6 * nr_points)
以下是原代码和最终效果,保留了汽车分割掉了地面。
本文参考了添加链接描述
//索引滤波
#include <iostream>
#include <pcl/ModelCoefficients.h>
#include <pcl/io/ply_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>
#include<pcl/visualization/cloud_viewer.h>
#include <pcl/filters/statistical_outlier_removal.h>
int
main(int argc, char** argv)
{
//sensor_msgs::PointCloud2::Ptr cloud_blob (new sensor_msgs::PointCloud2), cloud_filtered_blob (new sensor_msgs::PointCloud2);
pcl::PCLPointCloud2::Ptr cloud_blob(new pcl::PCLPointCloud2);
pcl::PCLPointCloud2::Ptr 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>);
// 填入点云数据
pcl::PLYReader reader;
reader.read("C:\\Users\\fhlhc\\Desktop\\car_left.ply", *cloud_blob);
std::cerr << "PointCloud before filtering: " << cloud_blob->width * cloud_blob->height << " data points." << std::endl;
// 创建滤波器对象:使用叶大小为1cm的下采样
pcl::VoxelGrid< pcl::PCLPointCloud2> sor; //体素栅格下采样对象
sor.setInputCloud(cloud_blob); //设置下采样原始点云数据
sor.setLeafSize(0.06f, 0.06f, 0.06f); //设置采样的体素大小
sor.filter(*cloud_filtered_blob); //执行采样保存数据cloud_filtered_blob
统计滤波器,删除离群点
pcl::StatisticalOutlierRemoval<pcl::PCLPointCloud2> Static; //创建滤波器对象
Static.setInputCloud(cloud_filtered_blob); //设置待滤波的点云
Static.setMeanK(50); //设置在进行统计时考虑查询点临近点数
Static.setStddevMulThresh(0.5); //设置判断是否为离群点的阀值
Static.filter(*cloud_filtered_blob); //存储
// 转化为模板点云
//pcl::fromROSMsg(*cloud_filtered_blob, *cloud_filtered);
pcl::fromPCLPointCloud2(*cloud_filtered_blob, *cloud_filtered);
std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height << " data points." << std::endl;
// 将下采样后的数据存入磁盘
pcl::PLYWriter 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(100); //设置最大迭代次数
seg.setDistanceThreshold(0.15); //设置判断是否为模型内点的距离阈值
// 创建滤波器对象
pcl::ExtractIndices<pcl::PointXYZ> extract;
int i = 0, nr_points = (int)cloud_filtered->points.size();
//为了处理点云中包含多个模型,在一个循环中执行该过程,
//并在每次模型被提取后,我们保存剩余的点,进行迭代;
//模型内点通过分割过程获取;
//当还有60%原始点云数据时退出循环
while (cloud_filtered->points.size() > 0.6 * 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++;
}
cout << "迭代次数:"<<i << endl;
pcl::visualization::CloudViewer viewer("Cloud Viewer"); //创建viewer对象
viewer.showCloud(cloud_f);
// viewer.runOnVisualizationThreadOnce(viewerOneOff);
while (!viewer.wasStopped())
{
//在此处可以添加其他处理
}
return (0);
}