当前位置: 首页 > news >正文

PCL点云库入门——PCL库点云滤波算法之统计滤波(StatisticalOutlierRemoval)

1、算法原理

        统计滤波算法是一种利用统计学原理对点云数据进行处理的方法。它主要通过计算点云中每个点的统计特性,如均值、方差等,来决定是否保留该点。算法首先会设定一个统计阈值,然后对点云中的每个点进行分析。如果一个点的统计特性与周围点的统计特性相差不大,即认为该点是可靠的,并将其保留;反之,如果一个点的统计特性与周围点相比差异较大,那么这个点可能是一个异常点或噪声点,算法会将其移除。通过这种方式,统计滤波算法能够有效地减少数据中的噪声和异常值,提高点云数据的质量。这种算法特别适用于需要对数据进行精细处理的场合,例如在高精度建模和测量中。由于其能够提供较为精确的数据处理结果,统计滤波算法在点云数据处理领域中也占有一席之地。算法的实现步骤如下:

        第1步:创建点云数据的拓扑结构,如KDtree或Octree。

        第2步:计算每一个点的K邻域集合。

        第3步:计算每个点与其K邻域的距离dij的均值,其中i=[1,....,m] 表示点云数目,j=[1,....,k] 点的K邻域点。

        第4步:计算点云数据高斯分布模型参数d~N(u,α),距离的均值u,距离的标准差α,具体公式内容如下:

         第5步:计算统计滤波的阈值。

                                        dt=  u+ S  ; ​​​

          第6步:遍历所有点,如果其距离的均值大于指定置阈值,则为外点,将会被移除。

2、主要成员函数和变量

        1.主要的成员变量

        1)、 构建点云空间拓扑的方法,如KDtree和Octree

       SearcherPtr searcher_;

         2)、搜索点K邻域的个数,用于计算点的平均距离信息

       int mean_k_;

         3)、标准差修正因子

       double std_mul_;

        2.主要的成员函数 

        1)、设置搜索点K邻域的个数,用于计算点的平均距离,可以根据直接点云数据分布情况设置一般值15、30、50等。

         inline void setMeanK (int nr_k);

        2)、标准差修正因子 ,如0.1、0.3和0.5等。

         inline void setStddevMulThresh (double stddev_mult)

3、主要实现代码注解 

template <typename PointT> void
pcl::StatisticalOutlierRemoval<PointT>::applyFilterIndices (std::vector<int> &indices)
{// 初始化构建点云拓扑关系方法if (!searcher_){if (input_->isOrganized ())//有序点云searcher_.reset (new pcl::search::OrganizedNeighbor<PointT> ());else //无序点云数据用KD树方法searcher_.reset (new pcl::search::KdTree<PointT> (false));}searcher_->setInputCloud (input_);// 初始化需要的参数,K个邻域的索引集、距离集;所有点云数据的距离集std::vector<int> nn_indices (mean_k_);std::vector<float> nn_dists (mean_k_);std::vector<float> distances (indices_->size ());//内点、外点索引集初始化indices.resize (indices_->size ());removed_indices_->resize (indices_->size ());int oii = 0, rii = 0;  // oii = output indices iterator, rii = removed indices iterator//第一步:计算所有点相对于k个最近邻域点的平均距离int valid_distances = 0;for (int iii = 0; iii < static_cast<int> (indices_->size ()); ++iii)  // iii = input indices iterator{//无效点判断if (!std::isfinite (input_->points[(*indices_)[iii]].x) ||!std::isfinite (input_->points[(*indices_)[iii]].y) ||!std::isfinite (input_->points[(*indices_)[iii]].z)){//无序点的距离为0distances[iii] = 0.0;continue;}// P执行K个邻域点搜索if (searcher_->nearestKSearch ((*indices_)[iii], mean_k_ + 1, nn_indices, nn_dists) == 0){distances[iii] = 0.0;PCL_WARN ("[pcl::%s::applyFilter] Searching for the closest %d neighbors failed.\n", getClassName ().c_str (), mean_k_);continue;}// 计算点与K个邻域点之间距离的平均值double dist_sum = 0.0;for (int k = 1; k < mean_k_ + 1; ++k)  // k = 0 is the query pointdist_sum += sqrt (nn_dists[k]);//将距离均值信息保存到对应索引值中  distances[iii] = static_cast<float> (dist_sum / mean_k_);valid_distances++;}// 估计距离的均值和标准差(u,a)double sum = 0, sq_sum = 0;for (const float &distance : distances){sum += distance;sq_sum += distance * distance;}//均值double mean = sum / static_cast<double>(valid_distances);//方差---》前半部分为母体方差,double variance = (sq_sum - sum * sum / static_cast<double>(valid_distances)) / (static_cast<double>(valid_distances) - 1);//标准差double stddev = sqrt (variance);//getMeanStd (distances, mean, stddev);// 计算外点阈值 ,等于=均值+标准差的修正系数double distance_threshold = mean + std_mul_ * stddev;// 第二步:根据计算的距离阈值和每个点的距离值(分辨率)对点进行分类(内点和外点)for (int iii = 0; iii < static_cast<int> (indices_->size ()); ++iii)  // iii = input indices iterator{//平均距离太高的点是离群值,传递给移除的索引if ((!negative_ && distances[iii] > distance_threshold) || (negative_ && distances[iii] <= distance_threshold)){if (extract_removed_indices_)(*removed_indices_)[rii++] = (*indices_)[iii];continue;}// Otherwise it was a normal point for output (inlier)indices[oii++] = (*indices_)[iii];}// Resize the output arraysindices.resize (oii);removed_indices_->resize (rii);
}

