各种滤波算法

news2025/1/16 5:59:18

各种滤波算法

  • 1. 半径离群点去除(Radius Outlier Removal,半径滤波)
  • 2. 统计离群点剔除(Statistical Outlier Removal, 统计滤波)
  • 3. 体素网格将采样(voxel grid downsampling)
  • 4. 最远点采样(Farthest Point Sampling, FPS)
  • 5. 正态空间将采样(Normal Space Sampling, NSS)
  • 6. 高斯滤波
  • 7. 双边滤波(Bilateral Filter)

这里整理了一些滤波算法,比如用于噪声点去除的Radius Outlier Removal和Statistical Outlier removal;用于降采样的voxel grid downsample,Farthest Point Sampling和Normal Space Sampling;用于上采样和平滑的Bilateral Filter;

1. 半径离群点去除(Radius Outlier Removal,半径滤波)

  • 算法思想
    半径离群点去除算法的原理主要涉及以下几个步骤:
    • 设定参数:首先需要设定半径 r r r 和最小邻居数k作为算法的参数。半径 r r r决定了在计算离群点时考虑的范围,最小邻居数 k k k 决定了一个点周围需要多少个邻居点才能将该点视为非离群点。
    • 建立数据结构:为了快速查找每个点的邻居点,通常会使用 KD 树或者球树等数据结构来存储点云数据,并加速邻居点的搜索。
    • 遍历点云:对于点云中的每个点 P i P_i Pi,在给定的半径 r r r内查找其邻居点,计算邻居点的数量 N i N_i Ni
    • 判断离群点:对于每个点 P i P_i Pi,如果其邻居点的数量小于设定的最小邻居数 k k k,则将 P i P_i Pi视为离群点。
    • 移除离群点:将被标记为离群点的点从点云中移除,得到去除离群点后的点云。

这种方法主要用于去除点云中的噪声点或者异常点,以提高点云数据的质量和准确性。

  • 代码1(使用PCL)
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/radius_outlier_removal.h>

int main() {
    // Load point cloud data
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::io::loadPCDFile("input_cloud.pcd", *cloud);

    // Set parameters for radius outlier removal
    float radius = 0.1; // Radius search
    int min_neighbors = 10; // Minimum number of neighbors
    pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
    outrem.setInputCloud(cloud);
    outrem.setRadiusSearch(radius);
    outrem.setMinNeighborsInRadius(min_neighbors);

    // Apply radius outlier removal
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
    outrem.filter(*cloud_filtered);

    // Save filtered point cloud
    pcl::io::savePCDFile("output_cloud.pcd", *cloud_filtered);

    return 0;
}
  • 代码2(不使用PCL, 自己构建KDTree)
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

struct Point {
    float x;
    float y;
    float z;
    int id;  // 用于标识点的ID

    Point(float x, float y, float z, int id) : x(x), y(y), z(z), id(id) {}
};

struct Node {
    Point point;
    Node* left;
    Node* right;

    Node(Point p) : point(p), left(nullptr), right(nullptr) {}
};

// 定义KD树
class KDTree {
private:
    Node* root;

    Node* buildTree(std::vector<Point>& points, int depth, int start, int end) {
        if (start > end) return nullptr;

        int axis = depth % 3;  // 在三维空间中进行分割

        std::sort(points.begin() + start, points.begin() + end + 1, [axis](const Point& a, const Point& b) {
            if (axis == 0) return a.x < b.x;
            else if (axis == 1) return a.y < b.y;
            else return a.z < b.z;
        });

        int mid = start + (end - start) / 2;
        Node* node = new Node(points[mid]);
        node->left = buildTree(points, depth + 1, start, mid - 1);
        node->right = buildTree(points, depth + 1, mid + 1, end);
        return node;
    }

