2.常见的点云数据滤波的方法总结(C++)

       常见的点云数据处理有体素网格滤波、半径滤波、直通滤波、双边滤波器,统计滤波器,卷积滤波,条件滤波,高斯滤波等等。每种方法的原理和代码如下:

1.体素网格滤波

       体素网格滤波是对密度大的三维的点在保持原来形状的条件下进行稀疏,目的是为了减小后期点云数据处理的计算量。

       体素是三维的一个个小空间。在输入的点云数据上创建一个个3D体素网格(将体素网格视为一组空间中的微小3D小空间,类似处理二维图片的卷积核)。 然后,在每个体素中,所有存在的点将用它们的质心近似(质心一般用体素内所有坐标的均值表示)。 这种方法比用体素的中心值(坐标的中位数)接代替它们要慢一些,但它可以更准确地保持宏观的几何外形。

PCL代码如下:

#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 ());

  //  1.读取pcd文件(点云文件)
  pcl::PCDReader reader;
  reader.read ("table_scene_lms400.pcd", *cloud); 

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

  // 2.体素网格滤波的处理
  pcl::VoxelGrid<pcl::PCLPointCloud2> sor;//创建体素滤波器对象
  sor.setInputCloud (cloud);//设置输入的点云
  sor.setLeafSize (0.01f, 0.01f, 0.01f);//设置体素大小为5cm*5cm*5cm
  sor.filter (*cloud_filtered);//执行滤波
  // 以下是打印输出
  std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height 
       << " data points (" << pcl::getFieldsList (*cloud_filtered) << ").";


  // 3.保存处理后的点云数据,保存为pcd文件
  pcl::PCDWriter writer;
  writer.write ("table_scene_lms400_downsampled.pcd", *cloud_filtered, 
         Eigen::Vector4f::Zero (), Eigen::Quaternionf::Identity (), false);

  return (0);
}

2.半径滤波

       半径滤波通过计算每个点在设置半径范围内是否有足够的点云数量来进行滤波处理,可以去除稀疏的离群点,实际上,离群点对后续的点云拟合等处理只是起到噪点的作用,去除掉对后续处理有好处。

PCL代码如下:

#include <pcl/io/pcd_io.h>  //文件输入输出
#include <pcl/point_types.h>  //点类型相关定义
#include <pcl/visualization/cloud_viewer.h>  //点云可视化相关定义
#include <pcl/filters/radius_outlier_removal.h>  //滤波相关
#include <pcl/common/common.h>  
#include <iostream>

using namespace std;

