自适应中值滤波及实现

article/2025/10/9 22:04:49

前言

无意中看到了一篇比较老的论文,Adaptive median filters: new algorithms and results。感兴趣的可以下载下来看看。主要就是提出了一种自适应中值滤波算法,这个算法是很经典的中值滤波算法的改进版本,自动选择滤波器的大小,以追求更好的效果。原理十分简单,后面都尽量简短地进行说明。

中值滤波器(Median Filter)

中值滤波的思想就是比较一定领域内的像素值的大小,取出其中值作为这个领域的中心像素新的值。假设对一定领域内的所有像素从小到大进行排序,如果存在孤立的噪声点,比如椒盐噪声(椒噪声——较小的灰度值,呈现的效果是小黑点;盐噪声——较大的灰度值,呈现的效果是小白点),那么从小到大排序的这个数组中,那些孤立的噪声一定会分布在两边(要么很小,要么很大),这样子取出的中值点可以很好地保留像素信息,而滤除了噪声点的影响。
中值滤波器受滤波窗口大小影响较大,用于消除噪声和保护图像细节,两者会存在冲突。如果窗口较小,则能较好地保护图像中的一些细节信息,但对噪声的过滤效果就会打折扣;反之,如果窗口尺寸较大则会有较好的噪声过滤效果,但也会对图像造成一定的模糊效果,从而丢失一部分细节信息。另外,如果在滤波窗口内的噪声点的个数大于整个窗口内像素的个数,则中值滤波就不能很好的过滤掉噪声。

自适应中值滤波器(Adaptive Median Filter)

在噪声密度不是很大的情况下(根据经验,噪声的出现的概率小于0.2),使用中值滤波的效果不错。但是当噪声出现的概率比较高时,原来的中值滤波算法就不是很有效了。只有增大滤波器窗口尺寸,尽管会使图像变得模糊。
使用自适应中值滤波器的目的就是,根据预设好的条件,动态地改变中值滤波器的窗口尺寸,以同时兼顾去噪声作用和保护细节的效果。
下面是自适应中值滤波器算法的详细描述:
预先定义好以下符号:

  • Sxy S x y :滤波器的作用区域,滤波器窗口所覆盖的区域,该区域中心点为图像中第y行第x列个像素点;
  • Zmin Z m i n Sxy S x y 中最小的灰度值;
  • Zmax Z m a x Sxy S x y 中最大的灰度值;
  • Zmed Z m e d Sxy S x y 中所有灰度值的中值;
  • Zxy Z x y :表示图像中第y行第x列个像素点的灰度值;
  • Smax S m a x Sxy S x y 所允许的最大窗口尺寸;

自适应中值滤波器分为以下两个过程,AB
A:
1. A1 = Zmed Z m e d - Zmin Z m i n
2. A2 = Zmed Z m e d - Zmax Z m a x
3. 如果 A1>0 A 1 > 0 A2<0 A 2 < 0 ,则跳转到B
4. 否则,增大窗口的尺寸
5. 如果增大后的尺寸 Smax S m a x ,则重复A
6. 否则,直接输出 Zmed Z m e d
B
1. B1 = Zxy Z x y - Zmin Z m i n
2. B2 = Zxy Z x y - Zmax Z m a x
3. 如果 B1>0 B 1 > 0 B2<0 B 2 < 0 ,则输出 Zxy Z x y
4. 否则输出 Zmed Z m e d

直观解释

在自适应中值滤波算法中,A步骤里面会先判断是否满足 Zmin<Zmed<Zmax Z m i n < Z m e d < Z m a x 。这一步骤实质是判断当前区域的中值点是否是噪声点,通常来说是满足 Zmin<Zmed<Zmax Z m i n < Z m e d < Z m a x 这个条件的,此时中值点不是噪声点,跳转到B;考虑一些特殊情况,如果 Zmed=Zmin Z m e d = Z m i n 或者 Zmed=Zmax Z m e d = Z m a x ,则认为是噪声点,应该扩大窗口尺寸,在一个更大的范围内寻找一个合适的非噪声点,随后再跳转到B,否则输出的中值点是噪声点;
接下来考虑跳转到B之后的情况:判断中心点的像素值是否是噪声点,判断条件为 Zmin<Zxy<Zmax Z m i n < Z x y < Z m a x ,原理同上,因为如果 Zxy=Zmin Z x y = Z m i n 或者 Zxy=Zmax Z x y = Z m a x ,则认为是噪声点。如果不是噪声点,我们可以保留当前像素点的灰度值;如果是噪声点,则使用中值替代原始灰度值,滤去噪声。