    void searchNeighbors(Node* node, const Point& query, float radius, std::vector<int>& neighbors) {
        if (node == nullptr) return;

        float dist = calculateDistance(node->point, query);
        if (dist <= radius) {
            neighbors.push_back(node->point.id);
        }

        int axis = 0;
        if (axis == 0) {
            if (query.x - radius <= node->point.x) searchNeighbors(node->left, query, radius, neighbors);
            if (query.x + radius > node->point.x) searchNeighbors(node->right, query, radius, neighbors);
        } else if (axis == 1) {
            if (query.y - radius <= node->point.y) searchNeighbors(node->left, query, radius, neighbors);
            if (query.y + radius > node->point.y) searchNeighbors(node->right, query, radius, neighbors);
        } else {
            if (query.z - radius <= node->point.z) searchNeighbors(node->left, query, radius, neighbors);
            if (query.z + radius > node->point.z) searchNeighbors(node->right, query, radius, neighbors);
        }
    }

public:
    KDTree(std::vector<Point>& points) {
        root = buildTree(points, 0, 0, points.size() - 1);
    }

    std::vector<int> getNeighbors(const Point& query, float radius) {
        std::vector<int> neighbors;
        searchNeighbors(root, query, radius, neighbors);
        return neighbors;
    }

    float calculateDistance(const Point& p1, const Point& p2) {
        float dx = p1.x - p2.x;
        float dy = p1.y - p2.y;
        float dz = p1.z - p2.z;
        return std::sqrt(dx * dx + dy * dy + dz * dz);
    }
};

int main() {
    std::vector<Point> points = {{1, 2, 3, 0}, {4, 5, 6, 1}, {7, 8, 9, 2}, {10, 11, 12, 3}};
    KDTree kd_tree(points);

    Point query_point(3, 4, 5, -1);
    float radius = 2.0;
    std::vector<int> neighbors = kd_tree.getNeighbors(query_point, radius);

    for (int id : neighbors) {
        std::cout << "Neighbor ID: " << id << std::endl;
    }

    return 0;
}

2. 统计离群点剔除(Statistical Outlier Removal, 统计滤波)

统计滤波基于一种假设: 点云中大部分点都是从同一平均分布中采样得到的,而离群点则来自一个不同的分布。

  • 算法思想步骤如下:

    • 设定参数:首先需要设定滤波算法的参数,包括点云中每个点的邻域范围(半径或K近邻),以及用于判断离群点的统计参数阈值,如均值和标准差的倍数。
    • 计算邻域统计参数:对于点云中的每个点,计算其邻域内点的统计参数,通常包括均值和标准差。可以选择使用固定大小的邻域或者自适应的 K 近邻来计算统计参数。
    • 判断离群点:根据设定的统计参数阈值,判断每个点是否为离群点。通常采用的判断方式是,如果点的某个统计参数(如距离邻域点的平均距离)超过了设定的阈值,则将该点标记为离群点。
    • 滤除离群点:将被标记为离群点的点从点云中移除,得到去除离群点后的点云。
  • 公式表达步骤如下:

    • 设置邻域r,得到点P周围邻域r内的若干点,记为k个(其实不是每个点在周围邻域内的邻居都有k个)
    • 计算所有点与自身邻域邻居的距离值 d i j , i ∈ [ 1 , 2 , . . . , m ] , j ∈ [ 1 , 2 , . . . , k ] d_{ij},i \in[1,2,...,m],j\in[1,2,...,k] dij,i[1,2,...,m],j[1,2,...,k]。m表示输入的点云个数,k表示每个点的邻居个数(只是方便表达,每个点的邻域邻居个数会有不同)
    • 通过 d i j d_{ij} dij,计算该组点云的距离高斯分布 d ~ N ( μ , σ ) d ~ N(\mu, \sigma) dN(μ,σ)也就是说这组点的每个点与其邻域内邻居的欧式距离均值都要遵循这个分布
      μ = 1 m × k ∑ i = 1 m ∑ j = 1 k d i j , σ = 1 m × k ∑ i = 1 m ∑ j = 1 k ( d i j − μ ) 2 \mu = {1 \over m \times k} \sum_{i=1}^m \sum_{j=1}^kd_{ij}, \sigma = \sqrt{{1 \over m \times k} \sum_{i=1}^m \sum_{j=1}^k(d_{ij}-\mu)^2} μ=m×k1i=1mj=1kdij,σ=m×k1i=1mj=1k(dijμ)2
    • 计算每个点与其邻域半径范围内点的距离均值 d d d
    • 剔除距离均值d不再置信度区间内的点,即如果不满足下边的公式,就要剔除
      ∑ j = 1 k d > μ + 3 σ  or  ∑ j = 1 k d < μ − 3 σ \sum_{j=1}^{k} d>\mu+3 \sigma \text { or } \sum_{j=1}^{k} d<\mu-3 \sigma j=1kd>μ+3σ or j=1kd<μ3σ
  • 代码

