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

《opencv实用探索·九》中值滤波简单理解

1、引言
均值滤波、方框滤波、高斯滤波,都是线性滤波方式。由于线性滤波的结果是所有像素值的线性组合,因此含有噪声的像素也会被考虑进去,噪声不会被消除,而是以更柔和的方式存在。这时使用非线性滤波效果可能会更好。中值滤波是一种非线性滤波方式,不再采用加权求均值的方式计算滤波结果,基本原理是把数字图像或数字序列中一点的值用该点的一个邻域中各点值的中值代替,让周围的像素值接近的真实值,从而消除孤立的噪声点。

优缺点:中值滤波可以有效的去除斑点和椒盐噪声。但是效率低,其运算时间 为均值滤波的五倍以上

2、中值滤波过程
首先我们先取一个nxn的滤波核,该核是一个空核,核中不在有权重等数字,把滤波核放在图像上滑动,每滑动到一个位置,就把核覆盖下的所有像素值进行排序,然后取排序后中间的像素值替换被核中心覆盖下的原图像像素值。下面演示下滤波过程。

先取一个3x3的空核:
在这里插入图片描述
把3x3的空核放在原图像上进行滑动:
在这里插入图片描述
计算被核覆盖下的像素中值,以上图核放在左上角为例:
先给所有像素进行排序,正序和逆序都可,这里使用正序
20,24,28,35,47,59,68,79,99

由排序可知中间值为:47
最后把中间值替换被核中心覆盖的像素值,即68会重新赋值为47,随着核滑动便可依次计算被核中心覆盖的像素值的中值,核中心滑动过的像素如下图阴影部分。
在这里插入图片描述

3、opencv中值滤波函数使用

void cv::medianBlur(InputArray   src,OutputArray dst,int  ksize
)

src:原图像,可以是单通道,三通道和四通道,数据类型与滤波器的尺寸相关,当滤波器尺寸为3或5时,图像可以是CV 8U,CV 16U或CV 32F类型,对于较大尺寸的滤波器,数据类型只能是CV 8U
dst:滤波后的图像
ksize:滤波核大小,必须是奇数,这样才有核中心

对原图分别执行3x3和9x9滤波,实现效果如下:
原图:
在这里插入图片描述
左边是3x3滤波,右边是9x9滤波
在这里插入图片描述

4、滤波核根据图像自适应调整大小
我们在调用medianBlur时需要手动传入一个滤波核大小,下面的一个demo介绍了核的大小根据局部邻域的均值和标准差进行自适应调整:

#include <opencv2/opencv.hpp>cv::Mat adaptiveMedianBlur(const cv::Mat& src, int maxWindowSize) {cv::Mat result = src.clone();int numChannels = src.channels();for (int y = maxWindowSize / 2; y < src.rows - maxWindowSize / 2; ++y) {for (int x = maxWindowSize / 2; x < src.cols - maxWindowSize / 2; ++x) {int windowSize = 3;  // 初始核大小while (windowSize <= maxWindowSize) {// 提取局部邻域cv::Mat region = src(cv::Range(y - windowSize / 2, y + windowSize / 2 + 1),cv::Range(x - windowSize / 2, x + windowSize / 2 + 1));// 计算标准差cv::Scalar mean, stddev;cv::meanStdDev(region, mean, stddev);// 中值滤波if (numChannels == 1 && src.at<uchar>(y, x) < mean[0] + stddev[0] * 0.5) {// 单通道图像中值滤波cv::Mat subRegion = result(cv::Range(y - windowSize / 2, y + windowSize / 2 + 1),cv::Range(x - windowSize / 2, x + windowSize / 2 + 1));cv::medianBlur(region, subRegion, windowSize);break;} else if (numChannels == 3 &&src.at<cv::Vec3b>(y, x)[0] < mean[0] + stddev[0] * 0.5 &&src.at<cv::Vec3b>(y, x)[1] < mean[1] + stddev[1] * 0.5 &&src.at<cv::Vec3b>(y, x)[2] < mean[2] + stddev[2] * 0.5) {// 三通道图像中值滤波cv::Mat subRegion = result(cv::Range(y - windowSize / 2, y + windowSize / 2 + 1),cv::Range(x - windowSize / 2, x + windowSize / 2 + 1));cv::medianBlur(region, subRegion, windowSize);break;} else {// 增大核大小windowSize += 2;if (windowSize > maxWindowSize) {break;}}}}}return result;
}int main() {// 读取图像cv::Mat image = cv::imread("input_image.jpg", cv::IMREAD_GRAYSCALE);// 应用自适应中值滤波cv::Mat result = adaptiveMedianBlur(image, 11);// 显示原始图像和处理后的图像cv::imshow("Original Image", image);cv::imshow("Adaptive Median Blur Image", result);cv::waitKey(0);cv::destroyAllWindows();return 0;
}