4、代码示例

/*****************************************************************//**
* \file   PCLPCLRadiusOutliermain.cpp
* \brief
*
* \author YZS
* \date   January 2025
*********************************************************************/
#include<iostream>
#include <vector>
#include <ctime>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/auto_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/filters/statistical_outlier_removal.h>
using namespace std;void PCLStatisticalOutlier()
{pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new  pcl::PointCloud<pcl::PointXYZ>());std::string fileName = "E:/PCLlearnData/11/table.pcd";pcl::io::load(fileName, *cloud);std::cout << "Cloud Size:" << cloud->points.size() << std::endl;//保存滤波后的结果点云pcl::PointCloud<pcl::PointXYZ>::Ptr cloudFilter(new  pcl::PointCloud<pcl::PointXYZ>());//统计滤波算法pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sorFilter;// 统计滤波器对象sorFilter.setInputCloud(cloud);sorFilter.setMeanK(30);//设置搜索点K邻域的个数sorFilter.setStddevMulThresh(0.3);//设置标准差修正因子 ,如0.1、0.3和0.5等sorFilter.filter(*cloudFilter); // 执行滤波,并且保存结果到cloudFilter中std::cout << "filter Cloud Size:" << cloudFilter->points.size() << std::endl;//结果可视化
// PCLVisualizer对象pcl::visualization::PCLVisualizer viewer("FilterVIS");//创建左右窗口的ID v1和v2int v1(0);int v2(1);//设置V1窗口尺寸和背景颜色viewer.createViewPort(0.0, 0.0, 0.5, 1, v1);viewer.setBackgroundColor(0, 0, 0, v1);//设置V2窗口尺寸和背景颜色viewer.createViewPort(0.5, 0.0, 1, 1, v2);viewer.setBackgroundColor(0.1, 0.1, 0.1, v2);// 添加2d文字标签viewer.addText("v1", 10, 10, 20, 1, 0, 0, "Txtv1", v1);viewer.addText("v2", 10, 10, 20, 0, 1, 0, "Txtv2", v2);//设置cloud1的渲染属性,点云的ID和指定可视化窗口v1viewer.addPointCloud(cloud, "cloud1", v1);viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "cloud1");//设置cloud2的渲染属性,点云的ID和指定可视化窗口v2viewer.addPointCloud(cloudFilter, "cloud2", v2);viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "cloud2");// 可视化循环主体while (!viewer.wasStopped()){viewer.spinOnce();}
}
int main(int argc, char* argv[])
{PCLStatisticalOutlier();std::cout << "Hello World!" << std::endl;std::system("pause");return 0;
}

结果:

 

5、统计滤波算法缺点

        1)、计算每一个点的邻域集合时,可能会因为数据集的复杂性导致邻域集合的计算不准确,从而影响后续的距离计算和滤波效果。

        2)、在计算每个点与其邻域的距离dij时,如果点云数据中存在噪声或异常值,这些异常值可能会扭曲距离的计算,导致滤波结果不理想。

        3)、高斯分布模型参数的计算依赖于距离的均值u和标准差α,如果数据集中的离群值较多,可能会使得均值和标准差的估计不准确,进而影响到离群值的识别和删除。

        4)、遍历所有点并移除外点的过程中,如果设定的置信度阈值过高或过低,可能会导致过多的正常点被错误地识别为离群点而被移除,或者真正的离群点没有被有效识别,从而影响数据集的质量。

 至此完成第十二节PCL库点云滤波算法之统计滤波(StatisticalOutlierRemoval)学习,下一节我们将进入《PCL库中点云特征》的学习。 