#include <iostream>
#include <vector>
#include <cmath>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>

typedef pcl::PointXYZ PointType;
typedef pcl::PointCloud<PointType> PointCloud;

// Statistical outlier removal filter function using KDTree
PointCloud::Ptr statisticalOutlierRemovalKDTree(const PointCloud::Ptr& cloud, float radius) {
    pcl::KdTreeFLANN<PointType> kdtree;
    kdtree.setInputCloud(cloud);

    PointCloud::Ptr filtered_cloud(new PointCloud);
    std::vector<int> pointIdxRadiusSearch;
    std::vector<float> pointRadiusSquaredDistance;

    for (size_t i = 0; i < cloud->points.size(); ++i) {
        if (kdtree.radiusSearch(cloud->points[i], radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) > 0) {
            std::vector<float> distances;
            for (size_t j = 0; j < pointIdxRadiusSearch.size(); ++j) {
                distances.push_back(std::sqrt(pointRadiusSquaredDistance[j]));
            }

            float sum_distances = 0.0;
            for (float dist : distances) {
                sum_distances += dist;
            }

            float mean_distance = sum_distances / distances.size();

            float sum_sq_diff = 0.0;
            for (float dist : distances) {
                sum_sq_diff += (dist - mean_distance) * (dist - mean_distance);
            }
            float std_dev = std::sqrt(sum_sq_diff / distances.size());

            float confidence_interval = 3 * std_dev;

            float sum_filtered_distances = 0.0;
            for (float dist : distances) {
                if (dist <= (mean_distance + confidence_interval) && dist >= (mean_distance - confidence_interval)) {
                    sum_filtered_distances += dist;
                }
            }

            float mean_filtered_distance = sum_filtered_distances / distances.size();

            if (sum_filtered_distances > (mean_distance + confidence_interval) || 
                sum_filtered_distances < (mean_distance - confidence_interval)) {
                // Point is an outlier, do not add to filtered cloud
            } else {
                // Point is within confidence interval, add to filtered cloud
                filtered_cloud->points.push_back(cloud->points[i]);
            }
        }
    }

    filtered_cloud->width = filtered_cloud->points.size();
    filtered_cloud->height = 1;
    return filtered_cloud;
}

int main() {
    PointCloud::Ptr cloud(new PointCloud);
    // Fill 'cloud' with point data (assuming loaded from file)

    float neighborhood_radius = 1.0;

    PointCloud::Ptr filtered_cloud = statisticalOutlierRemovalKDTree(cloud, neighborhood_radius);

    std::cout << "Filtered Cloud:" << std::endl;
    for (size_t i = 0; i < filtered_cloud->points.size(); ++i) {
        std::cout << "(" << filtered_cloud->points[i].x << ", " << filtered_cloud->points[i].y << ", " << filtered_cloud->points[i].z << ")" << std::endl;
    }

    return 0;
}

3. 体素网格将采样(voxel grid downsampling)