下面对代码做一些解释:
(1)函数需要传入一个原图像和一个滤波核的上限,表示该图像做中值滤波时最大只能用maxWindowSize
(2)最上面两层for循环遍历核中心扫过的像素,例如一个8x8图像被maxWindowSize = 5x5的核扫过的区域如下阴影部分:
在这里插入图片描述
这两层循环确保我们只处理在图像内部且不会越界的像素。这是为了确保局部邻域的提取和处理都在图像内部进行

(3) 根据当前核大小提取局部邻域
cv::Mat region = src(cv::Range(y - windowSize / 2, y + windowSize / 2 + 1),
cv::Range(x - windowSize / 2, x + windowSize / 2 + 1));
代码以被最大核扫过的每个像素分别为当前核的一个中心,提取一个局部邻域,比如当前核为3x3,第一个被扫过像素的局部邻域如下:
在这里插入图片描述
(4)计算邻域的均值和标准差
(5)
if (src.at(y, x) < mean[0] + stddev[0] * 0.5)
这个条件的意思是:如果当前像素 (y, x) 的值小于局部邻域的平均灰度值加上标准差的一半,就执行中值滤波。这样的判断条件旨在处理相对较小的像素值,因为在图像中的边缘或包含细节的区域,这些值可能代表着重要的信息。在这些情况下,使用中值滤波有助于更好地保留细节。如果像素值相对较大,可能处于较均匀的区域,就不执行中值滤波,以免过度平滑图像。

加上标准差的一半的目的是提高容错性,使得判断更加灵活。这个设计的理念是在图像中的一些相对较暗的区域或包含细节的区域,由于灰度值的波动,可能出现一些像素的值略低于整体平均值。通过引入标准差的一半,可以允许更大的变化范围,从而更好地适应图像的局部特征。当然也可以把标准差的一半换成一个固定的值,具体可以根据实验来调整。

下面是带三通道的情况:

cv::Mat adaptiveMedianBlur1(const cv::Mat& src, int maxWindowSize) {cv::Mat result = src.clone();int numChannels = src.channels();for (int y = maxWindowSize / 2; y < src.rows - maxWindowSize / 2; ++y) {for (int x = maxWindowSize / 2; x < src.cols - maxWindowSize / 2; ++x) {int windowSize = 3;  // 初始核大小while (windowSize <= maxWindowSize) {// 提取局部邻域cv::Mat region = src(cv::Range(y - windowSize / 2, y + windowSize / 2 + 1),cv::Range(x - windowSize / 2, x + windowSize / 2 + 1));// 计算标准差cv::Scalar mean, stddev;cv::meanStdDev(region, mean, stddev);// 中值滤波if (numChannels == 1 && src.at<uchar>(y, x) < mean[0] + stddev[0] * 0.5) {// 单通道图像中值滤波cv::Mat subRegion = result(cv::Range(y - windowSize / 2, y + windowSize / 2 + 1),cv::Range(x - windowSize / 2, x + windowSize / 2 + 1));cv::medianBlur(region, subRegion, windowSize);break;}else if (numChannels == 3 &&src.at<cv::Vec3b>(y, x)[0] < mean[0] + stddev[0] * 0.5 &&src.at<cv::Vec3b>(y, x)[1] < mean[1] + stddev[1] * 0.5 &&src.at<cv::Vec3b>(y, x)[2] < mean[2] + stddev[2] * 0.5) {// 三通道图像中值滤波cv::Mat subRegion = result(cv::Range(y - windowSize / 2, y + windowSize / 2 + 1),cv::Range(x - windowSize / 2, x + windowSize / 2 + 1));cv::medianBlur(region, subRegion, windowSize);break;}else {// 增大核大小windowSize += 2;if (windowSize > maxWindowSize) {break;}}}}}return result;
}
在这里插入图片描述

相关文章:

《opencv实用探索·九》中值滤波简单理解

1、引言 均值滤波、方框滤波、高斯滤波&#xff0c;都是线性滤波方式。由于线性滤波的结果是所有像素值的线性组合&#xff0c;因此含有噪声的像素也会被考虑进去&#xff0c;噪声不会被消除&#xff0c;而是以更柔和的方式存在。这时使用非线性滤波效果可能会更好。中值滤波是…...

PC行内编辑

点击编辑&#xff0c;行内编辑输入框出现&#xff0c;给列表的每条数据定义编辑标记&#xff0c;最后一定记得 v-model双向绑定&#xff0c;使数据回显。 步骤&#xff1a; 1、给行数据定义编辑标记 2、点击行编辑标记&#xff08;isedit&#xff09; 3、插槽根据标记渲染表单 …...

鸿蒙开发:Stage模型开发-应用/组件级配置以及UIAbility组件初步使用【鸿蒙专栏-20】

文章目录 Stage模型开发概述基本概念UIAbility组件和ExtensionAbility组件WindowStageContextAbilityStage开发流程应用组件开发了解进程模型了解线程模型应用配置文件应用版本声明配置Module支持的设备类型配置Module权限配置进阶应用配置...

Django回顾【五】

目录 一、多表操作 【1】基于对象的跨表查 【2】基于双下滑线的连表查 【3】related_name 二、聚合查询与分组查询 【1】聚合查询 【2】分组查询 三、F与Q查询 【1】F查询 【2】Q查询 四、其他字段和字段参数 【1】其他字段 【2】ORM字段参数 【3】ForeignKey 属…...

Python容器——字典

Key——Value 键值对...

基于Java SSM框架实现实现四六级英语报名系统项目【项目源码+论文说明】

基于java的SSM框架实现四六级英语报名系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个高校四六级报名管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作…...

翻硬币(第四届蓝桥杯省赛C++B组)(java版)

//翻硬币,每次都会改变两个硬币的状态 //因此我们可以从前往后枚举,s1[i] 与 s2[i] 状态不同就改变它的状态 //同时s1[i 1] 与 s2[i 1] 的状态会因此改变 // 所以继续往下枚举s1[i 1] 与 s2[i 1] //因为题目有说必须有解,因此枚举到 n - 1位的时候&#xff0c;两个字符串的…...

原生GPT本地及云端部署方式保姆级教程

前提条件 部署必须要有一个超过1年的Github账号 本地服务部署 运行效果 部署方法 下载安装包 暂时无法在飞书文档外展示此内容 GitHub授权登录&#xff1a; https://dash.pandoranext.com/ 登录后是这个样子&#xff1a; 复制下面红框里面这个License Id 编辑Config.js…...

Docker容器(一)概述

一、虚拟化概述 1.1引⼊虚拟化技术的必要性 服务器只有5%的时间是在⼯作的&#xff1b;在其它时间服务器都处于“休眠”状态. 虚拟化前 每台主机⼀个操作系统; 软硬件紧密结合; 在同⼀个主机上运⾏多个应⽤程序通常会遭遇冲突; 系统的资源利⽤率低; 硬件成本⾼昂⽽且不够灵活…...

Facebook引流怎么做?写个脚本就好!

在当今的数字化时代&#xff0c;流量对于任何一个网站或应用程序来说都至关重要&#xff0c;Facebook&#xff0c;作为全球最大的社交网络平台&#xff0c;无疑是一个获取流量的绝佳场所&#xff0c;但是&#xff0c;如何有效地从Facebook引流呢?写个脚本就好了! 在本文中&am…...

自动化集成有哪些典型应用场景?

为什么要做自动化场景集成&#xff1f; 主要分为以下几点&#xff1a; 提高效率/减少错误&#xff1a;减少人工操作、人为错误、人力成本&#xff0c;提高生产效率、生产质量和稳定性。 提高可靠性&#xff1a;提高系统的可靠性和稳定性&#xff0c;减少系统故障和停机时间。…...

探讨几种在CentOS 7上实现文件上传的方法

最近服务器过期了&#xff0c;把之前服务器的数据库都备份了下来&#xff0c;现在准备迁移各种服务&#xff0c;这就涉及到文件传输。之前用得多的都是xshell里的xtp来传&#xff0c;校园网禁用了ssh协议&#xff0c;还有一大堆乱七八糟的协议&#xff0c;我一般用的代理方法或…...

AWS EC2使用 instance profile 访问S3

AWS EC2 instance可以使用instance profile 配置访问S3的权限。 然后就可以直接在EC2上执行 python代码或者AWS CLI去访问S3了。 唯一需要注意的地方是&#xff0c;申明region。 示例代码&#xff1a; aws s3 ls xxxx-s3-bucket --region xxx-region import boto3 client …...

python中函数式编程

文章目录 map()函数filter()函数reduce()函数 map()函数 当使用map()函数时&#xff0c;可以使用lambda表达式来定义一个简单的转换函数。 以下是一个使用map()函数和lambda表达式的简单示例&#xff1a; numbers [1, 2, 3, 4, 5] squared_numbers map(lambda x: x**2, nu…...

Java_JDK8到JDK21各版本发行时间及重要特性

Java_JDK8到JDK21各版本发行时间及重要特性 背景JDK8新特性(2014年3月)JDK9新特性&#xff08;2017年9月&#xff09;JDK10新特性&#xff08;2018年3月&#xff09;JDK11新特性&#xff08;2018年9月&#xff09;&#xff08;LTS版本&#xff09;JDK12新特性&#xff08;2019年…...

03 数仓平台 Kafka

kafka概述 定义 Kafka 是一个开源的分布式事件流平台&#xff08;Event Streaming Plantform&#xff09;&#xff0c;主要用于大数据实时领域。本质上是一个分布式的基于发布/订阅模式的消息队列&#xff08;Message Queue&#xff09;。 消息队列 在大数据场景中主要采用…...

2023年全国硕士研究生入学统一考试管理类专业学位联考逻辑试题——解析版

文章目录 2023年全国硕士研究生入学统一考试管理类专业学位联考逻辑试题三、逻辑推理真题(2023-26)真题(2023-27)真题(2023-28)真题(2023-29)真题(2023-30)真题(2023-31-32)真题(2023-33)真题(2023-34)真题(2023-35)真题(2023-36)真题(2023-37-38...

Matlab论文插图绘制模板第129期—函数网格曲面图

在之前的文章中&#xff0c;分享了Matlab函数折线图的绘制模板&#xff1a; 函数三维折线图&#xff1a; 进一步&#xff0c;再来分享一下函数网格曲面图。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『数据代码』已上传资源群中&#xff0c;加群的朋友请自…...

无限移动的风景 css3 动画 鼠标移入暂停

<style>*{margin:0;padding:0;/* box-sizing: border-box; */}ul{list-style: none;}#nav{width:900px;height:100px;border:2px solid rgb(70, 69, 69);margin:100px auto; overflow: hidden;}#nav ul{animation:moving 5s linear infinite;width:200%; /*怎么模拟动画…...

Java基本数据类型、包装类及拆装箱详解

Java的基本数据类型和对应的包装类是Java语言中处理数据的两个关键概念。基本数据类型提供了简单而高效的方式来存储数据&#xff0c;而包装类使得基本数据类型具有对象的特性。本文将深入探讨基本数据类型与包装类的应用场景及详细描述&#xff0c;并对自动拆箱和装箱的源码实…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...

对象回调初步研究

_OBJECT_TYPE结构分析 在介绍什么是对象回调前&#xff0c;首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例&#xff0c;用_OBJECT_TYPE这个结构来解析它&#xff0c;0x80处就是今天要介绍的回调链表&#xff0c;但是先不着急&#xff0c;先把目光…...

大数据驱动企业决策智能化的路径与实践

&#x1f4dd;个人主页&#x1f339;&#xff1a;慌ZHANG-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 一、引言&#xff1a;数据驱动的企业竞争力重构 在这个瞬息万变的商业时代&#xff0c;“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...

GeoServer发布PostgreSQL图层后WFS查询无主键字段

在使用 GeoServer&#xff08;版本 2.22.2&#xff09; 发布 PostgreSQL&#xff08;PostGIS&#xff09;中的表为地图服务时&#xff0c;常常会遇到一个小问题&#xff1a; WFS 查询中&#xff0c;主键字段&#xff08;如 id&#xff09;莫名其妙地消失了&#xff01; 即使你在…...