基于链表的滑动中值滤波器实现思路

总之,先假设底层的链表已经实现好了,反正很简单。

原理

基本上,中值滤波,或者说滑动中值滤波,需要做三件事:

  1. 在新数据添加到窗口的同时,将最旧的数据删除;
  2. 对窗口中的数据排序;
  3. 找出中位数;

排序

每个新数据将被加入一个双向链表LinkedList中,并且在加入链表时就把它按从小到大的顺序放在合适的位置。添加新数据并排序,最多只需要把整个链表遍历一次。如果不在添加时立即排序,后续只能整体排序,那就指不定要来回几次了。换个视角看,如果用这种方法添加N 个数据,最多可能要遍历N 次,其实不算快,但是用在滤波器这种场合,需要考虑的主要是添加单个数据时排序的性能,那这种方法就很方便了。

中位数

保存当前的中位数 M M M,添加新数据 N N N 时,若 N < M N \lt M N<M ,那么 N N N 肯定会被排在 M M M 之前,链表的前半部分变长了,中点自然应该向前移动,所以新的中位数就对应 M M M 的前一个节点。反之,若 N ≥ M N \ge M NM,中点就向后移动。

删除旧数据也会导致链表中点发生变化,和添加新数据同理:如果前面短了,就让中点向后,否则让中点向前。为了避免重复移动中点,可以一次性比较新数据 N N N、旧数据 D D D、当前中点 M M M,如果中点前面删了一个数据,但是又加了个数据,那么中点就不用移动,其他情况同理。虽说,双向链表的优点就在于移动和增删的高效,中点重复挪几下或许比多几个分支判断要更高效呢。

当然,这么做的前提是,滤波器初始化时就要让 M M M 指向链表中间。这应该没什么难的,如果窗口大小是 W W W,初始化时就给链表中添加 W W W 个节点,每个节点的值是某个初始值,比如就是0,然后让 M M M 对应 W 2 \frac{W}{2} 2W对应的那个节点就好了。这个步骤只需要在初始化时做一次,不用担心会浪费性能,直接随便找个地方当中值点好像也完全OK。

删除旧数据

首先要找到最旧的数据,这个地方就稍微有点麻烦了,双向链表里的数据是按大小排序的,无法直接判断数据加入的先后顺序。我的思路是:另外再加一个单向链表,也可以说是队列,就起名叫ForwardLinkedList;每个数据将被同时加入双向链表和这个队列中,队列自然就有先进先出的功能,可以高效的找出最旧的数据。

具体来说,存放数据的链表节点应该含有三个指针域,即nextprevforwardnextprev 用来指向双向链表中的前一节点和后一节点,forward 用来在单向链表中指向后一节点。这些指针的类型都是一样的,指向存放数据的链表节点类型,可以取名叫DoubleLinkedNode。要删除旧数据,只需从队列中弹出最旧的节点,然后从双向链表中将该节点删除。

优点

  1. 运行可能比较快:这里用链表本身就是拿空间换时间,数据增删和挪动都只要一步完成。每次添加新数据时,排序最多只用遍历一次,理论上耗时也比较短;

缺点

  1. 比较费空间:如果滤波器接收的数据是float 类型,塞进链表节点里之后,加上三个指针,存储每个数据要耗费三倍的额外空间;
  2. 管理链表要操作一堆指针:尤其这还是两个链表交织到一起,写代码要小心点儿;

伪代码实现

还是假设底层链表已经实现好了,拿伪代码把思路细化:


struct DoubleLinkedNode {
	// 用于单向链表
	DoubleLinkedNode *forward;
	
	// 用于双向链表
	DoubleLinkedNode *next;
	DoubleLinkedNode *prev;

	float data;
};

// 双向链表
class LinkedList {
	// 假设已经实现好了
};

// 单向链表
class ForwardLinkedList {
	// 假设已经实现好了
};

// 滑动中值滤波器
class MovindMedianFilter {
	private:
	LinkedList _ordered_list;  // 用于排序的双向链表
	ForwardLinkedList _queue;  // 先进先出队列
	
	DoubleLinkedNode *_median_node;  // 指向中值
	
	public:
	MovindMedianFilter() {
	
	}
	
	// 初始化,创建并给链表中添加windows_size 个节点,值为init_value
	void init(size_t window_size, float init_value) {
        for (int i = 0; i < window_size; ++i) {
            auto * node = new DoubleLinkedNode{};
            node->data = init_value;
            
            if(i == window_size / 2) {  // 初始化中值节点
                _median_node = node;
            }
            
            _queue.push_back(node);
            _ordered_list.push_back(node);   // 所有节点的值相同,不用排序,
                                             // 直接添加就能保证初始中值节点位于链表中间
        }
	}
	