体素网格降采样的本质就是,设置体素网格的大小,对于包含点云的体素栅格,从栅格中提取一个点。如果只是针对坐标值来说,可以是随机提取,可以求均值,也可以直接使用栅格的中心。但是对于携带的物理信息,比如说反射强度等属性,可以采用投票的方式

  • 算法原理

    • 设定体素大小:首先需要设定体素网格的大小,即立方体的边长。这个大小决定了最终采样后点云的密度和分辨率。
    • 创建体素网格:将点云中的所有点按照体素大小划分到对应的立方体网格中。这一步可以使用一个三维数组或者类似的数据结构来表示体素网格。
    • 采样过程:对于每个立方体网格,选择其中包含点的网格作为采样后的点,可以选择点的均值或者其他统计值作为采样点的位置。
    • 降采样:根据体素网格的大小,可以选择保留每个体素网格中的一个采样点,或者按照一定规则保留部分采样点,实现点云的降采样。
  • 代码(PCL版本)

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

int main() {
    // Load point cloud data
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::io::loadPCDFile<pcl::PointXYZ>("input_cloud.pcd", *cloud);

    // Voxel grid downsampling
    pcl::VoxelGrid<pcl::PointXYZ> voxel_grid;
    voxel_grid.setInputCloud(cloud);
    voxel_grid.setLeafSize(0.1, 0.1, 0.1);  // Set voxel size
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_downsampled(new pcl::PointCloud<pcl::PointXYZ>);
    voxel_grid.filter(*cloud_downsampled);

    // Save downsampled cloud
    pcl::io::savePCDFile<pcl::PointXYZ>("output_downsampled.pcd", *cloud_downsampled);

    std::cout << "PointCloud before downsampling: " << cloud->size() << " points" << std::endl;
    std::cout << "PointCloud after downsampling: " << cloud_downsampled->size() << " points" << std::endl;

    return 0;
}

4. 最远点采样(Farthest Point Sampling, FPS)

最远点采样(Farthest Point Sampling,FPS)算法是一种常用于点云数据采样的方法,其原理如下:

  • 初始化:随机选择一个点作为起始点,并将其加入到采样点集合中。
  • 迭代采样:对于剩余的点,计算它们到已选取采样点集合中所有点的最短距离(或者最远距离),选取距离最大的点作为下一个采样点,并将其加入到采样点集合中。
  • 重复直到结束:重复上述过程,直到采样点集合达到所需的数量或者所有点都被选取。

这个算法的关键是在每次迭代中高效地找到距离已选取采样点集合最远的点。这通常可以通过优先级队列(如堆)来实现,以快速找到最远点。

在这里插入图片描述

5. 正态空间将采样(Normal Space Sampling, NSS)

普通的采样,对于表面非均匀的场景不太友好。如果点云曲面有小的凹陷,如果使用均匀采样,就会遗失这种特征,所以引入了NSS的方法,NSS可以保证所有的法向量范围都可以采样的到。

  • NSS算法原理:
    • 在法向量空间中建立一些容器,每个容器对应一个不同的法向量方向。
    • 遍历点云中的每个点,根据点的法向量将点放入对应的法向量容器中。
    • 从每个不同的法向量容器中取出一定数量的点作为采样点,可以根据需求调整采样点的数量。
      这种方法能够有效地在法向量空间内对点云进行采样,保留了不同法向量方向上的特征点,有助于保持点云的形状信息。
      在这里插入图片描述

6. 高斯滤波

在这里插入图片描述
这里的S代表像素p的位置邻域空间,q为邻域内的某个位置, G σ ( x ) G_\sigma(x) Gσ(x)代表像素p和像素q两个像素点在某个尺度上的高斯权重(这里两者在该尺度上的距离越远,权重越小,表示像素点q给像素点p的贡献越小), I q I_q Iq这里可以指代像素点的RGB通道值。也就是说这里是使用高斯滤波来求像素点p的rgb通道值(借助像素p周围邻域内的所有像素点的rgb通道值完成滤波)

具体来说,高斯滤波器在处理图像或信号时,通过将每个像素点或采样点与周围像素点或采样点的值按照高斯函数的权重进行加权平均,从而达到平滑、去噪或者边缘保留的效果。

高斯滤波的本质可以用以下几点来总结:

