pcl 使用gpu计算法向量_PCL滤波介绍(1)

在获取点云数据时 ,由于设备精度,操作者经验环境因素带来的影响,以及电磁波的衍射特性,被测物体表面性质变化和数据拼接配准操作过程的影响,点云数据中讲不可避免的出现一些噪声。在点云处理流程中滤波处理作为预处理的第一步,对后续的影响比较大,只有在滤波预处理中将噪声点 ,离群点,孔洞,数据压缩等按照后续处理定制,才能够更好的进行配准,特征提取,曲面重建,可视化等后续应用处理,PCL中点云滤波模块提供了很多灵活实用的滤波处理算法,例如:双边滤波,高斯滤波,条件滤波,直通滤波,基于随机采样一致性滤波

PCL中点云滤波的方案
PCL中总结了几种需要进行点云滤波处理情况,这几种情况分别如下:
(1) 点云数据密度不规则需要平滑
(2) 因为遮挡等问题造成离群点需要去除
(3) 大量数据需要下采样
(4) 噪声数据需要去除
对应的方案如下:
(1)按照给定的规则限制过滤去除点
(2) 通过常用滤波算法修改点的部分属性
(3)对数据进行下采样
双边滤波算法是通过取临近采样点和加权平均来修正当前采样点的位置,从而达到滤波效果,同时也会有选择剔除与当前采样点“差异”太大的相邻采样点,从而保持原特征的目的

Classes

pcl::ApproximateVoxelGrid< PointT > 类ApproximateVoxelGrid根据给定的点云形成三维体素栅格,并利用所有体素的中心点近似体素中包含的点集,这样完成下采样得到滤波结果,该类比较合适对海量点云数据在处理前进行压缩,提高算法效率

class pcl::BilateralFilter< PointT > 类BilateralFilter是对双边滤波算法在点云上的实现,该类的实现利用的并非XYZ字段的数据进行,而是利用强度数据进行双边滤波算法的实现,所以在使用该类时点云的类型必须有强度字段,否则无法进行双边滤波处理(所以在用这个函数的时候是需要注意自己输入点云的数据格式的,需要包含点云的强度信息)

class pcl::BoxClipper3D< PointT > 实现用一个原点为中心,XYZ各个方向尺寸为2 经过用户指定的仿射变换的立方体进行空间裁剪,通过设置一个仿射变换矩阵先对立方体进行变换处理,之后输出仿射变换后落在该立方体内的点集

class pcl::Clipper3D< PointT >是3D空间裁剪对象的基类

class pcl::ConditionalRemoval< PointT >实现过滤满足一定的条件的点云数据,非常灵活,可以设置滤波条件

class pcl::filters::Convolution< PointIn, PointOut > 实现卷积滤波处理

class pcl::filters::ConvolvingKernel< PointInT, PointOutT > 是所有卷积核的基类

class pcl::filters::GaussianKernel< PointInT, PointOutT > 是基于高斯核的卷积滤波实现 高斯滤波相当于一个具有平滑性能的低通滤波器

class pcl::filters::GaussianKernelRGB< PointInT, PointOutT > 是附加RGB通道基于高斯核的卷积滤波实现,不仅考虑空间XYZ而且考虑RGB

class pcl::CropBox< PointT > 过滤掉在用户给定立方体内的点云数据

class pcl::CropHull< PointT > 过滤在给定三维封闭曲面或二维封闭多边形内部或外部的点云数据

class pcl::ExtractIndices< PointT > 从一个点云中提取索引

class pcl::Filter< PointT > 是滤波模块最重要的类 其他所有的滤波模块的类都从它继承。

。。。。。。。。。。(还有很多)

按我的理解点云法向量也应该可以放在滤波的模块中,法向量在点云的滤波以及特征点的计算等方面都十分重要,,在后面的点云库的版本中,对于法向量的计算放在了feature模块中,这需要大家注意。

应用实例

(1)在PCL 中使用直通滤波器对点云进行滤波处理

代码解析如下

#include <iostream>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h>

int
 main (int argc, char** argv)
{
  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);
  }

  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;
  /************************************************************************************
   创建直通滤波器的对象,设立参数,滤波字段名被设置为Z轴方向,可接受的范围为(0.0,1.0)
   即将点云中所有点的Z轴坐标不在该范围内的点过滤掉或保留,这里是过滤掉,由函数setFilterLimitsNegative设定
   ***********************************************************************************/
  // 设置滤波器对象
  pcl::PassThrough<pcl::PointXYZ> pass;
  pass.setInputCloud (cloud);            //设置输入点云
  pass.setFilterFieldName ("z");         //设置过滤时所需要点云类型的Z字段
  pass.setFilterLimits (0.0, 1.0);        //设置在过滤字段的范围
  //pass.setFilterLimitsNegative (true);   //设置保留范围内还是过滤掉范围内
  pass.filter (*cloud_filtered);            //执行滤波,保存过滤结果在cloud_filtered

  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);
}

由于随机生成的点云,所以每次运行结果不一样,但是都会将点云中Z坐标在(0,1)范围外的点过滤掉,

df8053d30c8e68666c36f4d241e42a3f.png

(modify 2019-12-23 这里说明一下以上只是一个简单的实例,实际的使用过程中,需要根据自己的实际情况来定,比如你需要处理激光雷达的数据,需要得到路边沿的点云数据,此时你就可以使用滤波函数只过滤掉比较远的的点云数据)

(2)使用VoxelGrid滤波器对点云进行下采样