	// 传入一个新数据,返回经过滤波的中值
	float feed(float new_value) {
        float median_value = _median_node->data;  // 当前中值
        
        auto *old_node = _queue.pop_front();      // 弹出旧数据
        _ordered_list.remove(old_node);
        float old_value = old_node->data;
        
        // 重用旧节点,添加新数据。所以初始化以后,只要窗口大小不变,滤波器就不需要释放或获取更多内存
        old_node->data = new_value;
        _queue.push_back(old_node);
        _ordered_list.insert_sorted(old_node);
        
        // 如果新数据和旧数据都大于或小于当前中值,当前中值就不需要移动
        if(new_value < median_value && old_value >= median_value) {
            // 前面加一个,后面减一个,中点前移
            _median_node = _median_node->prev;
        }
        else if(new_value >= median_value && old_value < median_value) {
            // 后面加一个,前面减一个,中点后移
            _median_node = _median_node->next;
        }
        
        return _median_node->data;
	}

};

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

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

相关文章

kubernetes中Pod调度-Taints污点和污点容忍

一、污点的概念 所谓的污点&#xff0c;是给k8s集群中的节点设置的&#xff0c;通过设置污点&#xff0c;来规划资源创建是所在的节点 污点的类型 解释说明PreferNoshedule 节点设置这个污点类型后&#xff1b; 表示&#xff0c;该节点接收调度&#xff0c;但是会降低调度的概…

hbase 集成 phoenix 实现 sql 化

1. 依赖 hbase > hbase 集群搭建 2. 下载安装包 点击下载 ps&#xff1a;该网页在内网可能打不开&#xff0c;遇到该情况有条件的可以打开 VPN 在下载 3. 上传解压 使用工具将安装包上传的服务器上 笔者这里选择 上传到 /opt/software 目录&#xff0c;解压到 /opt/mo…

基于STM32和阿里云的智能台灯(STM32+ESP8266+MQTT+阿里云+语音模块)

一、主要完成功能 1、冷光模式和暖光模式两种灯光 主要支持冷光和暖光模式两种&#xff0c;可以通过语音模块或手机app远程切换冷暖光 2、自动模式和手动模式 主要支持手动模式和自动两种模式&#xff08;app或语音助手切换&#xff09; (1)自动模式&#xff1a;根据环境光照…

针孔相机模型原理坐标系辨析内参标定流程内参变换

针孔相机的内参标定 针孔相机原理真空相机模型图片的伸缩和裁剪变换 内参标定———非线性优化张正定标定详细原理(含公式推导)通过多张棋盘格照片完成相机的内参标定流程(C代码)其他工具箱 相机分为短焦镜头和长焦镜头&#xff0c;短焦镜头看到的视野更广阔&#xff0c;同样距…

QFD赋能人工智能:打造智能化需求分析与优化新纪元

在科技飞速发展的今天&#xff0c;人工智能(AI)已经渗透到我们生活的方方面面。然而&#xff0c;如何让AI更加贴合用户需求&#xff0c;提供更加精准和个性化的服务&#xff1f;这成为了一个亟待解决的问题。质量功能展开&#xff08;Quality Function Deployment&#xff0c;简…

openjudge_2.5基本算法之搜索_1998:寻找Nemo

题目 1998:寻找Nemo 总时间限制: 2000ms 内存限制: 65536kB 描述 Nemo 是个顽皮的小孩. 一天他一个人跑到深海里去玩. 可是他迷路了. 于是他向父亲 Marlin 发送了求救信号.通过查找地图 Marlin 发现那片海像一个有着墙和门的迷宫.所有的墙都是平行于 X 轴或 Y 轴的. 墙的厚度可…

股票战法课程之倍阴龙战法

1. 核心要素 1、股价处于低位震荡区间 2、涨停板分时走的比较流畅&#xff0c;即使去到分时均线以下也能够是秒拉上来&#xff0c;或者沿着分时均线上攻打板 3、涨停后次日阴线的成交量是前一日涨停板成交量的两倍以上 4、倍量阴线出现后的30天以内第一个涨停板则是买点的浮现…

【数据结构】图(Graph)

