OpenCV-AMF算法(自适应中值滤波Adaptive Median Filtering)

news2024/11/16 16:36:19

作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

实现原理

       AMF(Adaptive Median Filter,自适应中值滤波)是一种用于图像处理和信号处理的滤波算法,其目的是在保持图像细节的同时去除噪声。它是基于中值滤波的一种改进,可以根据局部像素的灰度值特征自适应地调整滤波器的大小和形状。

       AMF算法的主要思想是根据像素邻域的特征动态调整滤波器的尺寸和形状,以适应不同区域的噪声水平和图像细节。具体步骤如下:

  1. 参数和初始化

    • 函数接受一个输入图像input和一个整数参数winSize,表示滤波器的初始大小。
    • 创建一个和输入图像大小相同的输出图像output,用于存储滤波结果。
    • 变量 R 为窗口半径的最大限制值。
  2. 并行遍历处理图像每个像素

    • 使用 OpenMP 实现并行化,对图像的每一个像素进行处理。注意需要在设置里打开OpenMP。
  3. 窗口处理

    • 对于每个像素(i, j),以其为中心构建一个大小为(2*r+1)*(2*r+1)的窗口。
    • 在窗口边界进行限制,防止越界。
  4. 窗口数据排序

    • 将窗口内的像素值存储在 datas 向量中,并进行排序。
  5. 计算中值和最值

    • 计算排序后的数据中的最小值 minValue、最大值 maxValue 和中值 midValue
  6. 判断是否需要扩大窗口

    • 根据中值与最小值、最大值的差值是否超过某个阈值 thresh 来决定是否需要扩大窗口。
    • 如果窗口内的中值不在最小值和最大值之间一定范围内,则继续扩大窗口,直到r大于R。
  7. 滤波处理

    • 如果当前像素值与窗口最值的差值大于阈值,说明不需要滤波,直接将当前像素值赋给输出图像的对应位置。
    • 否则,将窗口内的中值赋给输出图像的对应位置。
  8. 返回结果

    • 返回处理后的输出图像。

       AMF算法的优点是能够有效地处理不同区域的噪声和图像细节,因为它在滤波器大小和形状上具有自适应性。然而,它也有一些缺点,比如计算复杂度较高,处理速度较慢,特别是对于大尺寸的滤波器。

       总的来说,AMF算法是一种灵活而有效的滤波器,特别适用于那些噪声水平不均匀且图像细节丰富的场景,尤其是针对椒盐噪声。

功能函数代码

// AMF算法(Adaptive Median Filtering)
cv::Mat adaptiveMedianFiltering(cv::Mat input, int winSize)
{
	int row = input.rows;
	int col = input.cols;
	cv::Mat output(row, col, CV_8UC1);
	int R = winSize / 2;
	// 遍历处理
#pragma omp parallel for
	for (int i = 0; i < row; i++)
	{
		vector<float> datas;
		for (int j = 0; j < col; j++)
		{
			int r = 1;
			while (r <= R)
			{
				// 卷积窗口边界限制,防止越界
				int ms = ((i - r) > 0) ? (i - r) : 0;
				int me = ((i + r) < (row - 1)) ? (i + r) : (row - 1);
				int ns = ((j - r) > 0) ? (j - r) : 0;
				int ne = ((j + r) < (col - 1)) ? (j + r) : (col - 1);
				// 窗口内有效数据排序
				datas.clear();
				for (int m = ms; m <= me; ++m)
				{
					for (int n = ns; n <= ne; ++n)
					{
						datas.push_back(float(input.at<uchar>(m, n)));
					}
				}
				sort(datas.begin(), datas.end());
				// 计算数值
				float minValue = datas[0];
				float maxValue = datas[datas.size() - 1];
				float midValue = datas[datas.size() / 2];
				// 若窗口内中值不为在最值一定范围内,则说明当前窗口尺寸足够,不需要扩展也可完成有效滤波;反之,则继续扩大窗口,直到最大窗口尺寸
				float thresh = (maxValue - minValue) * 0.02f;
				if ((midValue - minValue) > thresh && (maxValue - midValue) > thresh)
				{
					// 若数据本身就不为最值,则不需要滤波,这也是自适应算法保持高分辨的关键
					if ((input.at<uchar>(i, j) - minValue) > thresh && (maxValue - input.at<uchar>(i, j)) > thresh)
					{
						output.at<uchar>(i, j) = input.at<uchar>(i, j);
					}
					// 若为最值,则说明当前数值大概率是噪声信息,进行中值滤波
					else
					{
						output.at<uchar>(i, j) = uchar(midValue);
					}
					break;
				}
				else
				{
					r++;
				}
				// 如果窗口尺寸达到限制了,则直接滤波
				if (r > R)
				{
					output.at<uchar>(i, j) = uchar(midValue);
				}
			}
		}
	}
	return output;
}

