当前位置: 首页 > 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;
}
在这里插入图片描述

相关文章:

  • Golang实践录:sqlite的使用
  • kettle作业发送@163邮件
  • 在安全环境中使用虚拟化进行隔离——Armv8.4上的安全世界软件架构
  • Nacos多数据源插件
  • Windows 基于 VMware 虚拟机安装银河麒麟高级服务器操作系统
  • 添加新公司代码的配置步骤-Part2
  • 禁奥义·SQL秘籍
  • Java架构师系统架构设计服务拆分应用
  • 汽车软件大时代,如何提升软件工程创新力?
  • LoadBalancer将服务暴露到外部实现负载均衡purelb-layer2模式配置介绍
  • 百度推送收录工具-免费的各大搜索引擎推送工具
  • 软件工程-(可行性分析、需求分析)
  • 3D模型材质编辑
  • 轻量封装WebGPU渲染系统示例<40>- 多层材质的Mask混合(源码)
  • Ubuntu systemd-analyze命令(系统启动性能分析工具:分析系统启动时间,找出可能导致启动缓慢的原因)
  • Android 控件背景颜色处理
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • canvas 高仿 Apple Watch 表盘
  • CSS3 变换
  • CSS魔法堂:Absolute Positioning就这个样
  • HTTP中GET与POST的区别 99%的错误认识
  • Java|序列化异常StreamCorruptedException的解决方法
  • java第三方包学习之lombok
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • js对象的深浅拷贝
  • October CMS - 快速入门 9 Images And Galleries
  • uni-app项目数字滚动
  • 从0实现一个tiny react(三)生命周期
  • ------- 计算机网络基础
  • 前端工程化(Gulp、Webpack)-webpack
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • 【运维趟坑回忆录】vpc迁移 - 吃螃蟹之路
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • #QT(QCharts绘制曲线)
  • #数据结构 笔记一
  • (1)虚拟机的安装与使用,linux系统安装
  • (2)空速传感器
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测
  • (二)延时任务篇——通过redis的key监听,实现延迟任务实战
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (转载)深入super,看Python如何解决钻石继承难题
  • .“空心村”成因分析及解决对策122344
  • .Family_物联网
  • .form文件_一篇文章学会文件上传
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始