文章目录 概念图的存储方式邻接矩阵邻接矩阵表示法邻接矩阵表示法的特点 邻接表邻接表表示法邻接表表示法的特点邻接表表示法的定义与实现查找插入删除其它构造函数析构函数创建图输出图 图的遍历深度优先遍历&#xff08;DFS&#xff09;广度优先遍历 图的连接分量和生成树生成…

Hive查询操作详解

Hive 数据准备&#xff1a; Tips&#xff1a; &#xff08;1&#xff09;SQL 语言大小写不敏感。 &#xff08;2&#xff09;SQL 可以写在一行或者多行。 &#xff08;3&#xff09;关键字不能被缩写也不能分行。 &#xff08;4&#xff09;各子句一般要分行写。 &#xff0…

进程动静态库

文章目录 动态库和静态库1. 静态库2. 动态库 承接上文&#xff1a; 文件描述符 动态库和静态库 静态库与动态库&#xff1a; 静态库&#xff08;.a&#xff09;&#xff1a;程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库动态库&#xf…

python绘制R控制图(Range Chart)

R控制图&#xff08;Range Chart&#xff09;&#xff0c;也称为范围图或移动极差图&#xff0c;是一种用于分析和控制生产过程中的变异性的统计工具。它通常与Xbar控制图&#xff08;均值图&#xff09;一起使用&#xff0c;可以提供关于生产过程变异性的额外信息。以下是R控制…

ArgoCD集成部署到Kubernetes

1&#xff1a;环境 kubernetes1.23.3ArgoCD2.3.3 2&#xff1a;ArgoCD介绍 Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. Argo CD是一个基于Kubernetes的声明式的GitOps工具。 那么&#xff0c;什么是GitOps呢&#xff1f; GitOps是以Git为基…

feign整合sentinel做降级知识点

1&#xff0c;配置依赖 <!-- Feign远程调用依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency> <!--sentinel--><dependency>…

Linux使用操作(一)

Linux创建链接的方式 在Linux中&#xff0c;可以给文件创建链接。链接的意思可以理解是快捷方式&#xff0c;它指向另一个文件或目录。 软链接 软连接&#xff08;也叫符号链接&#xff09;是一种特殊类型的文件&#xff0c;它指向另一个文件或目录 语法 ln -s 原文件路径…

谷歌发布基于声学建模的无限虚拟房间增强现实鲁棒语音识别技术

声学室模拟允许在AR眼镜上以最少的真实数据进行训练&#xff0c;用于开发鲁棒的语音识别声音分离模型。 随着增强现实&#xff08;AR&#xff09;技术的强大和广泛应用&#xff0c;它能应用到各种日常情境中。我们对AR技术的潜能感到兴奋&#xff0c;并持续不断地开发和测试新…

SpringBoot---------整合Mybatisplus

快速入门 第一步&#xff1a;导入依赖 <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.1</version></dependency> 第二步&#xff1a;编写mapper…

区块链 | OpenSea 相关论文:Toward Achieving Anonymous NFT Trading(下)

&#x1f951;原文&#xff1a; Toward Achieving Anonymous NFT Trading VII 讨论&#xff1a;关于匿名性与市场平台的困境 在本文的这一部分&#xff0c;我们将讨论关于隐藏 NFT 所有者地址的困境&#xff0c;以及为什么像 OpenSea 这样的 NFT 市场平台几乎必须得到完全的信…

Java | 选择排序算法实现

大家可以关注一下专栏&#xff0c;方便大家需要的时候直接查找&#xff0c;专栏将持续更新~ 题目描述 编写一个Java程序&#xff0c;实现选择排序算法。程序需要能够接收一个整型数组作为输入&#xff0c;并输出排序后的数组。 选择排序是一种简单直观的排序算法&#xf…

imx6ull -- SPI

SPI 是 Motorola 公司推出的一种同步串行接口 技术&#xff0c;是一种高速、全双工的同步通信总线&#xff0c; SPI 时钟频率相比 I2C 要高很多&#xff0c;最高可以工作 在上百 MHz。 SPI 以主从方式工作&#xff0c;通常是有一个主设备和一个或多个从设备&#xff0c;一般 SP…

ASP.NET Core WEB API 使用element-ui文件上传组件el-upload执行手动文件文件,并在文件上传后清空文件

前言&#xff1a; 从开始学习Vue到使用element-ui-admin已经有将近快两年的时间了&#xff0c;在之前的开发中使用element-ui上传组件el-upload都是直接使用文件选取后立即选择上传&#xff0c;今天刚好做了一个和之前类似的文件选择上传的需求&#xff0c;不过这次是需要手动点…
最新文章