pcl滤波器
pcl一共是有十二个主要模块,详细了解可以查看官网。https://pcl.readthedocs.io/projects/tutorials/en/latest/#basic-usage
今天学习一下pcl的滤波器模块。
滤波器模块,官网一共是提供了6个例程,今天看第五个、第六个。
从一个点云中提取索引
在本例程中,将学习如何使用ExtractIndices过滤器根据分割算法输出的索引从点云中提取点的子集。
感兴趣的可以看一下YouTube演示视频
https://youtu.be/ZTK7NR1Xx4c
作为演示的pcd文件地址,需要自取
https://raw.github.com/PointCloudLibrary/data/master/tutorials/table_scene_lms400.pcd
看代码
#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)
{
/**********************************************************************************************************
从输入的.PCD 文件载入数据后,创建一个VoxelGrid滤波器对数据进行下采样,在这里进行下才样是为了加速处理过程,
越少的点意味着分割循环中处理起来越快
**********************************************************************************************************/
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;
// 创建体素栅格下采样: 下采样的大小为1cm
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 (); // 点云总数
// While 30% of the original cloud is still there
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 the inliers
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);
// Create the filtering object
extract.setNegative (true);
extract.filter (*cloud_f);
cloud_filtered.swap (cloud_f);
i++;
}
return (0);
}
看一下结果:
table_scene_lms400
table_scene_lms400_downsampled
table_scene_lms400_plane_0
table_scene_lms400_plane_1
CMakeLists.txt
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(extract_indices)
find_package(PCL 1.2 REQUIRED)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
add_executable (extract_indices extract_indices.cpp)
target_link_libraries (extract_indices ${PCL_LIBRARIES})
用ConditionalRemoval或RadiusOutlinerRemoval移除离群点
本文档演示了如何使用过滤器模块中的几种不同方法从PointCloud中删除离群值。
首先,了解如何使用ConditionalRemoval过滤器,该过滤器删除给定输入云中不满足一个或多个给定条件的所有索引。
然后,学习如何创建一个RadiusOutlierRemoval过滤器,该过滤器删除其输入云中的所有索引,这些索引在一定范围内至少没有一些邻居。
#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)
{
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 = 1024 * rand() / (RAND_MAX + 1.0f);
cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
}
if (strcmp(argv[1], "-r") == 0)
{ // RadiusOutlierRemoval
pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem; //创建滤波器
outrem.setInputCloud(cloud); //设置输入点云
outrem.setRadiusSearch(0.8); //设置半径为0.8的范围内找临近点
outrem.setMinNeighborsInRadius(2); //设置查询点的邻域点集数小于2的删除
// apply filter
outrem.filter(*cloud_filtered); //执行条件滤波 在半径为0.8 在此半径内必须要有两个邻居点,此点才会保存
}
else if (strcmp(argv[1], "-c") == 0)
{ //RadiusOutlierRemoval
//创建条件限定的下的滤波器
pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond(new pcl::ConditionAnd<pcl::PointXYZ>()); //创建条件定义对象
//添加在Z字段上大于0的比较算子
//GT greater than
//EQ equal
//LT less than
//GE greater than or equal
//LE less than
//为条件定义对象添加比较算子
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;
condrem.setCondition(range_cond);
condrem.setInputCloud(cloud); //输入点云
condrem.setKeepOrganized(true); //设置保持点云的结构
// 设置是否保留滤波后删除的点,以保持点云的有序性,通过setuserFilterValue设置的值填充点云;或从点云中删除滤波后的点,从而改变其组织结构
// 如果设置为true且不设置setUserFilterValue的值,则用nan填充点云
// https://blog.csdn.net/qq_37124765/article/details/82262863
// 执行滤波
condrem.filter(*cloud_filtered); //大于0.0小于0.8这两个条件用于建立滤波器
}
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;
// display pointcloud after filtering
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);
}
下面的图片有助于可视化RadiusOutlierRemoval过滤器对象的作用。用户指定邻居的数量,每个索引必须在指定的半径内保持在PointCloud中。例如,如果指定了1个邻居,则只有黄色的点会从PointCloud中移除。如果指定了2个邻居,那么黄色点和绿色点都将从PointCloud中移除。
CMakeLists.txt
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(remove_outliers)
find_package(PCL 1.2 REQUIRED)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
add_executable (remove_outliers remove_outliers.cpp)
target_link_libraries (remove_outliers ${PCL_LIBRARIES})