《点云处理》 点云去噪

前言

通常从传感器(3D相机、雷达)中获取到的点云存在噪点(杂点、离群点、孤岛点等各种叫法)。噪点产生的原因有不同,可能是扫描到了不想要扫描的物体,可能是待测工件表面反光形成的,也可能是相机内部的原因。在进行其他算法处理之前,通常需要先去除噪声,避免带来干扰。去噪的方法有很多,效率和效果也是各不相同,应用场景也不太一样,本篇内容就是想要将不同的去噪方法进行归纳。

环境:

Windows + VS2019 + PCL1.11.1

在开始之前,先贴出用于测试的点云,可以看出来空间中漂浮了很多噪点。原始点云总数为5882482个点,x和y方向间距为0.05。
在这里插入图片描述

1.半径滤波

PCL中集成有半径滤波,可以用于噪点的去除。主要需要设定两个参数,一个是搜索半径,另一个是在搜索半径内近邻点的最小数量。这两个参数需要根据点云的x,y,z方向上的点间距来设定(或者说分辨率)。一般使用线扫相机或者结构光相机这类3D相机得到的点在x,y方向上间距都比较均匀。假设点间距是0.05,那么将搜索半径设置为0.1,则搜索半径内理论上在上下左右方向上分别有2个点,共8个点,所以近邻点最起码也有8个。

头文件:
#include <pcl/filters/radius_outlier_removal.h>

代码如下:

/// <summary>
/// 使用半径滤波进行点云噪点去除
/// </summary>
/// <param name="cloud">输入点云</param>
/// <param name="cloud_out">输出除噪后的点云</param>
/// <param name="radius">搜索半径</param>
/// <param name="neighbors">单个搜索内近邻点数</param>
/// <returns>return true表示去噪成功,return false表示失败</returns>
bool NoiseRemoveROR(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_out, const float& radius,
	const int& neighbors)
{
	if (cloud == nullptr) return false;
	if (cloud->points.size() < 10) return false;

	pcl::RadiusOutlierRemoval<pcl::PointXYZ> rorfilter(true); // 初始化为true以获取被移除的索引
	rorfilter.setInputCloud(cloud);
	rorfilter.setRadiusSearch(radius);                 // 设置搜索半径
	rorfilter.setMinNeighborsInRadius(neighbors);      // 设置搜索半径内满足的点数
	//rorfilter.setUserFilterValue(1);                 // 将不符合要求的点更改为新的值,而不是去去除它们,与setKeepOrganized一起使用
	//rorfilter.setKeepOrganized(false);               // 保证有序点云的结构,与setUserFilterValue一起使用
	rorfilter.filter(*cloud_out);                      // 进行离群点去除
	return true;
}

一般近邻点的数量设置与搜索半径有直接关系。而经过测试发现,搜索的近邻点数量需要越多则耗时越长。较小的搜索半径只需要设置较小数量的近邻点,但是这样可能会导致有的噪点去除不干净。稍大一点的搜索半径以及近邻点数量适当调大可以得到更稳定的去噪效果。因此,效果和效率之间需要找到一个平衡点。
测试及结果:搜索半径设置为0.2,近邻点数量为20,耗时10.94s。效果如下:
在这里插入图片描述
针对于半径滤波耗时问题其实可以将点云使用直通滤波分成若干份,然后使用OpenMP对这几份点云并行处理进行半径滤波,将结果再合并成一个点云,这个方法可能能够起到加速的作用。

有一篇论文《Fast Radius Outlier Filter Variant for Large Point Clouds》,更快速的半径滤波,还未找到源码,若有人找到请联系我,感谢。

2.统计学滤波

统计学滤波也是一种比较常见的去噪算法。其官方解释如下:
The statistical outlier removal process is a bit more refined. First, for every point, the mean distance to its K neighbors is computed. Then, if we asume that the result is a normal (gaussian) distribution with a mean μ and a standard deviation σ, we can deem it safe to remove all points with mean distances that fall out of the global mean plus deviation. Basically, it runs a statistical analysis of the distances between neighboring points, and trims all which are not considered “normal” (you define what “normal” is with the parameters of the algorithm).

个人理解就是遍历点云,对于每个点,都先搜索与其最相近的k个点,计算这k个点与该点的距离,并得到一个平均距离和一个平均距离的标准差。然后比较距离是否是大于μ+stddev*σ,如果大于则表示是离群点。

头文件:#include <pcl/filters/statistical_outlier_removal.h>

代码如下:

/// <summary>
/// 使用PCL中集成的统计学滤波进行去噪
/// </summary>
/// <param name="cloud">输入点云</param>
/// <param name="cloud_out">输出点云</param>
/// <param name="k">搜索近邻点的个数</param>
/// <param name="stddev">平均距离标准差的乘数</param>
/// <returns>return true表示去噪成功,return false表示失败</returns>
bool NoiseRemoveSOR(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_out, const int& k, const double& stddev)
{
	if (cloud == nullptr) return false;
	if (cloud->points.size() < 10) return false;
	pcl::StatisticalOutlierRemoval<pcl::PointXYZ> filter; // Filter object.
	filter.setInputCloud(cloud);
	filter.setMeanK(k);                  // Set number of neighbors to consider to K.
	filter.setStddevMulThresh(stddev);   // Points with a distance larger than stddev standard deviation of the mean distance will be outliers.
	filter.filter(*cloud_out);
	return true;
}

使用这个统计学滤波感觉还是十分耗时,但是效果确实是不错的。
测试及结果:近邻点k值的数量为20,系数设定为1.0,耗时11.07s。效果如下:
在这里插入图片描述
有一篇论文《Fast statistical outlier removal based method for large 3d point clouds of outdoor environments》,根据其题目理解来说是一种更快的统计学滤波方法去除离群点,论文还没看,源码也没找到,谁能找到麻烦联系我,感谢!

3.RANSAC

没错,就是RANSAC。使用RANSAC去去噪需要满足条件,那就是目标点云是具有几何特征的。如果目标点云是一个平面,那么就可以使用RANSAC拟合一个平面,并且将距离平面较远的点(外点)去除。这样当然也可以达到去噪的效果,而且速度还比较快。

头文件:
#include <pcl/kdtree/kdtree.h>
#include <pcl/segmentation/sac_segmentation.h>

/// <summary>
/// 使用PCL中集成的用于点云分割的RANSAC方法进行平面拟合
/// </summary>
/// <param name="cloud_in">输入待拟合的点云</param>
/// <param name="inliers">RANSAC拟合得到的内点</param>
/// <param name="coefficients">得到的平面方程参数</param>
/// <param name="iterations">平面拟合最大迭代次数</param>
/// <param name="threshold">RANSAC拟合算法距离阈值</param>
void SEG_RANSAC(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_in, pcl::PointIndices::Ptr& inliers, Eigen::VectorXf& coefficients,
	const int& iterations, const double& threshold)
{
	if (inliers == nullptr) inliers.reset(new pcl::PointIndices);
	pcl::ModelCoefficients::Ptr coefficients_m(new pcl::ModelCoefficients);
	pcl::shared_ptr<pcl::search::KdTree<pcl::PointXYZ>> tree(new pcl::search::KdTree<pcl::PointXYZ>);
	tree->setInputCloud(cloud_in);
	pcl::SACSegmentation<pcl::PointXYZ> seg;
	seg.setOptimizeCoefficients(true);
	seg.setModelType(pcl::SACMODEL_PLANE);
	seg.setMethodType(pcl::SAC_RANSAC);
	seg.setMaxIterations(iterations);                     // 设置最大迭代次数
	seg.setDistanceThreshold(threshold);                  // 设定阈值
	seg.setNumberOfThreads(10);                           // 设置线程数量
	seg.setSamplesMaxDist(3, tree);
	seg.setInputCloud(cloud_in);
	seg.segment(*inliers, *coefficients_m);

	coefficients.resize(4);
	coefficients[0] = coefficients_m->values[0]; coefficients[1] = coefficients_m->values[1];
	coefficients[2] = coefficients_m->values[2]; coefficients[3] = coefficients_m->values[3];
	std::cout << "SEG coefficients: " << coefficients[0] << ", " << coefficients[1] << ", " << coefficients[2] << ", " << coefficients[3] << std::endl;
}

运行时间只需要1.5s,迭代了200次。速度比半径滤波和统计学滤波要快很多。但是这个方法也是很有局限性的,只适合特定点云才能用,而且如下图所示,距离平面较近的噪点无法去除,所以是存在去除噪点不干净的情况,如果追求速度,而对去噪要求没那么高,则可以考虑使用该方法。
在这里插入图片描述

4.欧式聚类

欧式聚类既可以用于分割,也可以用于去噪,其实跟上述半径滤波区别不大。噪点肯定是距离想要的点云比较“远”的,设置好minSize,把想要的点聚成一个类,噪点自然就去除了。

代码如下:

/// <summary>
/// PCL中集成的欧式聚类
/// </summary>
/// <param name="cloud">输入点云</param>
/// <param name="cluster_indices">聚类索引的数组</param>
/// <param name="tolerance ">距离阈值</param>
/// <param name="MinClusterSize">单个类最少的点数</param>
/// <param name="MaxClusterSize">单个类最大的点数</param>
/// <returns>return true表示有聚类结果,return false表示聚类失败</returns>
bool NoiseRemoveEC(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, std::vector<pcl::PointIndices>& cluster_indices, const double& tolerance,
	const int& MinClusterSize, const int& MaxClusterSize)
{
	if (cloud == nullptr) return false;
	if (cloud->points.size() < 10) return false;
	int maxSize = MaxClusterSize;
	if (maxSize < 0) maxSize = cloud->points.size();
	if (MinClusterSize > maxSize) return false;

	pcl::shared_ptr<pcl::search::KdTree<pcl::PointXYZ>> tree(new pcl::search::KdTree<pcl::PointXYZ>); // Creating the KdTree object for the search method of the extraction
	tree->setInputCloud(cloud);

	cluster_indices.clear();
	pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;    // 欧式聚类对象
	ec.setClusterTolerance(tolerance);                       // 设置近邻搜索的搜索半径为  单位是m
	ec.setMinClusterSize(MinClusterSize);                 // 设置一个聚类需要的最少的点数目
	ec.setMaxClusterSize(maxSize);                        // 设置一个聚类需要的最大点数目
	ec.setSearchMethod(tree);                             // 设置点云的搜索机制
	ec.setInputCloud(cloud);                              // 输入聚类点云
	ec.extract(cluster_indices);                          // 从点云中提取聚类,并将点云索引保存在cluster_indices中

	if (cluster_indices.empty()) return false;
	return true;
}

设置的距离阈值为0.1,是两倍的点距大小(x,y方向点距都是0.05)。耗时10.9s。当距离阈值tolerance设置的比较大时,就会特别耗时,该方法就几乎不可用了。

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

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

相关文章

Axure中继器完成表格的增删改查的自定义元件(三列表格与十列表格)

目录 一、中继器 1.1 定义 1.2 特点 1.3 适用场景 二、三列表格增删改查 2.1 实现思路 2.2 效果演示 三、十列表格增删改查 3.1 实现思路 3.2 效果演示 一、中继器 1.1 定义 在Axure中&#xff0c;"中继器"通常指的是界面设计中的一个元素&#xff0c;用…

第二百一十五回 如何创建单例模式

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 实现方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"分享三个使用TextField的细节"沉浸式状态样相关的内容&#xff0c;本章回中将介绍 如何创建单例模式.闲话休提&#xff0c;让我们一起Talk Flutter吧。 …

03-数据结构-栈与队列

1.栈 栈和队列是两种操作受限的线性表。如上图所示显示栈的结构 栈&#xff1a;先进后出&#xff0c;入栈&#xff08;数据进入&#xff09; 和出栈&#xff08;数据出去&#xff09;均在栈顶操作。 常见栈的应用场景包括括号问题的求解&#xff0c;表达式的转换和求值&#…

记录每日LeetCode 162.寻找峰值与1901.寻找峰值II Java实现

寻找峰值 题目描述&#xff1a; 峰值元素是指其值严格大于左右相邻值的元素。 给你一个整数数组 nums&#xff0c;找到峰值元素并返回其索引。数组可能包含多个峰值&#xff0c;在这种情况下&#xff0c;返回 任何一个峰值 所在位置即可。 你可以假设 nums[-1] nums[n] -…

Unity3d C#利用Editor编辑器拓展实现配置UI背景样式一键设置UI背景样式功能(含源码)

前言 在开发UI滚动列表的时候&#xff0c;经常会有每项的背景图不统一的情况&#xff0c;会间隔重复的情况居多。这种情况下&#xff0c;手动去设置间隔一行的背景图或者颜色是比较麻烦的。在此背景下&#xff0c;笔者尝试写个小工具&#xff0c;在搭建UI时配置一下循环背景的…

开发知识点-09Rust

Rust Rust 语言通常用于编写系统级软件、网络服务器和高性能应用程序&#xff0c;它具有以下特点&#xff1a;1. 高性能和内存安全&#xff1a;Rust 在保证高性能的同时&#xff0c;利用其所有权模型和借用检查器等特性确保内存安全&#xff0c;避免了 C/C 等语言的内存错误和崩…

MySQL数据库——SQL语法

Structured Query Language&#xff08;结构化查询语言&#xff09;&#xff0c;简称SQL&#xff0c;是用于操作关系型数据库的标准编程语言。SQL提供了一种与数据库交互的方式&#xff0c;可以用于查询、插入、更新和删除数据库中的数据。 1. SQL通用语法 SQL语句可以写在一…

Jenkins Docker Cloud在Linux应用开发CI中的实践

Jenkins Docker Cloud在Linux应用开发CI中的实践 背景 通过代码提交自动触发CI自动构建、编译、打包是任何软件开发组织必不可少的基建&#xff0c;可以最大程度保证产物的一致性&#xff0c;方便跨组跨部门协作&#xff0c;代码MR等。 Docker在流水线中越来越重要&#xff…

行为型设计模式(一)模版方法模式 迭代器模式