相关文章:

PCL点云库入门——PCL库点云滤波算法之统计滤波(StatisticalOutlierRemoval)

1、算法原理 统计滤波算法是一种利用统计学原理对点云数据进行处理的方法。它主要通过计算点云中每个点的统计特性&#xff0c;如均值、方差等&#xff0c;来决定是否保留该点。算法首先会设定一个统计阈值&#xff0c;然后对点云中的每个点进行分析。如果一个点的统计特性与周…...

【机器学习】Kaggle实战信用卡反欺诈预测(场景解析、数据预处理、特征工程、模型训练、模型评估与优化)

构建信用卡反欺诈预测模型 建模思路 本项目需解决的问题 本项目通过利用信用卡的历史交易数据&#xff0c;进行机器学习&#xff0c;构建信用卡反欺诈预测模型&#xff0c;提前发现客户信用卡被盗刷的事件。 项目背景 数据集包含由欧洲持卡人于2013年9月使用信用卡进行交的…...

【RISC-V CPU debug 专栏 4 -- RV CSR寄存器介绍】

文章目录 Overview1. CSR寄存器访问指令2. 为何CSR地址不是4字节对齐(1) CSR寄存器空间是独立的地址空间(2) 节省编码空间(3) 对硬件实现的简化 3. CSR的物理大小和对齐无关4. RISC-V 中的 GPR 寄存器及其作用GPR 的详细用途CSR&#xff08;控制状态寄存器&#xff09;与 GPR 的…...

Object.defineProperty() 完整指南

Object.defineProperty() 完整指南 1. 基本概念 Object.defineProperty() 方法允许精确地添加或修改对象的属性。默认情况下&#xff0c;使用此方法添加的属性是不可修改的。 1.1 基本语法 Object.defineProperty(obj, prop, descriptor)参数说明&#xff1a; obj: 要定义…...

postgresql函数创建

postgresql的函数创建 1.创建函数的基本语法&#xff1a; CREATE [OR REPLACE] FUNCTION function_name(parameter_list) RETURNS return_type AS $$ BEGIN -- 函数体 END; $$ LANGUAGE language_name;2.创建函数时传入参数示例:add_user tbl_user表 | id | username | …...

ECMAScript 变量

文章目录 前言一、ECMAScript 变量二、var 关键字1、var 声明作用域2、var 声明提升(hoist)三、let 关键字四、const 关键字🔰 总结前言 任何语言的核心所描述的都是这门语言在最基本的层面上如何工作,涉及 语法、操作符、数据类型以及内置功能,在此基础之上才可以构建复…...

CAN总线波形中最后一位电平偏高或ACK电平偏高问题分析

参考&#xff1a;https://zhuanlan.zhihu.com/p/689336144 有时候看到CAN总线H和L的差值波形的最后一位电平会变高很多&#xff0c;这是什么原因呢&#xff1f; 实际上这是正常的现象&#xff0c;最后一位是ACK位。问题描述为&#xff1a;CAN总线ACK电平偏高。 下面分析下原因…...

【C++】22___STL常用算法

目录 一、常用遍历算法 二、常用查找算法 2.1 find 2.2 其它查找算法 三、常用排序算法 3.1 sort 3.2 其它排序算法 四、拷贝 & 替换 4.1 copy 4.2 其它算法 五、常用的算数生成算法 5.1 accumulate 5.2 fill 六、常用集合算法 6.1 set_intersection 6…...

意静明和-十成

十成 责任&#xff08;健康&#xff09;、使命&#xff08;事业&#xff09;、信念&#xff08;意义&#xff09;、自律&#xff08;排诱&#xff09;、自修&#xff08;知识&#xff09;、总结&#xff08;四为&#xff09;、执行&#xff08;一事不拖&#xff09;、人情&…...

easyui textbox使用placeholder无效

easyui textbox使用placeholder无效 在easyui 的textbox控件&#xff0c;请使用data-options 设定 示例 <input type text class easyui-textbox data-options "prompt:请输入您的邮箱"/>...

flux中的缓存