程序实现

程序中定义了产生椒噪声和盐噪声函数,以及中值滤波和自适应中值滤波的函数。
程序很基础,不做赘述。

#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>//盐噪声  
void saltNoise(cv::Mat img, int n)
{int x, y;for (int i = 0;i < n / 2;i++){x = std::rand() % img.cols;y = std::rand() % img.rows;if (img.type() == CV_8UC1){img.at<uchar>(y, x) = 255;}else if (img.type() == CV_8UC3){img.at<cv::Vec3b>(y, x)[0] = 255;img.at<cv::Vec3b>(y, x)[1] = 255;img.at<cv::Vec3b>(y, x)[2] = 255;}}
}//椒噪声  
void pepperNoise(cv::Mat img, int n)
{int x, y;for (int i = 0;i < n / 2;i++){x = std::rand() % img.cols;y = std::rand() % img.rows;if (img.type() == CV_8UC1){img.at<uchar>(y, x) = 0;}else if (img.type() == CV_8UC3){img.at<cv::Vec3b>(y, x)[0] = 0;img.at<cv::Vec3b>(y, x)[1] = 0;img.at<cv::Vec3b>(y, x)[2] = 0;}}
}// 中值滤波器
uchar medianFilter(cv::Mat img, int row, int col, int kernelSize)
{std::vector<uchar> pixels;for (int y = -kernelSize / 2;y <= kernelSize / 2;y++){for (int x = -kernelSize / 2;x <= kernelSize / 2;x++){pixels.push_back(img.at<uchar>(row + y, col + x));}}sort(pixels.begin(), pixels.end());auto med = pixels[kernelSize*kernelSize / 2];return med;
}// 自适应中值滤波器
uchar adaptiveMedianFilter(cv::Mat &img, int row, int col, int kernelSize, int maxSize)
{std::vector<uchar> pixels;for (int y = -kernelSize / 2;y <= kernelSize / 2;y++){for (int x = -kernelSize / 2;x <= kernelSize / 2;x++){pixels.push_back(img.at<uchar>(row + y, col + x));}}sort(pixels.begin(), pixels.end());auto min = pixels[0];auto max = pixels[kernelSize*kernelSize - 1];auto med = pixels[kernelSize*kernelSize / 2];auto zxy = img.at<uchar>(row, col);if (med > min && med < max){// to Bif (zxy > min && zxy < max)return zxy;elsereturn med;}else{kernelSize += 2;if (kernelSize <= maxSize)return adaptiveMedianFilter(img, row, col, kernelSize, maxSize);// 增大窗口尺寸,继续A过程。elsereturn med;}
}int main()
{int minSize = 3;int maxSize = 7;cv::Mat img;img = cv::imread("lena.bmp");cv::cvtColor(img, img, cv::COLOR_BGR2GRAY);cv::imshow("src", img);saltNoise(img, 40000);pepperNoise(img, 40000);cv::imshow("noise", img);cv::Mat temp = img.clone();// 自适应中值滤波cv::Mat img1;// 扩展图像的边界cv::copyMakeBorder(img, img1, maxSize / 2, maxSize / 2, maxSize / 2, maxSize / 2, cv::BorderTypes::BORDER_REFLECT);// 图像循环for (int j = maxSize / 2;j < img1.rows - maxSize / 2;j++){for (int i = maxSize / 2;i < img1.cols - maxSize / 2;i++){img1.at<uchar>(j, i) = adaptiveMedianFilter(img1, j, i, minSize, maxSize);}}cv::imshow("adaptiveMedianFilter", img1);// 中值滤波cv::Mat img2;int kernelSize = 3;cv::copyMakeBorder(temp, img2, kernelSize / 2, kernelSize / 2, kernelSize / 2, kernelSize / 2, cv::BorderTypes::BORDER_REFLECT);for (int j = kernelSize / 2;j < img2.rows - kernelSize / 2;j++){for (int i = kernelSize / 2;i < img2.cols - kernelSize / 2;i++){img2.at<uchar>(j, i) = medianFilter(img2, j, i, kernelSize);}}cv::imshow("medianFilter", img2);cv::waitKey();cv::destroyAllWindows();return 0;
}

结果截图

原始图像和添加椒盐噪声后的图像。
png
其实截图中看不出很明显的区别,在自己电脑上运行后看结果会清楚点。可以看到使用普通中值滤波的结果相比另一个会模糊一些,且局部仍然会有一小的噪声点。图像边缘多出来的区域不好做中值滤波处理保留了原始图像,所以边缘那一圈仍然有噪声。
png


http://chatgpt.dhexx.cn/article/EbFT9h74.shtml

相关文章

浅谈自适应滤波器

在通常的滤波场合中&#xff0c;从频域的角度进行滤波&#xff0c;其相关的理论已经相当的成熟&#xff0c;只要给出相应的设计指标就可以很方便的设计出满足要求的滤波器。然而在更一般的情况下&#xff0c;人们所需要的滤波器工作的环境是时变的&#xff0c;这就导致事先已经…

自适应滤波原理

在网上&#xff0c;看到一篇不错的自适应滤波原理讲解的文章&#xff0c;原文网址为&#xff1a;自适应滤波原理简介 全文包括&#xff1a; 1. 自适应滤波器简介 2. 自适应干扰抵消原理 3. 自适应滤波原理 4. 最小均方&#xff08;LMS&#xff09;算法 5. Matlab实现 一、自适…

自适应滤波算法综述

我要讲的几种方法 绪论自适应滤波的基本原理自适应滤波算法自适应滤波算法种类最小均方误差算法&#xff08;LMS&#xff09;递推最小二乘算法&#xff08;RLS&#xff09;变换域自适应滤波算法仿射投影算法其他 自适应滤波算法性能评价 自适应滤波的Matlab仿真正弦信号加噪的L…

Java反射机制你还不会?那你怎么看Spring源码

文章目录 1.Java代码在计算机中经历的阶段&#xff1a;三个阶段2.Java识别类和对象信息的两种方式3.什么是反射4.获取Class对象的方式5.Class对象的功能6.通过反射操作类中的成员变量、构造函数、方法7.案例8.反射的优缺点 1.Java代码在计算机中经历的阶段&#xff1a;三个阶段…

Java 反射 理解

Java 反射 定义 Java的反射&#xff08;reflection&#xff09;机制是指在程序的运行状态中&#xff0c;可以构造任意一个类的对象&#xff0c;可以了解任意一个对象所属的类&#xff0c;可以了解任意一个类的成员变量和方法&#xff0c;可以调用任意一个对象的属性和方法。 简…

Java反射机制的原理和用途

看了好多关于Java反射机制的文章&#xff0c;大多都太过官方&#xff0c;消化起来比较稍显费劲&#xff0c;本篇&#xff0c;我会依据自己的理解去阐述什么是Java的反射机制&#xff0c;反射用在什么地方&#xff0c;以及怎么来使用&#xff1f; 开篇前&#xff0c;我们还是要了…

Java反射详解及作用

参考视频链接: 哔哩哔哩视频. 1. 反射概述 能够分析类能力的程序叫做反射(reflective)&#xff0c;对于任何一个Class类&#xff0c;反射可以在运行时直接得到这个类的全部成分&#xff0c;包括构造器&#xff0c;成员方法&#xff0c;成员变量。获得的构造器对象为Construct…

JAVA反射机制分析-------spring的通过反射创建bean实例对象以及属性注入的原理解析

JAVA反射机制 java反射机制是在运行状态中&#xff0c;对于任意一个类&#xff0c; 能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象,都能够调用他的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称为JAVA语言的反射机制。 巧妙的利用ja…

【Spring】spring的反射机制详解

一、什么是反射&#xff1a; &#xff08;1&#xff09;Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息&#xff0c;从而操作类或对象的属性和方法。本质是JVM得到class对象之后&#xff0c;再通过class对象进行反编译&#xff0c;从而获取对象的各种信息。 &…

Spring————java的反射机制,Spring的IOC和DI

一、认识Spring 1.1、Spring家族 SpringFramework&#xff1a; Spring框架&#xff1a;是Spring中最早核心的技术&#xff0c;也是所有其他技术及的基础。 SpringBoot:Spring是用来简化开发。而SpringBoot是来帮助Spring在简化的基础上能更快速进行开发。 SpringCloud&#xf…

分布式定时任务调度实战

目录 1、为什么需要定时任务 2、定时任务调度框架 2.1 单机 2.2 分布 3、xxl-job和elastic-job对比 3.1 支持集群部署方式 3.2 多节点部署任务执行方式 3.3 日志可追溯 3.4 监控告警 3.5 弹性扩容缩容 3.6 支持并行调度 3.7 高可用策略 3.8 失败处理策略 3.9 动态…

浅谈传统定时任务和分布式定时任务

为什么用定时任务&#xff1f; 定时任务平台可以在后台自动检测数据并进行操作。主要应用在订单状态改变、后台统计、定时发送邮件或短信等。 定时任务怎么部署实现&#xff1f; 传统的定时任务可以通过可定时线程池、timertask、quartz、spring-schedule方式来进行处理。他…

分布式定时任务技术选型

1、目前的定时任务方案 Java中开发大多数使用Spring-Scheduler&#xff0c;只需要在Spring中的bean的对应方法加上sheduler注解即可完成我们的定时任务&#xff0c;但是光是用这个注解还远远不能保证定时任务执行多次&#xff0c;我们需要一些其他手段的保证&#xff0c;一般来…

java 分布式 定时任务_Java中实现分布式定时任务的方法

定时器Scheduler在平时使用比较频繁&#xff0c;在springboot中&#xff0c;配置好Scheduled和EnableScheduling之后&#xff0c;定时器就能正常执行&#xff0c;实现定时任务的功能。 但是在这样的情况下&#xff1a;如果开发的服务需要水平部署实现负载均衡&#xff0c;那么定…

【手把手】分布式定时任务调度解析之Quartz

1、任务调度背景 在业务系统中有很多这样的场景&#xff1a; 1、账单日或者还款日上午 10 点&#xff0c;给每个信用卡客户发送账单通知&#xff0c;还款通知。如何判断客户的账单日、还款日&#xff0c;完成通知的发送&#xff1f; 2、银行业务系统&#xff0c;夜间要完成跑批…

轻量级分布式定时任务框架XXL-Job

轻量级分布式定时任务框架XXL-Job: XXL-JOB是一款轻量级的分布式定时任务框架&#xff0c;上手简单&#xff0c;操作容易&#xff0c;XXL-Job可以到官网下载也可以去gitee上拉取源码&#xff0c;其中核心模块分页两个&#xff1a;1&#xff1a;是分布式调度服务&#xff0c; 2&…

Springboot结合Redis实现分布式定时任务

一、背景 之前分享过分布式定时任务的技术选型方案&#xff1a;分布式定时任务技术选型方案&#xff0c;个人青睐xxl_job&#xff0c;分享了搭建接入流程&#xff1a;xxl_job搭建方案&#xff0c;本次项目需求较为简单&#xff0c;同时时间紧张。下面介绍利用Redis锁实现分布式…

分布式定时任务框架说明

分布式定时任务框架说明 分布式定时任务框架说明Quartz概念架构组件springboot集成方式使用内存使用数据库 TBSchedule&#xff1a;elastic-job概念架构组件执行流程特性 satumxxl-job概念特性架构组件使用 分布式定时任务框架说明 Quartz 概念 Quartz&#xff1a;Java事实上…

分布式定时任务对比

1. 什么是分布式定时任务 把分散的&#xff0c;可靠性差的计划任务纳入统一的平台&#xff0c;并实现集群管理调度和分布式部署的一种定时任务的管理方式。叫做分布式定时任务。 2. 常见开源方案 elastic-job , xxl-job &#xff0c;quartz , saturn, opencron , antares el…

简单粗暴的分布式定时任务解决方案

分布式定时任务 1.为什么需要定时任务&#xff1f;2.数据库实现分布式定时任务3.基于redis实现 1.为什么需要定时任务&#xff1f; 因为有时候我们需要定时的执行一些操作&#xff0c;比如业务中产生的一些临时文件&#xff0c;临时文件不能立即删除&#xff0c;因为不清楚用户…