使用visual Studio MFC 平台实现对灰度图添加椒盐噪声,并进行均值滤波与中值滤波
平滑处理–滤波
本文使用visual Studio MFC 平台实现对灰度图添加椒盐噪声,并进行均值滤波与中值滤波
关于其他MFC单文档工程可参考
01-Visual Studio 使用MFC 单文档工程绘制单一颜色直线和绘制渐变颜色的直线02-visual Studio MFC 绘制单一颜色三角形、渐变颜色边框三角形、渐变填充三角形、边框渐变的正方形与填充渐变的正方形实例
文章目录
- 平滑处理--滤波
- 一、添加椒盐噪声
- 1.1 添加椒盐噪声的原理
- 1.2 添加椒盐噪声的代码实现
- 1.3 添加椒盐噪声后的效果
- 二、均值滤波
- 2.1 均值滤波的原理
- 2.2 均值滤波的代码实现
- 2.3 均值滤波的实验效果
- 三、 中值滤波
- 3.1 中值滤波的原理
- 3.2 中值滤波的C++实现
- 3.3中值滤波后的效果图
一、添加椒盐噪声
1.1 添加椒盐噪声的原理
添加椒盐噪声是一种常见的图像噪声引入方式,其原理是在图像中随机选择一些像素点,并将这些像素点的灰度值设置为最大或最小值,通常是白色(最大值)或黑色(最小值)。这样的噪声模拟了图像中出现的随机强烈亮或暗的噪声点,类似于椒盐的颗粒,因此得名。
具体步骤如下:
-
选择噪声点: 在图像中随机选择一些像素点作为噪声点。
-
设定噪声值: 对于每个选定的噪声点,将其灰度值设定为最大值(白色)或最小值(黑色)。
这样就在图像中引入了椒盐噪声,这种噪声形式使图像中的某些区域变得非常亮或非常暗,从而增加了图像的复杂性和难度。
椒盐噪声主要用于模拟一些特殊环境下的图像问题,例如图像采集中的传感器错误、传输中的丢包等情况。在图像处理中,去除或减轻椒盐噪声的方法通常包括滤波技术,例如中值滤波。
1.2 添加椒盐噪声的代码实现
//添加椒盐噪声
void CMFCApplication1View::OnAddsaltpeppernoise()
{// TODO: 在此添加命令处理程序代码if (gray_data != nullptr) {// 获取绘图设备CClientDC dc(this);CDC* pDC = &dc;// 创建临时数组用于保存添加噪声后的数据noisy_data = new unsigned char[bmpWidth * bmpHeight];// 复制原始数据到临时数组std::copy(gray_data, gray_data + bmpWidth * bmpHeight, noisy_data);// 添加椒盐噪声srand(static_cast<unsigned int>(time(nullptr))); // 初始化随机数种子const double saltPepperRatio = 0.01; // 椒盐噪声比例for (int i = 0; i < bmpWidth * bmpHeight; ++i) {double randomValue = static_cast<double>(rand()) / RAND_MAX;if (randomValue < saltPepperRatio / 2) {noisy_data[i] = 0; // 添加椒噪声}else if (randomValue < saltPepperRatio) {noisy_data[i] = 255; // 添加盐噪声}}// 绘制带有椒盐噪声的图像m_pBmp->drawGrayBmp(pDC, noisy_data, bmpWidth, bmpHeight, offset_left+2*bmpWidth, offset_top + 3 * bmpHeight);
关于上面将原来灰度图的数据
复制
到新的数组noisy_data中的目的是为了保留添加椒盐噪声后的灰度图的数据,以便后面的平滑操作
1.3 添加椒盐噪声后的效果
二、均值滤波
2.1 均值滤波的原理
均值滤波是一种常见的图像平滑处理方法,其原理基于对图像的像素值进行平均运算。这种滤波方法主要用于去除图像中的噪声或细小的细节,以产生更平滑的图像。
具体步骤如下:
-
定义卷积核: 选择一个固定大小的卷积核,通常是一个正方形的矩阵。卷积核的大小取决于应用场景和对平滑度的需求。
-
卷积操作: 将卷积核应用于图像的每个像素。对于每个像素,将其与卷积核中对应位置的像素相乘,然后将所有乘积的和除以卷积核的总权重。这个和的结果就是卷积核中心像素的新值。
-
更新图像: 将卷积操作得到的新值赋予原始图像相应位置的像素,形成平滑后的图像。
均值滤波的效果在平滑图像的同时,会导致图像失真,特别是对边缘和细节的处理较为粗糙。这是因为平均操作会模糊图像,擦除图像中的高频信息。
公式表示如下,其中 M 为卷积核的大小,f(x, y) 为原始图像,h(i, j) 为卷积核中的权重:
g ( x , y ) = 1 M ∑ i = 1 M ∑ j = 1 M h ( i , j ) ⋅ f ( x + i , y + j ) g(x, y) = \frac{1}{M} \sum_{i=1}^{M} \sum_{j=1}^{M} h(i, j) \cdot f(x+i, y+j) g(x,y)=M1i=1∑Mj=1∑Mh(i,j)⋅f(x+i,y+j)
其中,g(x, y)是滤波后的像素值。
2.2 均值滤波的代码实现
//均值滤波
void CMFCApplication1View::OnMeanfilter()
{// TODO: 在此添加命令处理程序代码if (noisy_data != nullptr) {// 获取绘图设备CClientDC dc(this);CDC* pDC = &dc;// 创建临时数组用于保存均值滤波后的数据unsigned char* filtered_data = new unsigned char[bmpWidth * bmpHeight];// 设置均值滤波的卷积核大小(3x3)const int kernelSize = 3;const int kernelHalfSize = kernelSize / 2;// 应用均值滤波for (int y = kernelHalfSize; y < bmpHeight - kernelHalfSize; ++y) {for (int x = kernelHalfSize; x < bmpWidth - kernelHalfSize; ++x) {int sum = 0;// 计算卷积核内的像素值之和for (int ky = -kernelHalfSize; ky <= kernelHalfSize; ++ky) {for (int kx = -kernelHalfSize; kx <= kernelHalfSize; ++kx) {int pixelValue = noisy_data[(y + ky) * bmpWidth + (x + kx)];sum += pixelValue;}}// 计算均值并赋值给滤波后的像素filtered_data[y * bmpWidth + x] = static_cast<unsigned char>(sum / (kernelSize * kernelSize));}}// 绘制均值滤波后的图像m_pBmp->drawGrayBmp(pDC, filtered_data, bmpWidth, bmpHeight, offset_left + 3 * bmpWidth, offset_top + 3 * bmpHeight);// 在图片下方添加文字GdiplusStartupInput gdiplusStartupInput;ULONG_PTR gdiplusToken;GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);{Graphics graphics(pDC->m_hDC);Gdiplus::Font font(L"Arial", 12);SolidBrush brush(Color(255, 128, 0, 128)); // 文字颜色为紫色// 文字的位置PointF point(offset_left +3* bmpWidth, offset_top + 4 * bmpHeight);// 绘制文字graphics.DrawString(L"均值滤波后的图像", -1, &font, point, &brush);}// 释放临时变量的内存delete[] filtered_data;GdiplusShutdown(gdiplusToken);}else {// 处理图像未加载的情况AfxMessageBox(_T("未加载图片"));}
}
2.3 均值滤波的实验效果
三、 中值滤波
3.1 中值滤波的原理
中值滤波是一种非线性滤波方法,它的原理是用像素点邻域灰度值的中值来代替该像素点的灰度值。中值滤波不会改变图像的灰度平均值,但可以有效地去除图像中的椒盐噪声等离群点,对于保持图像边缘细节方面也有一定的优势。
中值滤波的步骤如下:
-
选择滤波模板大小: 中值滤波通常采用3×3、5×5等奇数大小的模板。选择模板的大小会影响滤波效果。
-
将模板覆盖在图像的每个像素点上: 以当前像素为中心,取模板中所有像素的灰度值。
-
对模板中的灰度值进行排序: 将模板中的灰度值按升序或降序排列。
-
取排序后的中值: 选取排序后的中间值作为当前像素的新灰度值。
-
重复该过程: 将模板在整个图像上滑动,对每个像素应用相同的操作。
中值滤波的优点在于能够有效地去除椒盐噪声,但在一些情况下可能会导致图像细节模糊。它特别适用于去除局部性较强的噪声。
3.2 中值滤波的C++实现
//中值滤波
void CMFCApplication1View::OnMedianfilter()
{// TODO: 在此添加命令处理程序代码if (noisy_data != nullptr) {// 获取绘图设备CClientDC dc(this);CDC* pDC = &dc;// 创建临时数组用于中值滤波处理unsigned char* median_filtered_data = new unsigned char[bmpWidth * bmpHeight];// 中值滤波的处理代码int filter_size = 3; // 中值滤波的邻域大小,可以根据实际情况调整int filter_radius = filter_size / 2;for (int y = filter_radius; y < bmpHeight - filter_radius; ++y) {for (int x = filter_radius; x < bmpWidth - filter_radius; ++x) {// 获取邻域内的像素值std::vector<unsigned char> neighborhood;for (int j = -filter_radius; j <= filter_radius; ++j) {for (int i = -filter_radius; i <= filter_radius; ++i) {neighborhood.push_back(noisy_data[(y + j) * bmpWidth + (x + i)]);}}// 对邻域内的像素值进行排序std::sort(neighborhood.begin(), neighborhood.end());// 计算中值并赋值给当前像素median_filtered_data[y * bmpWidth + x] = neighborhood[filter_size * filter_size / 2];}}// 绘制中值滤波后的图像m_pBmp->drawGrayBmp(pDC, median_filtered_data, bmpWidth, bmpHeight, offset_left + 4 * bmpWidth, offset_top + 3 * bmpHeight);// 在图片下方添加文字GdiplusStartupInput gdiplusStartupInput;ULONG_PTR gdiplusToken;GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);{Graphics graphics(pDC->m_hDC);Gdiplus::Font font(L"Arial", 12);SolidBrush brush(Color(255, 128, 0, 128)); // 文字颜色为紫色// 文字的位置PointF point(offset_left + 4 * bmpWidth, offset_top + 4 * bmpHeight);// 绘制文字graphics.DrawString(L"中值滤波", -1, &font, point, &brush);}// 释放临时数组的内存delete[] median_filtered_data;GdiplusShutdown(gdiplusToken);}else {// 处理图像未加载的情况AfxMessageBox(_T("未加载图片"));}
}
3.3中值滤波后的效果图
相关文章:

使用visual Studio MFC 平台实现对灰度图添加椒盐噪声,并进行均值滤波与中值滤波
平滑处理–滤波 本文使用visual Studio MFC 平台实现对灰度图添加椒盐噪声,并进行均值滤波与中值滤波 关于其他MFC单文档工程可参考 01-Visual Studio 使用MFC 单文档工程绘制单一颜色直线和绘制渐变颜色的直线 02-visual Studio MFC 绘制单一颜色三角形、渐变颜色边…...

Django HMAC 请求签名校验与 Vue.js 实现安全通信
概要 在 Web 应用的开发过程中,确保数据传输的安全性和完整性是一个不容忽视的问题。使用 HMAC(Hash-based Message Authentication Code)算法对请求内容进行签名校验,是一种常见且有效的安全策略。本文将详细介绍如何在 Django …...

深度学习之循环神经网络
视频链接:6 循环神经网络_哔哩哔哩_bilibili 给神经网络增加记忆能力 对全连接层而言,输入输出的维数固定,因此无法处理序列信息 对卷积层而言,因为卷积核的参数是共享的,所以卷积操作与序列的长度无关。但是因为卷积…...

与原有视频会议系统对接
要实现与原有视频会议系统对接,需要确保通信协议的一致性。连通宝视频会议系统可与第三方视频会议系统对接。实现与第三方会议系统对接还可以使用会议室连接器,可以确保不同系统之间的数据传输和交互。 具体对接流程可能因不同品牌和类型的视频会议系统而…...

C# Serilog--可记录异常完整路径
1.Serilog安装 2.控制台代码 --设置日志记录器的最小级别为 Debug,即只记录 Debug 级别及以上的日志信息 --.WriteTo.File("logs\\log.txt", rollingInterval: RollingInterval.Day):将日志信息写入到指定路径的文件中(这里的路径…...

鉴源实验室 | 汽车网络安全攻击实例解析(三)
作者 | 张璇 上海控安可信软件创新研究院工控网络安全组 来源 | 鉴源实验室 社群 | 添加微信号“TICPShanghai”加入“上海控安51fusa安全社区” 引言:随着现代汽车技术的迅速发展,车辆的进入和启动方式经历了显著的演变。传统的物理钥匙逐渐被无钥匙进…...
php 中生成订单号
字母日期。。。。。。。 function setOrderNo($year 2011) {$yCode array(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);$orderSn $yCode[intval(date(Y)) - $year] . strtoupper(dechex(date(m))) . date(d) . substr(time(), -5) . s…...

Jmeter工具+ant+jenkins实现持续集成
jmeterantjenkins持续集成 一、下载并配置jmeter 首先下载jmeter工具,并配置好环境变量;参考: jmeter默认保存的是.jtl格式的文件,要设置一下bin/jmeter.properties,文件内容,保存jmeter.save.saveservice.output_f…...

基于SSM的经典电影推荐网站设计与实现
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…...
JavaScript中使用JSON的基本操作示例
简介 JSON(JavaScript Object Notation)是一种数据交换格式,也是JavaScript中处理数据的常见方式之一。JSON是一种轻量级的数据交换格式,易于阅读和编写,同时也易于解析和生成。在JavaScript中,可以使用内…...
上拉、下拉电阻的作用
上拉、下拉电阻的作用 (1)一般作为单键触发使用,如果芯片本身没有内接电阻,为了使单键维持在不被触发的状态或触发后回到原状态,必须在芯片外部接一个电阻,即保持芯片引脚高电平(或低电平)输入,这样单击按键…...

docker部署elasticsearch+kibana+head
前言 最近,项目需要使用elasticsearch,所以就想快速安装一个使用,最开始是docker安装了7.10.1版本。 后面计划使用Java开发,发现有 RestHighLevelClient 和 Elasticsearch Java API Client两种客户端连接方式。 然后网上查阅了一…...

Linux:vim的简单使用
个人主页 : 个人主页 个人专栏 : 《数据结构》 《C语言》《C》《Linux》 文章目录 前言一、vim的基本概念二、vim的基本操作三、vim正常模式命令集四、vim底行模式命令集五、.xxx.swp的解决总结 前言 本文是对Linux中vim使用的总结 一、vim的基本概念 …...
Python---文件和文件夹操作
os模块 在Python中文件和文件夹的操作要借助os模块里面的相关功能,具体步骤如下: 第一步:导入os模块 import os 第二步:调用os模块中的相关方法 os.函数名() 与文件操作相关方法 编号函数功能1os.rename(目标文件名称&…...
electron-vue运用及案例代码
前言 Electron是一个使用JavaScript, HTML和CSS构建跨平台桌面应用程序的开源库。它允许开发者使用纯web技术创建原生应用程序,这使得web开发者能够利用他们已经掌握的web技术来构建桌面应用。 以下是一个简单的Electron应用程序的代码示例: // 引入Electron的主模块 co…...

视频字幕处理+AI绘画,Runway 全功能超详细使用教程(4)
runway的视频字幕处理、AI绘图功能介绍,感觉完全就是为了做电影而布局,一整套功能都上线了!想系统学习的必收藏! 在深度研究Runway各个功能后,无论是AI视频生成及后期处理技术,还是AI图像生成技术ÿ…...
令人疑惑的Promise相关问题
令人疑惑的Promise相关问题 问题1 const promise new Promise((resolve, reject) > {reject(Error(Error occurred)); });promise.catch(error > console.log(error.message)); promise.catch(error > console.log(error.message));输出: Error occurr…...

Spring 多数据源搭建
目录 前言 正文 1.Druid 介绍和使用 2.其他多数据源解决方案 总结 前言 对于复杂的业务和项目,可能在一个单体项目中存在需要连接多个数据库的情况。这时,就会使用到多数据源,实际中遇到的可能性比较大。 正文 如果一个项目中需要连…...

【二分查找】LeetCode1970:你能穿过矩阵的最后一天
本文涉及的基础知识点 二分查找算法合集 作者推荐 动态规划LeetCode2552:优化了6版的1324模式 题目 给你一个下标从 1 开始的二进制矩阵,其中 0 表示陆地,1 表示水域。同时给你 row 和 col 分别表示矩阵中行和列的数目。 一开始在第 0 …...

利用python连接MySQL数据库并执行相关sql操作
一、新建MySQL数据库 1.启动MySQL服务 打开phpstudy,开启MySQL服务。如果开启失败的话,可以打开任务管理器,把正在运行的mysqld服务的进程进行关闭,再次打开MySQL服务即可启动。 2.新建MySQL数据库 选择数据库,点击…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...

链式法则中 复合函数的推导路径 多变量“信息传递路径”
非常好,我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题,统一使用 二重复合函数: z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y)) 来全面说明。我们会展示其全微分形式(偏导…...