1. cache&#xff0c;onBackpressureBuffer。都是缓存。cache可以将hot流的数据缓存起来。onBackpressureBuffer也是缓存&#xff0c;但是当下游消费者的处理速度比上游生产者慢时&#xff0c;上游生产的数据会被暂时存储在缓冲区中&#xff0c;防止丢失。 2. Flux.range 默认…...

代码重定位详解

文章目录 一、段的概念以及重定位的引入1.1 问题的引入1.2 段的概念1.3 重定位 二、如何实现重定位2.1 程序中含有什么&#xff1f;2.2 谁来做重定位&#xff1f;2.3 怎么做重定位和清除BSS段&#xff1f;2.4 加载地址和链接地址的区别 三、散列文件使用与分析3.1 重定位的实质…...

活动预告 | Microsoft 365 在线技术公开课:让组织针对 Microsoft Copilot 做好准备

课程介绍 通过Microsoft Learn免费参加Microsoft 365在线技术公开课&#xff0c;建立您需要的技能&#xff0c;以创造新的机会并加速您对Microsoft云技术的理解。参加我们举办的“让组织针对 Microsoft Copilot for Microsoft 365 做好准备” 在线技术公开课活动&#xff0c;学…...

从0到机器视觉工程师(一):机器视觉工业相机总结

目录 相机的作用 工业相机 工业相机的优点 工业相机的种类 工业相机知名品牌 光源与打光 打光方式 亮暗场照明 亮暗场照明的应用 亮暗场照明的区别 前向光漫射照明 背光照明 背光照明的原理 背光照明的应用 同轴光照明 同轴光照明的应用 总结 相机的作用 相机…...

Docker安装(Docker Engine安装)

一、Docker Engine和Desktop区别 Docker Engine 核心组件&#xff1a;Docker Engine是Docker的核心运行时引擎&#xff0c;负责构建、运行和管理容器。它包括守护进程&#xff08;dockerd&#xff09;、API和命令行工具客户端&#xff08;docker&#xff09;。适用环境&#…...

数组的深度监听deep

场景&#xff1a;组件提供的emit事件可能被占用&#xff0c;在不能使用事件提交的情况下&#xff0c;就要上watch数组监听了&#xff0c;但是是发现只有在数组的长度发生变化的时候才会触发监听&#xff0c;这怎么行。。。。。 对于对象数组的深度监听&#xff0c;如果没有正确…...

点击锁定按钮,锁定按钮要变成解锁按钮,然后状态要从待绑定变成 已锁定(升级版)

文章目录 1、updateInviteCodeStatus2、handleLock3、InviteCodeController4、InviteCodeService5、CrudRepository 点击锁定按钮&#xff0c;锁定按钮要变成解锁按钮&#xff0c;然后状态要从待绑定变成 已锁定&#xff1a;https://blog.csdn.net/m0_65152767/article/details…...

UniApp 性能优化策略

一、引言 在当今数字化时代&#xff0c;移动应用的性能成为影响用户留存与满意度的关键因素。UniApp 作为一款热门的跨平台开发框架&#xff0c;以一套代码适配多端的特性极大提升了开发效率&#xff0c;但同时也面临着性能优化的挑战。优化 UniApp 性能&#xff0c;不仅能够让…...

【Linux报告】实训六 重置超级用户密码

实训六 重置超级用户密码 2018编写-今日公布 【练习一】忘记root密码 步骤一&#xff1a;开启或重启系统&#xff0c;并且要在五秒之内按任何键&#xff1b; 步骤二&#xff1a;按任意键&#xff0c;停止进入系统&#xff0c;按【e】键&#xff0c;跳转新页面&#xff0c;再…...

smolagents:一个用于构建代理的简单库

HF推出 smolagents&#xff0c;一个非常简单的库&#xff0c;它能够解锁语言模型的代理功能。以下是它的简要介绍&#xff1a; from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModelagent CodeAgent(tools[DuckDuckGoSearchTool()], modelHfApiModel())agent…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...

快速排序算法改进:随机快排-荷兰国旗划分详解

随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀”

深入浅出JavaScript中的ArrayBuffer&#xff1a;二进制数据的“瑞士军刀” 在JavaScript中&#xff0c;我们经常需要处理文本、数组、对象等数据类型。但当我们需要处理文件上传、图像处理、网络通信等场景时&#xff0c;单纯依赖字符串或数组就显得力不从心了。这时&#xff…...