模板方法模式 Template 1、什么是模版方法模式 模版方法模式定义了一个算法的骨架&#xff0c;它将其中一些步骤的实现推迟到子类里面&#xff0c;使得子类可以在不改变算法结构的情况下重新定义算法中的某些步骤。 2、为什么使用模版方法模式 封装不变部分&#xff1a;模版…

Home Assistant HAOS版如何安装HACS

环境&#xff1a; Home Assistant 11.2 SSH & Web Terminal 17.0 问题描述&#xff1a; Home Assistant HAOS版如何安装HACS 解决方案&#xff1a; 1.打开WEB 里面的终端输入下面命令 wget -O - https://hacs.vip/get | bash -如果上面的命令执行后卡住不动&#xff…

得物-Golang-记一次线上服务的内存泄露排查

1.出现内存泄漏 1.1 事发现场 在风和日丽的一天&#xff0c;本人正看着需求、敲着代码&#xff0c;展望美好的未来。突然收到一条内存使用率过高的告警。 1.2 证人证词 告警的这个项目&#xff0c;老代码是python的&#xff0c;最近一直在go化。随着go化率不断上升&#xff…

maven 项目导入异常问题

问题如下 一、 tomcat正再运行的包是哪一个 不同构建、打包情况下分别运行 out\artifacts下 当直接去Project Structure下去构建artifacts 后&#xff0c;运行tomcat 则会在out下target下 reimport项目后,则会在artifacts自动生成部署包。删除tomcat之前deployment 下的包(同…

Android studio Android SDK下载安装

我们访问地址 https://developer.android.google.cn/studio?hlzh-cn 拉下来直接点击下载 然后来下来 勾选 然后点击下载 下载好之后 我们双击打开 点击下一步 确认上面的勾选 然后下一步 这里 我们选择一下安装目录 然后点击下一步 安装 安装完之后点击进行下一步 Fin…

JDK各个版本新特性

JDK8新特性 Java 8 发布于 2014 年 3 月份&#xff0c;可以说是 Java 6 之后最重要的版本更新&#xff0c;深受开发者的喜爱。 函数式编程和 Lambda 表达式 Stream 流 参考&#xff1a;https://mp.weixin.qq.com/s/7hNUjjmqKcHDtymsfG_Gtw 单从“Stream”这个单词上来看&…

【数据结构】栈的使用|模拟实现|应用|栈与虚拟机栈和栈帧的区别

目录 一、栈(Stack) 1.1 概念 1.2 栈的使用 1.3 栈的模拟实现 1.4 栈的应用场景 1. 改变元素的序列 2. 将递归转化为循环 3. 括号匹配 4. 逆波兰表达式求值 5. 出栈入栈次序匹配 6. 最小栈 1.5 概念区分 一、栈(Stack) 1.1 概念 栈&#xff1a;一种特殊的线性表&…

解决腾讯云CentOS 6硬盘空间不足问题:从快照到数据迁移

引言&#xff1a; 随着数据的不断增加&#xff0c;服务器硬盘空间不足变成了许多运维人员必须面对的问题。此主机运行了httpd&#xff08;apache服务&#xff09;&#xff0c;提供对外web访问服务,web资源挂载在**/data/wwwroot目录下,http日志存放在/data/wwwlogs目录下&…

创建型设计模式 | 原型模式

一、原型模式 1、原理 原型模式&#xff0c;用原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象&#xff0c;而且不需要知道任何创建的细节。原型像是一个模板&#xff0c;可以基于它复制好多…

el-form与el-upload结合上传带附件的表单数据(前端篇)

1.写在之前 本文前端采用Vue element-plus技术栈&#xff0c;前端项目参考yudao-ui-admin-vue3项目与Geeker-Admin项目。 这篇文章是el-form与el-upload结合上传带附件的表单数据&#xff08;后端篇&#xff09;-CSDN博客姐妹篇&#xff0c;后端篇文章主要讲的是后端的实现逻…

左移运算符(<<),右移运算符(>>)

#include <stdio.h>int main() {int a 10, b 10, c -10, d -10;int a1 0, a2 0, a3 0, a4 0, a5 0; int b1 0, b2 0, b3 0, b4 0, b5 0; int c1 0, c2 0, c3 0, c4 0, c5 0; int d1 0, d2 0, d3 0, d4 0, d5 0;//a10,左移a1 a << 1; a2…

JDK各个版本特性讲解-JDK14特性

JDK各个版本特性讲解-JDK14特性 一、Java14概述二、语法层面的变化1. instanceof2. switch表达式3. 文本块的改进4. Records记录类型 二、关于GC1.G1的NUMA内存分配优化2. 弃用SerialCMS,ParNewSerial Old3.删除CMS4.ZGC on macOS and Windows 三、其他变化1.友好的空指针异常提…
最新文章