C++测试代码

#include <algorithm>
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

// AMF算法(Adaptive Median Filtering)
cv::Mat adaptiveMedianFiltering(cv::Mat input, int winSize)
{
	int row = input.rows;
	int col = input.cols;
	cv::Mat output(row, col, CV_8UC1);
	int R = winSize / 2;
	// 遍历处理
#pragma omp parallel for
	for (int i = 0; i < row; i++)
	{
		vector<float> datas;
		for (int j = 0; j < col; j++)
		{
			int r = 1;
			while (r <= R)
			{
				// 卷积窗口边界限制,防止越界
				int ms = ((i - r) > 0) ? (i - r) : 0;
				int me = ((i + r) < (row - 1)) ? (i + r) : (row - 1);
				int ns = ((j - r) > 0) ? (j - r) : 0;
				int ne = ((j + r) < (col - 1)) ? (j + r) : (col - 1);
				// 窗口内有效数据排序
				datas.clear();
				for (int m = ms; m <= me; ++m)
				{
					for (int n = ns; n <= ne; ++n)
					{
						datas.push_back(float(input.at<uchar>(m, n)));
					}
				}
				sort(datas.begin(), datas.end());
				// 计算数值
				float minValue = datas[0];
				float maxValue = datas[datas.size() - 1];
				float midValue = datas[datas.size() / 2];
				// 若窗口内中值不为在最值一定范围内,则说明当前窗口尺寸足够,不需要扩展也可完成有效滤波;反之,则继续扩大窗口,直到最大窗口尺寸
				float thresh = (maxValue - minValue) * 0.02f;
				if ((midValue - minValue) > thresh && (maxValue - midValue) > thresh)
				{
					// 若数据本身就不为最值,则不需要滤波,这也是自适应算法保持高分辨的关键
					if ((input.at<uchar>(i, j) - minValue) > thresh && (maxValue - input.at<uchar>(i, j)) > thresh)
					{
						output.at<uchar>(i, j) = input.at<uchar>(i, j);
					}
					// 若为最值,则说明当前数值大概率是噪声信息,进行中值滤波
					else
					{
						output.at<uchar>(i, j) = uchar(midValue);
					}
					break;
				}
				else
				{
					r++;
				}
				// 如果窗口尺寸达到限制了,则直接滤波
				if (r > R)
				{
					output.at<uchar>(i, j) = uchar(midValue);
				}
			}
		}
	}
	return output;
}

void main()
{
	cv::Mat image = cv::imread("test.jpg", cv::IMREAD_GRAYSCALE);
	// 添加随机椒盐噪声
	int row = image.rows;
	int col = image.cols;
	cv::Mat noise = image.clone();
	for (int i = 0; i < row; i += 10)
	{
		for (int j = 0; j < col; j += 10)
		{
			noise.at<uchar>(i, j) = rand() % 255;
		}
	}
	// 传统中值滤波
	int winSize = 15;
	cv::Mat median;
	cv::medianBlur(noise, median, winSize);
	// AMF算法处理
	cv::Mat resultimage = adaptiveMedianFiltering(noise, winSize);
	// 显示
	cv::imshow("ori", image);
	cv::imshow("noise", noise);
	cv::imshow("AMF", resultimage);
	cv::waitKey(0);
	cout << "end." << endl;
}