使用体素化网格方法实现下采样,即减少点的数量 减少点云数据,并同时保存点云的形状特征,在提高配准,曲面重建,形状识别等算法速度中非常实用,PCL是实现的VoxelGrid类通过输入的点云数据创建一个三维体素栅格,容纳后每个体素内用体素中所有点的重心来近似显示体素中其他点,这样该体素内所有点都用一个重心点最终表示,对于所有体素处理后得到的过滤后的点云,这种方法比用体素中心逼近的方法更慢,但是对于采样点对应曲面的表示更为准确。(modify 2019-12-23这个函数的一般在点云数据比较多的情况下,使用滤波的方法,能够很好的减少点云的数据量,减少计算量,特别是在我们要计算法向量的时候,如果能够使用体素过滤之后,计算法向量就会变得很快。)

代码解释

voxel_grid.cpp
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>


int
main (int argc, char** argv)
{

  pcl::PCLPointCloud2::Ptr cloud (new pcl::PCLPointCloud2 ());
  pcl::PCLPointCloud2::Ptr cloud_filtered (new pcl::PCLPointCloud2 ());

  //点云对象的读取
  pcl::PCDReader reader;
 
  reader.read ("table_400.pcd", *cloud);    //读取点云到cloud中

  std::cerr << "PointCloud before filtering: " << cloud->width * cloud->height 
       << " data points (" << pcl::getFieldsList (*cloud) << ").";

  /******************************************************************************
  创建一个叶大小为1cm的pcl::VoxelGrid滤波器,
**********************************************************************************/
  pcl::VoxelGrid<pcl::PCLPointCloud2> sor;  //创建滤波对象
  sor.setInputCloud (cloud);            //设置需要过滤的点云给滤波对象
  sor.setLeafSize (0.01f, 0.01f, 0.01f);  //设置滤波时创建的体素体积为1cm的立方体
  sor.filter (*cloud_filtered);           //执行滤波处理,存储输出

  std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height 
       << " data points (" << pcl::getFieldsList (*cloud_filtered) << ").";

  pcl::PCDWriter writer;
  writer.write ("table_scene_lms400_downsampled.pcd", *cloud_filtered, 
         Eigen::Vector4f::Zero (), Eigen::Quaternionf::Identity (), false);

  return (0);
}

从输出的结果可以看出,过滤后的数据量大大减小了。打印结果如下

08dc289ce5205a02e146ba7a91949e98.png

modify2019-12-23 这里有很多小的函数可以和大家分享,比如pcl::ApproximateVoxelGrid< PointT >,这个函数与体素滤波有什么区别呢???有兴趣的可以留言评论,这里就不在说明。纸上得来终觉浅,绝知此事要躬行啊

但是由于时间有限这里就不再一一举例,如果后面有机会将会更加详细的说明一下函数

显示的结果图可以看出对比

07d960438b227c1ad973119441b3d1e4.png

20b048f8934bec6483657420cfa7288a.png

原始点云与滤波后的点云可视化结果,明显的可以看出来,点的密度大小与整齐程度不同,虽然处理后的数据量大大减小,但是很明显所含有的形状特征和空间结构信息与原始点云差不多。

文章中如有问题可以留言交流,可扫描二维码,关注微信公众号,共同学习交流

d634f4d10788a61f3ae52b883998531d.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
计算点云向量可以使用PCL库的NormalEstimation类。 首先,需要对点云进行下采样,以减少计算量和噪声对向量的影响。可以使用VoxelGrid滤波器实现。 然后,需要创建一个NormalEstimation对象,并将下采样后的点云数据传递给它。接着,可以设置线估计方的搜索半径,并调用compute()方计算向量。 最后,可以使用PassThrough滤波器过滤掉无效向量。例如,可以通过设置向量的z值是否为nan来实现。 下面是一段示例代码: ```cpp #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/filters/voxel_grid.h> #include <pcl/features/normal_3d.h> #include <pcl/filters/passthrough.h> int main (int argc, char** argv) { // 加载点云数据 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>); pcl::io::loadPCDFile<pcl::PointXYZ> ("input.pcd", *cloud); // 下采样 pcl::VoxelGrid<pcl::PointXYZ> sor; sor.setInputCloud (cloud); sor.setLeafSize (0.01f, 0.01f, 0.01f); sor.filter (*cloud); // 计算向量 pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne; ne.setInputCloud (cloud); pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>); ne.setSearchMethod (tree); pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>); ne.setRadiusSearch (0.03); ne.compute (*cloud_normals); // 过滤无效向量 pcl::PassThrough<pcl::Normal> pass; pass.setInputCloud (cloud_normals); pass.setFilterFieldName ("normal_z"); pass.setFilterLimits (-1.0, 1.0); pcl::PointCloud<pcl::Normal>::Ptr cloud_normals_filtered (new pcl::PointCloud<pcl::Normal>); pass.filter (*cloud_normals_filtered); // 输出结果 pcl::io::savePCDFileASCII ("output.pcd", *cloud_normals_filtered); return 0; } ``` 其,setRadiusSearch()方用于设置搜索半径。这个半径越大,计算出的向量越平滑;反之,则越精细。 PassThrough滤波器的setFilterFieldName()方用于设置过滤字段,这里设置为normal_z,表示对向量的z值进行过滤。setFilterLimits()方用于设置过滤范围,这里设置为(-1.0, 1.0),表示只保留z值在这个范围内的向量

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值