int main()
{
	//1.读取点云
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PCDReader r;
	r.read<pcl::PointXYZ>("data\\table_scene_lms400.pcd", *cloud);
	cout << "there are " << cloud->points.size() << " points before filtering." << endl;

	//2.半径滤波
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filter(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::RadiusOutlierRemoval<pcl::PointXYZ> sor;  // 创建滤波器
	sor.setInputCloud(cloud); //设置输入点云
	sor.setRadiusSearch(0.02); //设置在radius半径的范围内找邻近点
	sor.setMinNeighborsInRadius(15); //设置查询点的邻近点集数小于15的删除
	sor.setNegative(false); 
	sor.filter(*cloud_filter); //执行条件滤波,存储结果到cloud_filte

	//3.滤波结果保存
	pcl::PCDWriter w;
	w.writeASCII<pcl::PointXYZ>("data\\table_scene_lms400_Radius_filter.pcd", *cloud_filter);
	cout << "there are " << cloud_filter->points.size() << " points after filtering." << endl;

	system("pause");
	return 0;
}

3.直通滤波

       直通滤波的作用是过滤掉在指定维度方向上取值不在给定值域内的点,即通过限定点云三轴坐标的范围快速剔除掉不在需求范围内的点,适用于消除背景等操作。实现原理是:首先,指定一个维度以及该维度下的值域,其次,遍历点云中的每个点,判断该点在指定维度上的取值是否在值域内,删除取值不在值域内的点,最后,遍历结束,留下的点即构成滤波后的点云。

PCL代码如下:

#include <iostream>
#include <ctime>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h>
int
 main (int argc, char** argv)
{ srand(time(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 = rand () / (RAND_MAX + 1.0f)-0.5;
    cloud->points[i].y = rand () / (RAND_MAX + 1.0f)-0.5;
    cloud->points[i].z = rand () / (RAND_MAX + 1.0f)-0.5;
  }
  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;
  // 创建滤波器对象
  pcl::PassThrough<pcl::PointXYZ> pass;//创建滤波器对象
  pass.setInputCloud (cloud);			//设置待滤波的点云
  pass.setFilterFieldName ("z");		//设置在Z轴方向上进行滤波
  pass.setFilterLimits (0.0, 1.0);		//设置滤波范围为0~1,在范围之外的点会被剪除
  //pass.setFilterLimitsNegative (true);//是否反向过滤,默认为false
  pass.filter (*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);
}

4.双边滤波

       双边滤波是一种非线性滤波器,它可以达到保持边缘、降噪平滑的效果。一定程度上拟补了高斯滤波的缺点。双边滤波对高斯噪声效果比较好。双边滤波从单纯的考虑空间域点的位置的高斯滤波上,又加上一个维度上的权重。在点云处理上,可以叫做为特征域,即当前点的法向量与临近点的法向量。

PCL代码如下:

#include <pcl/filters/fast_bilateral.h>
 
 
void Bilateral_Filter(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_input,
                      pcl::PointCloud<pcl::PointXYZI>::Ptr &result,
                      float sigma_s, float sigma_R )
{
  pcl::FastBilateralFilter<pcl::PointXYZI> filter;
  filter.setInputCloud(cloud_input);
  filter.setSigmaS(sigma_s);        //0.5 空间邻域/窗口设置双边滤波器使用的高斯标准差
  filter.setSigmaR(sigma_R);         //0.0004 设置高斯的标准偏差,用于控制由于强度差,而使相邻像素的权重降低的程度
  filter.applyFilter(*result);
}

5.统计滤波

     理论看了以下博主点云统计滤波理解-CSDN博客的,觉得他写的详细明了,其实就是运用统计,通过所有的点云计算一个均值和方差,再通过均值和方差得到一个距离,然后通过这个距离剔除掉一些点。

      统计滤波算法(Statistical Outlier Removal),基于点云数据中点到其邻域点距离的分布(有点类似最近邻算法)进行滤波,自我理解其实现步骤如下:
1、分别计算点云中各点k邻域距离的平均值,如假设一点云中有50个点,我设定条件为计算各点k邻域距离的平均值,则有50个平均值,各点k领域(以8领域为例)平均值计算方式如下图所示:

       作者认为,整个点云得到的50个平均值在分布上应该是呈高斯分布的,就可以得到均值μ和标准差σ,以μ+std_mul*σ为距离阈值,距离在区间之外的点便视为离群点,并可以在点云数据中去除,其中std_mul是用户指定的标准差倍数的阈值。如图2所示,圆形的半径为统计后根据设置所得距离阈值,则A点邻域有3个点被视为离群点,B、C点邻域各有1个点被视为离群点在滤波过程中被删除。

PCL代码如下:

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.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>);
 
	// 1.定义读取对象
	pcl::PCDReader reader;
	// 读取点云文件
	reader.read<pcl::PointXYZ>(argv[1], *cloud);
 
	std::cerr << "Cloud before filtering: " << std::endl;
	std::cerr << *cloud << std::endl;
 
	// 2.创建滤波器,对每个点分析的临近点的个数设置为50 ,并将标准差的倍数设置为1  这意味着如果一
	//个点的距离超出了平均距离一个标准差以上,则该点被标记为离群点,并将它移除,存储起来
	pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;  //创建滤波器对象
	sor.setInputCloud(cloud);                           //设置待滤波的点云
	sor.setMeanK(50);                               	//设置在进行统计时考虑查询点临近点数
	sor.setStddevMulThresh(1.0);                      	//设置判断是否为离群点的阀值
	sor.filter(*cloud_filtered);                    	//存储
  
	std::cerr << "Cloud after filtering: " << std::endl;
	std::cerr << *cloud_filtered << std::endl;
	
	//3.保存滤波后的点云
	pcl::PCDWriter writer;
	writer.write<pcl::PointXYZ>("after_filter.pcd", *cloud_filtered, false);
 
	//sor.setNegative(true);
	//sor.filter(*cloud_filtered);
	//writer.write<pcl::PointXYZ>("1_outliers.pcd", *cloud_filtered, false);
 
	return (0);
}

6.条件滤波

       条件滤波器通过设定滤波条件进行滤波,有点分段函数的味道,当点云在一定范围则留下,不在则舍弃,滤除离群点的一种滤波方法。

PCL代码如下:

//创建条件对象
pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond (new pcl::ConditionAnd<pcl::PointXYZ> ());
/*
//比较条件符号说明:
//GT greater than
//EQ equal
//LT less than
//GE greater than or equal
//LE less than or equal
*/
//添加比较条件1:z>0.0
range_cond->addComparison (pcl::FieldComparison<pcl::PointXYZ>::ConstPtr (new pcl::FieldComparison<pcl::PointXYZ> ("z", pcl::ComparisonOps::GT, 0.0)));
//添加比较条件2:z<0.8
range_cond->addComparison (pcl::FieldComparison<pcl::PointXYZ>::ConstPtr (new pcl::FieldComparison<pcl::PointXYZ> ("z", pcl::ComparisonOps::LT, 0.8)));


pcl::ConditionalRemoval<pcl::PointXYZ> condrem;	//创建条件滤波器对象
condrem.setCondition (range_cond);				//设置条件
condrem.setInputCloud (cloud);					//设置输入点云
condrem.setKeepOrganized(true);					//设置保持点云的结构
condrem.filter (*cloud_filtered);				//执行滤波

condrem.setKeepOrganized( )详解:
设置受保持点云的结构。
有序点云执行滤波之后,希望仍然能够保持有序性
若设置为true,再通过setuserFilterValue设置一个指定的值,被滤除的点将会被用户指定的值代替。
若设置为true,没有通过setuserFilterValue设置一个指定的值,则用nan填充被滤除的点
若设置为false,则直接将滤除的点删除,从而可能改变点云的组织结构
 

       通过以上滤波方法,我大概可以总结为:滤波的目的就是去除掉点云的噪点。其实就是以体素网格为主的降维或者以半径滤波为主的去除离散点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/330400.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Go后端开发 -- 反射reflect

Go后端开发 – 反射reflect && 结构体标签 文章目录 Go后端开发 -- 反射reflect && 结构体标签一、反射reflect1.编程语言中反射的概念2.interface 和反射3.变量内置的pair结构4.reflect的基本功能TypeOf和ValueOf5.从relfect.Value中获取接口interface的信息6…

SSL证书自动化管理有什么好处?如何实现SSL证书自动化?

SSL证书是用于加密网站与用户之间传输数据的关键元素&#xff0c;在维护网络安全方面&#xff0c;管理SSL证书与部署SSL证书一样重要。定期更新、监测和更换SSL证书&#xff0c;可以确保网站的安全性和合规性。而自动化管理可以为此节省时间&#xff0c;并避免人为错误和不必要…

React 基于Ant Degisn 实现table表格列表拖拽排序

效果图&#xff1a; 代码&#xff1a; myRow.js import { MenuOutlined } from ant-design/icons; import { DndContext } from dnd-kit/core; import { restrictToVerticalAxis } from dnd-kit/modifiers; import {arrayMove,SortableContext,useSortable,verticalListSorti…

【Java实战项目】基于ssm的数据结构课程网络学习平台

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Datawhale 强化学习笔记(二)马尔可夫过程,DQN 算法

文章目录 参考马尔可夫过程DQN 算法&#xff08;Deep Q-Network&#xff09;如何用神经网络来近似 Q 函数如何用梯度下降的方式更新网络参数强化学习 vs 深度学习 提高训练稳定性的技巧经验回放目标网络 代码实战 DQN 算法进阶Double DQNDueling DQN 算法代码实战 参考 在线阅…

计算机网络-计算机网络的概念 功能 发展阶段 组成 分类

文章目录 计算机网络的概念 功能 发展阶段总览计算机网络的概念计算机网络的功能计算机网络的发展计算机网络的发展-第一阶段计算机网络的发展-第二阶段-第三阶段计算机网络的发展-第三阶段-多层次ISP结构 小结 计算机网络的组成与分类计算机网络的组成计算机网络的分类小结 计…

RDMA原理浅析

1. DMA和RDMA概念 1.1 DMA DMA(直接内存访问)是一种能力&#xff0c;允许在计算机主板上的设备直接把数据发送到内存中去&#xff0c;数据搬运不需要CPU的参与。 传统内存访问需要通过CPU进行数据copy来移动数据&#xff0c;通过CPU将内存中的Buffer1移动到Buffer2中。DMA模式…

【Axure高保真原型】文字翻页效果

今天和大家分享选择文字翻页效果的原型模板&#xff0c;我们通过这个模板实现类似翻书的效果。鼠标点击右箭头&#xff0c;可以翻开下一页&#xff0c;点击左箭头翻开上一页&#xff1b;当然我们也可以通过鼠标拖动的操作进行翻页&#xff0c;鼠标想左拖动时&#xff0c;翻开下…

algotithm -- 排序算法