测试效果 

图1 原图
图2 噪声图
图3 传统中值滤波算法效果
图4 AMF算法效果图

       如上图所示,AMF算法在保留图像细节的同时有效去除了椒盐噪声。

       Matlab版本见:

Matlab-AMF算法(自适应中值滤波Adaptive Median Filtering)-CSDN博客   

       如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~

       如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

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

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

相关文章

文献学习-35-用于评估血流储备分数的条件物理信息图神经网络

Conditional Physics-Informed Graph Neural Network for Fractional Flow Reserve Assessment Authors: Baihong Xie, Xiujian Liu, Heye Zhang, Chenchu Xu, Tieyong Zeng, Yixuan Yuan, Guang Yang, and Zhifan Gao Source: MICCAI 2023 Abstract 血流储备分数 (FFR) 的评…

linux 自定义命令/别名

参考资料 Linux(Ubuntu)自定义命令的使用Linux/Ubuntu系统自定义Shell命令Ubuntu/Linux 操作系统 自定义命令 目录 一. 为路径取别名二. 修改.profile文件2.1 .profile简介2.2 需求2.3 修改.profile文件 三. 创建软链接 一. 为路径取别名 ⏹需求&#xff1a;有一个work文件夹…

学习STM32第十六天

RTC实时时钟 一、简介 RTC是一个独立的BCD格式定时器&#xff0c;提供一个时钟日历&#xff0c;两个可编程报警中断&#xff0c;一个具有中断功能周期性可编程唤醒标志&#xff0c;RTC和时钟配置系统处于后备区域。 通过两个32位寄存器以BCD格式实现秒、分钟、小时&#xff08…

如何在阿里云主机上安装FreeBSD14系统

文章目录 在阿里云主机上安装FreeBSD14系统准备阿里云云主机识别目标磁盘下载 FreeBSD14解压缩 FreeBSD14系统镜像创建可启动的磁盘启动 FreeBSD14在阿里云主机上安装FreeBSD14系统 阿里云主机不支持 FreeBSD14 系统的镜像,因此需要手动进行安装。 准备阿里云云主机 在阿里云…

千锤百炼算法系列之动态规划

题外话 这段时间,我必须把算法弄明白 这篇直接讲解动态规划所有细节! 前面那篇 千锤百炼之每日算法(一)-CSDN博客 也有关于动态规划的讲解,也非常详细 很简单,我成尊不就是了?!!! 正题 动态规划 这里我们主要是让大家明白什么是动态规划,怎么用动态规划解题 我就不用…

数据结构之排序了如指掌(二)

目录 题外话 正题 选择排序 选择排序思路 选择排序代码详解 选择排序复杂度 双向选择排序 双向选择排序思路 双向选择排序代码详解 堆排序 堆排序思路 堆排序代码详解 堆排序复杂度 冒泡排序 冒泡排序思路 冒泡排序代码详解 冒泡排序复杂度 小结 题外话 今天…