加权平均:高斯滤波器对每个像素或采样点的值进行加权平均,权重由高斯函数决定。
平滑效果:高斯函数在中心值附近有较高的权重,而在距离中心值较远的位置权重逐渐减小,因此对信号进行高斯滤波可以实现平滑效果,去除噪声。
边缘保留:由于高斯函数的权重在中心值附近较高,在边缘处权重急剧减小,因此高斯滤波在一定程度上能够保留图像或信号的边缘特征,避免过度平滑导致细节丢失

7. 双边滤波(Bilateral Filter)

在这里插入图片描述

双边滤波是说同时使用两个尺度的高斯距离来给权重

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

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

相关文章

第六期丨酷雷曼无人机技能培训

第6期无人机技能提升培训 盼望着盼望着&#xff0c;第六期无人机技能提升培训会终于如期和大家见面了。 2024年1月1日&#xff0c;国务院、中央军事委员会颁布《无人驾驶航空器飞行管理暂行条例》&#xff0c;对民用无人机飞行活动实施更为严格的规范约束&#xff0c;越来越多…

AOP源码解析

一、注册AnnotationAwareAspectJAutoProxyCreator&#xff0c;引入postProcessAfterInitialization 1、Spring 入口&#xff1a; <aop:aspectj-autoproxy /> registerAspectJAnnotationAutoProxyCreatorIfNecessary,注册AnnotationAwareAspectJAutoProxyCreator Annot…

elsint报错Delete `␍`eslintprettier/prettier

一&#xff0c;原因 这篇博客写得很清楚&#xff1a;解决VSCode Delete ␍eslint(prettier/prettier)错误_vscode 删除cr-CSDN博客 还有这篇文章&#xff0c;解决办法很详细&#xff1a;滑动验证页面 二&#xff0c;解决办法 根目录下新建.prettierrc.js文件 module.exports…

wordpress外贸独立站模板

wordpress外贸独立站模板 WordPress Direct Trade 外贸网站模板&#xff0c;适合做跨境电商的外贸公司官方网站使用。 https://www.waimaoyes.com/wangzhan/22.html

入门用Hive构建数据仓库

在当今数据爆炸的时代&#xff0c;构建高效的数据仓库是企业实现数据驱动决策的关键。Apache Hive 是一个基于 Hadoop 的数据仓库工具&#xff0c;可以轻松地进行数据存储、查询和分析。本文将介绍什么是 Hive、为什么选择 Hive 构建数据仓库、如何搭建 Hive 环境以及如何在 Hi…

读《Spring实战》:面向切面

AOP术语 通知&#xff08;Advice&#xff09; 在AOP中&#xff0c;切面的工作被称为通知&#xff0c;也就是通知就是具体要干的工作。 spring中有5中通知&#xff1a; 前置通知&#xff1a; 在目标方法之前调用通知功能后置通知&#xff1a; 在目标方法之后调用通知功能返回…

无线电和雷达频谱大全

1&#xff0c;频率单位 2&#xff0c;全球警用雷达频率 3&#xff0c;军用雷达频段 4&#xff0c;国际电联ITU雷达频段 5&#xff0c;无线电频段 6&#xff0c;电子对抗ECM频段 7&#xff0c;声波频段

C#清空窗体的背景图片

目录 一、涉及到的知识点 1.设置窗体的背景图 2.加载窗体背景图 3.清空窗体的背景图 二、 示例 一、涉及到的知识点 1.设置窗体的背景图 详见本文作者的其他文章&#xff1a;C#手动改变自制窗体的大小-CSDN博客 https://wenchm.blog.csdn.net/article/details/137027140…

基于SpringBoot和Vue的金融融资管理系统的设计和实现【附源码】

1、系统演示视频&#xff08;演示视频&#xff09; 2、需要交流和学习请联系

paddlepaddle模型转换onnx指导文档

一、检查本机cuda版本 1、右键找到invdia控制面板 2、找到系统信息 3、点开“组件”选项卡&#xff0c; 可以看到cuda版本&#xff0c;我们这里是cuda11.7 cuda驱动版本为516.94 二、安装paddlepaddle环境 1、获取pip安装命令 &#xff0c;我们到paddlepaddle官网&#xff…