排序算法总结表&#xff1a; 1. In-place 和 Out-place 含义 参考链接 in-place 占用常数内存&#xff0c;不占用额外内存 假如问题规模是n&#xff0c;在解决问题过程中&#xff0c;只开辟了常数量的空间&#xff0c;与n无关&#xff0c;这是原址操作&#xff0c;就是In-…

Kotlin 移动端多平台

支持多平台编程是 Kotlin 的主要优势之一。它减少了为不同平台编写和维护相同代码所花费的时间&#xff0c;同时保留了本机编程的灵活性和优势。 1. 基本概念 KMM&#xff1a;Kotlin Multiplatform for mobile&#xff08;移动设备的 Kotlin 多平台&#xff09; KMM 多平台的主…

vue3有了解过吗?能说说跟vue2的区别吗?

一、Vue3介绍 关于vue3的重构背景&#xff0c;尤大是这样说的&#xff1a; 「Vue 新版本的理念成型于 2018 年末&#xff0c;当时 Vue 2 的代码库已经有两岁半了。比起通用软件的生命周期来这好像也没那么久&#xff0c;但在这段时期&#xff0c;前端世界已经今昔非比了 在我…

Redis 笔记一

概览 1.Redis核心数据存储结构 2.Redis底层String编码int&embstr&raw 3.Redis底层压缩列表&跳表&哈希表 4.Redis底层Zset实现压缩列表和跳表如何选择 5.基于Redis实现微博&抢红包&12306核心业务 辅助学习&#xff1a;Redis 教程 | 菜鸟教程 1.Redis为什…

web3.0基本概念简析

web3.0概念简析 web3.0的发展史 web1.0 仅用于展示&#xff0c;无法进行点赞评论等交互 web2.0 不仅可以展示&#xff0c;还可以上传视频、图片等&#xff0c;用户可以参与创作内容并获取收益。但还是中心化的模型 缺点 1 机械化的人机验证 2 账户安全无法保证 多年未登陆…

dp--64. 最小路径和/medium 理解度A

64. 最小路径和 1、题目2、题目分析3、复杂度最优解代码示例4、抽象与扩展 1、题目 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 示例 …

Dockerfile镜像实战

目录 一 构建SSH镜像 1.开启ip转发功能 2. 准备工作目录 3.修改配置文件 5.启动容器并修改root密码 二 构建Systemctl镜像 1. 准备工作目录 ​编辑2.修改配置文件 3.生成镜像 4.启动容器&#xff0c;并挂载宿主机目录挂载到容器中&#xff0c;进行初始化 5.进入容器 三…

C++三剑客之std::variant(二):深入剖析

目录 1.概述 2.辅助类介绍 2.1.std::negation 2.2.std::conjunction 2.3.std::is_destructible 2.4.std::is_object 2.5.is_default_constructible 2.6.std::is_trivially_destructible 2.7.std::in_place_type和std::in_place_index 3.原理分析 3.1.存储分析 3.2.…

【昕宝爸爸小模块】深入浅出之针对大Excel做文件读取问题

➡️博客首页 https://blog.csdn.net/Java_Yangxiaoyuan 欢迎优秀的你&#x1f44d;点赞、&#x1f5c2;️收藏、加❤️关注哦。 本文章CSDN首发&#xff0c;欢迎转载&#xff0c;要注明出处哦&#xff01; 先感谢优秀的你能认真的看完本文&…

VUE 中的 v-for 和 v-if 是否可以共存

VUE 中的 v-for 和 v-if 是否可以共存 前言1、面试经2、正确回答3、总结总结&#xff1a; 前言 要成功&#xff0c;先发疯&#xff0c;头脑简单往前冲&#xff01; 三金四银&#xff0c;金九银十&#xff0c;多学知识&#xff0c;也不能埋头苦干&#xff0c;要成功&#xff0c…

uniapp中uview组件库的NoticeBar 滚动通知 使用方法

目录 #平台差异说明 #基本使用 #配置主题 #配置图标 #配置滚动速度 #控制滚动的开始和暂停 #事件回调 #API #Props #Events 该组件用于滚动通告场景&#xff0c;有多种模式可供选择 #平台差异说明 AppH5微信小程序支付宝小程序百度小程序头条小程序QQ小程序√√√√…

2018年认证杯SPSSPRO杯数学建模B题(第一阶段)动态模糊图像全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 B题 动态模糊图像 原题再现&#xff1a; 人眼由于存在视觉暂留效应&#xff0c;所以看运动的物体时&#xff0c;看到的每一帧画面都包含了一段时间内 (大约 1/24 秒) 的运动过程&#xff0c;所以这帧画面事实上是模糊的。对电影的截图来说&…
最新文章