2024年第十六届“华中杯”(A题)大学生数学建模挑战赛| 物理建模,多目标优化| 数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们来看看华中杯 (A题&#xff09;&#xff01; CS团队倾…

最简单的例子说清楚量化对冲----量化中性策略 vs DMA策略

一个简单的例子 何为中性对冲&#xff0c;比如股票市场上有一个指数叫做中国指数&#xff0c;由十支股票组成。现在每一只股票买一万块&#xff0c;共十万。这样子我们是不是相当于等权的买了整个指数所包含的全部股票。现在出现一个东西叫做股指期货&#xff0c;股指期货的作…

Redis: 集群

文章目录 一、单点Redis的问题二、主从架构1、概述2、集群结构3、主从数据同步原理&#xff08;1&#xff09;全量同步&#xff08;2&#xff09;增量同步 4、总结&#xff08;1&#xff09;全量同步和增量同步的区别&#xff08;2&#xff09;什么时候执行全量同步&#xff08…

基础拓扑学习

基础拓扑 有限集、可数集和不可数集 2.1 定义 考虑两个集 A A A和 B B B&#xff0c;他们的元素可以是任何东西。假定对于 A A A的每个元素 x x x&#xff0c;按照某种方式&#xff0c;与集 B B B的一个元素联系着&#xff0c;这个元素记作 f ( x ) f\left( x \right) f(x);那…

Django框架设计原理

相信大多数的Web开发者对于MVC&#xff08;Model、View、Controller&#xff09;设计模式都不陌生&#xff0c;该设计模式已经成为Web框架中一种事实上的标准了&#xff0c;Django框架自然也是一个遵循MVC设计模式的框架。不过从严格意义上讲&#xff0c;Django框架采用了一种更…

Syncthing实时共享同步数据,可用于异地备份数据

一、Syncthing概述 Syncthing的主要功能是用来进行文件传输&#xff0c;我需要对多台不同系统的&#xff08;windows&#xff0c;linux&#xff09;服务器的数据进行备份&#xff0c;当前测试使用Syncthing来进行两台Centos7服务器数据备份&#xff0c;注意在不同服务器使用Syn…

手写商城项目学习/复习到的知识

1.在windowr创建项目可以选择自定义/vue2/vue3,但尝试在vscode不能选择. 2.vant vant是组件库,可导入结构等.vant2用于vue2,vant3,vant\4用于vue3 vant2的使用 官网: Vant 2 - 轻量、可靠的移动端组件库 (gitee.io) 全部导入:将vant所有的组件放到了所有组件内component使…

web自动化测试系列-selenium常用方法定位(五)

目录 1.selenium的定位方法 2.操作案例 3.实现代码 前面我们介绍了html页面元素主要是通过标签和属性来进行定位 &#xff0c;只要满足唯一&#xff0c;无论是标签还是属性 &#xff0c;都能进行定位 。当然 &#xff0c;我们要通过selenium来进行定位 &#xff0c;同样还是…

Python:可迭代对象与迭代器

相关阅读 Pythonhttps://blog.csdn.net/weixin_45791458/category_12403403.html?spm1001.2014.3001.5482 根据Python官方文档&#xff0c;可迭代对象(iterable)是“一种能够逐个返回其成员项的对象”。具体来说&#xff0c;这种对象要么定义了一个返回迭代器(iterator)的魔术…

深入解析Nacos配置中心的动态配置更新技术

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 在微服务架构中&#xff0c;配置管理变得尤为关键。Nacos&#xff0c;作为一个开源的、易于使用的、功能丰富的平台&#xff0c;为…

python 无处不在的二分搜索

我们知道二分查找算法。二分查找是最容易正确的算法。我提出了一些我在二分搜索中收集的有趣问题。有一些关于二分搜索的请求。我请求您遵守准则&#xff1a;“我真诚地尝试解决问题并确保不存在极端情况”。阅读完每个问题后&#xff0c;最小化浏览器并尝试解决它。 …

从计算机视觉到生命科学

人工智能技术的快速发展正在深刻影响和重塑我们的生活。作为AI领域的前沿方向,多模态大模型凭借其强大的跨域学习和推理能力,在众多行业和科学领域展现出广阔的应用前景。多模态AI指的是能够同时处理和整合文本、图像、音频、视频等不同模态数据的智能系统。这种融合不同信息源…

Java详解:GUI容器组件 | 功能组件

✎ 简介&#xff1a; Graphical User Interface&#xff08;GUI&#xff09;图形用户界面 图形界面对于用户来说在视觉上更易于接受. ✎ 关于swing: • swing是一个为java设计的GUI工具包javax.swing&#xff0c;包括了用户界面的各种组件. • swing中组件可以分为两大类&…

刷题之Leetcode19题(超级详细)

19.删除链表的倒数第N个节点 力扣题目链接(opens new window)https://leetcode.cn/problems/remove-nth-node-from-end-of-list/ 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 进阶&#xff1a;你能尝试使用一趟扫描实现吗&#x…