网络原理 - HTTP / HTTPS(3)——http响应

目录 一、认识 “状态码”&#xff08;status code&#xff09; 常见的状态码 &#xff08;1&#xff09;200 OK &#xff08;2&#xff09;404 Not Found &#xff08;3&#xff09;403 ForBidden &#xff08;4&#xff09;405 Method Not Allowed &#xff08;5&…

Unity框架,ET框架8.1版本的打包流程记录

目录 打包代码前置1.必须要安装Visusal Studio 2022的组件&#xff0c;如下图&#xff0c;必须都要进行安装&#xff0c;不然会在代码重构的时候报错&#xff0c;丢失SDK。Rider的版本必须2023及以上 步骤一、使用Rider编辑器打开项目后进行重构项目步骤二、使用HybirdCLR生成A…

openGauss学习笔记-256 openGauss性能调优-使用Plan Hint进行调优-优化器GUC参数的Hint

文章目录 openGauss学习笔记-256 openGauss性能调优-使用Plan Hint进行调优-优化器GUC参数的Hint256.1 功能描述256.2 语法格式256.3 参数说明 openGauss学习笔记-256 openGauss性能调优-使用Plan Hint进行调优-优化器GUC参数的Hint 256.1 功能描述 设置本次查询执行内生效的…

Flume 拦截器概念及自定义拦截器的运用

文章目录 Flume 拦截器拦截器的作用拦截器运用1.创建项目2.实现拦截器接口3.编写事件处理逻辑4.拦截器构建5.打包与上传6.编写配置文件7.测试运行 Flume 拦截器 在 Flume 中&#xff0c;拦截器&#xff08;Interceptors&#xff09;是一种可以在事件传输过程中拦截、处理和修改…

【Qt 学习笔记】Qt的坐标体系

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt的坐标体系 文章编号&#xff1a;Qt 学习笔记 / 11 文章目录 Qt的坐…

ML.NET(二) 使用机器学习预测表情分析

这个例子使用模型进行表情分析&#xff1a; 准备数据&#xff1a; happy,sad 等&#xff1b; using Common; using ConsoleApp2; using Microsoft.ML; using Microsoft.ML.Data; using System.Diagnostics; using static Microsoft.ML.Transforms.ValueToKeyMappingEstimator;…

[C#]OpenCvSharp实现直方图均衡化全局直方图局部直方图自适应直方图

【什么是直方图均衡化】 直方图均衡化是一种简单而有效的图像处理技术&#xff0c;它旨在改善图像的视觉效果&#xff0c;使图像变得更加清晰和对比度更高。其核心原理是将原始图像的灰度直方图从可能较为集中的某个灰度区间转变为在全部灰度范围内的均匀分布。通过这种方法&a…

【接口】HTTP(1)|请求|响应

1、概念 Hyper Text Transfer Protocol&#xff08;超文本传输协议&#xff09;用于从万维网&#xff08;就是www&#xff09;服务器传输超文本到本地浏览器的传送协议。 HTTP协议是基于TCP的应用层协议&#xff0c;它不关心数据传输的细节&#xff0c;主要是用来规定客户端和…

【Linux】第二个小程序--简易shell

请看上面的shell&#xff0c;其本质就是一个字符串&#xff0c;我们知道bash本质上就是一个进程&#xff0c;只不过命令行就是一个输出的字符串&#xff0c; 我们输入的命令“ls -a -l”实际上是我们在输入行输入的字符串&#xff0c;所以&#xff0c;如果我们想要做一个简易的…

vscode开发ESP32问题记录

vscode 开发ESP32问题记录 1. 解决vscode中的波浪线警告 1. 解决vscode中的波浪线警告 参考链接&#xff1a;https://blog.csdn.net/fucingman/article/details/134404485 首先可以通过vscode 中的IDF插件生成模板工程&#xff0c;这样会自动创建.vscode文件夹中的一些json配…