《点云处理》 点云去噪

news2024/9/29 13:27:43

前言

通常从传感器(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.coloradmin.cn/o/1323058.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

原子学习笔记2——输入设备应用编程

一、输入类设备介绍 1、输入设备 常见的输入设备有鼠标、键盘、触摸屏、遥控器、电脑画图板等&#xff0c;用户通过输入设备与系统进行交互。 2、input子系统 常见的输入设备有鼠标、键盘、触摸屏、遥控器、电脑画图板等&#xff0c;用户通过输入设备与系统进行交互。 基于…

docker 在线安装redis

1、远程仓库拉取redis镜像&#xff0c; docker pull redis&#xff0c;默认拉取最新版本 2、在本地宿主机文件夹下创建相关目录文件&#xff0c;供容器卷使用&#xff0c;创建 /usr/local/data/redisdocker/data 文件夹&#xff0c;准备一个纯净版 redis.conf 配置文件 &#x…

【Linux】ip命令使用

ip命令 用于管理与配置网络接口和路由表。 ip命令的安装 ip 命令来自 iproute2 软件包&#xff0c;在 CentOS 7 中默认已安装。 yum install -y iproute 语法 ip [ OPTIONS ] OBJECT { COMMAND | help }ip [ -force ] -batch filename选项及作用 执行令 &#xff1a; ip …

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

1.写在之前 本文采用Spring Boot MinIO MySQLMybatis Plus技术栈&#xff0c;参考ruoyi-vue-pro项目。 前端实现请看本篇文章el-form与el-upload结合上传带附件的表单数据&#xff08;前端篇&#xff09;-CSDN博客。 2.需求描述 在OA办公系统中&#xff0c;流程表单申请人…

【SQL】根据年月,查询月份中每一天的数据量

传入YYYY-MM-01&#xff0c;查询这个月中每一天的数据量&#xff0c;没有数据的天数用0表示 WITH RECURSIVE DateRange AS (SELECT :startDate AS DateUNION ALLSELECT DATE_ADD(Date, INTERVAL 1 DAY) FROM DateRangeWHERE Date < LAST_DAY(:startDate) ) SELECTdr.Date,CO…

从 MySQL 到 DolphinDB,Debezium + Kafka 数据同步实战

Debezium 是一个开源的分布式平台&#xff0c;用于实时捕获和发布数据库更改事件。它可以将关系型数据库&#xff08;如 MySQL、PostgreSQL、Oracle 等&#xff09;的变更事件转化为可观察的流数据&#xff0c;以供其他应用程序实时消费和处理。本文中我们将采用 Debezium 与 K…

单总线cpu设计(包含定长指令周期和变长指令周期和现代时序设计)

来都来了点个赞收藏一下再走呗~~~&#x1f339;&#x1f339;&#x1f339;&#x1f339;&#x1f339; 一、定长指令周期cpu设计 第1关&#xff1a;MIPS指令译码器设计 此实验就是只需要知道mips知道操作码op对应的值是什么就可以了&#xff0c;下面给出实验中用到的mips指令…

大型语言模型:RoBERTa — 一种稳健优化的 BERT 方法

slavahead 一、介绍 BERT模型的出现BERT模型带来了NLP的重大进展。 BERT 的架构源自 Transformer&#xff0c;它在各种下游任务上取得了最先进的结果&#xff1a;语言建模、下一句预测、问答、NER标记等。 尽管 BERT 性能出色&#xff0c;研究人员仍在继续尝试其配置&#xff0…

JNDI注入Log4jFastJson白盒审计不回显处理

目录 0x00 前言 0x01 Maven 仓库及配置 0x02 JNDI 注入简介 0x03 Java-第三方组件-Log4J&JNDI 0x04 Java-第三方组件-FastJson&反射 0x05 白盒审计 - FastJson 0x06 白盒审计 - Log4j 0x07 不回显的处理方法 0x00 前言 希望和各位大佬一起学习&#xff0c;如果…

RK3399平台开发系列讲解(内核入门篇)网络协议的分层

🚀返回专栏总目录 文章目录 一、应用层二、传输层三、网络层四、数据链路层(Data Link Layer)五、物理层沉淀、分享、成长,让自己和他人都能有所收获!😄 📢对于多数的应用和用户而言,使用互联网的一个基本要求就是数据可以无损地到达。用户通过应用进行网络通信࿰

Android的组件、布局学习

介绍 公司组织架构调整&#xff0c;项目组需要承接其他项目组的android项目&#xff0c;负责维护和开发新需求&#xff0c;故学习下基础语法和项目开发。 组件学习 Toolbarheader布局部分 就是app最顶部的部分 他的显示与否&#xff0c;是与F:\androidProject\android_lear…

LVGL 显示图片

LVGL 显示图片 LVGL显示图片1. 显示图片文件2. 显示C数组格式3. 显示RAM中的图像文件4. 图像符号显示5. 显示GIF动画LVGL显示图片代码分析 LVGL显示图片 lvgl 8.3版本默认支持PNG,BMP,JPG,SJPG和GIF动图等格式的图片显示&#xff1b; 需要在lv_conf.h配置文件里使能对应图片的…

【网络安全】—Shell编程入门(1)

文章目录 基础变量概念介绍特殊变量进阶数值计算实践条件测试比较条件判断语句流程控制语句循环语句应用 Shell 是 Unix/Linux 操作系统下的一种命令行解释器&#xff0c;它接收用户输入的命令然后调用相应的程序。我们可以通过 Shell 脚本来自动执行一系列的命令。接下来&…

基于python的leetcode算法介绍之递归

文章目录 零 算法介绍一 简单示例 辗转相除法Leetcode例题与思路[509. 斐波那契数](https://leetcode.cn/problems/fibonacci-number/)解题思路&#xff1a;题解&#xff1a; [206. 反转链表](https://leetcode.cn/problems/reverse-linked-list/)解题思路&#xff1a;题解&…

最新AI创作系统ChatGPT系统源码+DALL-E3文生图+AI绘画+GPT语音对话功能

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

计算机网络 网络层下 | IPv6 路由选择协议,P多播,虚拟专用网络VPN,MPLS多协议标签

文章目录 5 IPv65.1 组成5.2 IPv6地址5.3 从IPv4向IPv6过渡5.3.1 双协议栈5.3.2 隧道技术 6 因特网的路由选择协议6.1 内部网关协议RIP6.2 内部网关协议 OSPF基本特点 6.3 外部网关协议 BGP6.3.1 路由选择 6.4 路由器组成6.4.1 基本了解6.4.2 结构 7 IP多播7.1 硬件多播7.2 IP多…

vp与vs联合开发-通过FrameGrabber连接相机

添加控件 1.CogRecordDisplay 控件 用于显示图像 初始化相机对象方法 //启动窗体时 调用初始化相机方法 //封装相机关闭方法 //窗体关闭时 调用相机关闭方法 拍照 设置采图事件 // 保存图像 设置曝光按钮事件 1.可变参数

C# pictureBox显示一张图片,我想先释放这个图片以免占用无法修改,(旋转)改完再显示这张图片

效果 public static bool RotateFlip(MyDel Log, string fileName){try{string tempPath Path.GetTempFileName();using (Bitmap bmp new Bitmap(fileName)){float resolution 600; //x,y必须为这个数 误差小于-1bmp.RotateFlip(RotateFlipType.Rotate90FlipNone);bmp.Save(…

智能优化算法应用:基于冠状病毒群体免疫算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于冠状病毒群体免疫算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于冠状病毒群体免疫算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.冠状病毒群体免疫算法4.…

飞天使-k8s-知识点1-kubernetes架构简述

文章目录 名词功能要点 k8s核心要素CNCF 云原生框架简介k8s组建介绍 名词 CI 持续集成, 自动化构建和测试&#xff1a;通过使用自动化构建工具和自动化测试套件&#xff0c;持续集成可以帮助开发人员自动构建和测试他们的代码。这样可以快速检测到潜在的问题&#xff0c;并及早…