使用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. 设定噪声值: 对于每个选定的噪声点,将其灰度值设定为最大值(白色)或最小值(黑色)。

这样就在图像中引入了椒盐噪声,这种噪声形式使图像中的某些区域变得非常亮或非常暗,从而增加了图像的复杂性和难度。

椒盐噪声主要用于模拟一些特殊环境下的图像问题,例如图像采集中的传感器错误、传输中的丢包等情况。在图像处理中,去除或减轻椒盐噪声的方法通常包括滤波技术,例如中值滤波。

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 均值滤波的原理

均值滤波是一种常见的图像平滑处理方法,其原理基于对图像的像素值进行平均运算。这种滤波方法主要用于去除图像中的噪声或细小的细节,以产生更平滑的图像。

具体步骤如下:

  1. 定义卷积核: 选择一个固定大小的卷积核,通常是一个正方形的矩阵。卷积核的大小取决于应用场景和对平滑度的需求。

  2. 卷积操作: 将卷积核应用于图像的每个像素。对于每个像素,将其与卷积核中对应位置的像素相乘,然后将所有乘积的和除以卷积核的总权重。这个和的结果就是卷积核中心像素的新值。

  3. 更新图像: 将卷积操作得到的新值赋予原始图像相应位置的像素,形成平滑后的图像。

均值滤波的效果在平滑图像的同时,会导致图像失真,特别是对边缘和细节的处理较为粗糙。这是因为平均操作会模糊图像,擦除图像中的高频信息。

公式表示如下,其中 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=1Mj=1Mh(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 中值滤波的原理

中值滤波是一种非线性滤波方法,它的原理是用像素点邻域灰度值的中值来代替该像素点的灰度值。中值滤波不会改变图像的灰度平均值,但可以有效地去除图像中的椒盐噪声等离群点,对于保持图像边缘细节方面也有一定的优势。

中值滤波的步骤如下:

  1. 选择滤波模板大小: 中值滤波通常采用3×3、5×5等奇数大小的模板。选择模板的大小会影响滤波效果。

  2. 将模板覆盖在图像的每个像素点上: 以当前像素为中心,取模板中所有像素的灰度值。

  3. 对模板中的灰度值进行排序: 将模板中的灰度值按升序或降序排列。

  4. 取排序后的中值: 选取排序后的中间值作为当前像素的新灰度值。

  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中值滤波后的效果图

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

利用python连接MySQL数据库并执行相关sql操作

一、新建MySQL数据库 1.启动MySQL服务 打开phpstudy&#xff0c;开启MySQL服务。如果开启失败的话&#xff0c;可以打开任务管理器&#xff0c;把正在运行的mysqld服务的进程进行关闭&#xff0c;再次打开MySQL服务即可启动。 2.新建MySQL数据库 选择数据库&#xff0c;点击…

ORACLE同义词说明及使用

同义词概念 Oracle的同义词&#xff08;synonyms&#xff09;从字面上理解就是别名的意思&#xff0c;和视图的功能类似&#xff0c;就是一种映射关系。它可以节省大量的数据库空间&#xff0c;对不同用户的操作同一张表没有多少差别;它扩展了数据库的使用范围&#xff0c;能够…

基于SSM的经典电影推荐网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

Python 流程控制

目录 程序流程 顺序结构 分支结构 单分支 双分支 多分支 if 嵌套 循环结构 while循环 for 循环 退出循环 循环与分支嵌套 附录 程序流程 程序是由语句构成&#xff0c;而流程控制语句 是用来控制程序中每条语句执行顺序的语句。可以通过控制语句实现更丰富的逻辑…

中国版的 GPTs:InsCode AI 生成应用

前言 在上一篇文章 《InsCode&#xff1a;这可能是下一代应用开发平台&#xff1f;》中&#xff0c;我们介绍了一个新的应用开发平台 InsCode&#xff0c;它是基于云原生开发环境 云 IDE AI 辅助编程的一站式在线开发平台。 最近&#xff0c;InsCode 又推出了另一种全新的开…

ASP.NET Core 使用IIS调试出现505.24错误

最近一直再学习asp.net 相关的东西&#xff0c;主要是为前端app提供一个webapi接口。在使用iis调试程序时出现HTTP Error 500.24 - Internal Server Error错误&#xff0c;搞了好久才最终解决。 1.在项目中增加web.config配置文件 2.将配置文件改为如下内容 <?xml version…

Unity | 渡鸦避难所-0 | 创建 URP 项目并导入商店资源

0 前言 知识点零零碎碎&#xff0c;没有目标&#xff0c;所以&#xff0c;一起做游戏吧 各位老师如果有什么指点、批评、漫骂、想法、建议、疑惑等&#xff0c;欢迎留言&#xff0c;一起学习 1 创建 3D&#xff08;URP&#xff09;项目 在 Unity Hub 中点击新项目&#xff…

Hive jar包冲突问题排查解决

1、报错情况 hiveserver2启动失败&#xff0c;查看日志报错&#xff1a; 2022-07-04T20:14:53,315 WARN [main]: server.HiveServer2 (HiveServer2.java:startHiveServer2(1100)) - Error starting HiveServer2 on attempt 1, will retry in 60000ms java.lang.NoSuchMethod…

Vue3中的组合式API的详细教程和介绍

文章目录 前言介绍组合式 API 基础setup 组件选项 带 ref 的响应式变量生命周期钩子注册内部 setupwatch 响应式更改独立的 computed 属性后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;vue.js &#x1f431;‍&#x1f453;博主在前端…

基于YOLOv8深度学习的120种犬类检测与识别系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战、狗类检测、犬种识别

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

SpringMVC利用@ControllerAdvice和ResponseBodyAdvice接口统一处理返回值

在我们进行Java的Web应用开发时&#xff0c;如何写更少的代码&#xff0c;做更多的事情。如何让开发更容易上手&#xff0c;更专注于业务层面&#xff0c;不需要太关心底层的实现。这里就分享一些我平时在搭建基础框架时候的一些心得体验。 统一处理返回值 在web应用中&#x…

Python爬虫基础之Scrapy框架详解

目录 1. 简介2. Scrapy的安装3. Scrapy的架构4. Scrapy的数据流程5. Scrapy开发流程5.1 创建项目5.2 创建Spider5.3 创建Item5.4 编写Spider5.5 运行Spider 参考文献 原文地址&#xff1a;https://program-park.top/2023/12/01/reptile_5/ 本文章中所有内容仅供学习交流使用&am…

8、内部FLASH模拟EEPROM实验(STM32F407)

STM32编程方式 在线编程&#xff08;ICP,In-Circuit Programming&#xff09;: 通过JTAG/SWD协议或者系统加载程序(Bootloader)下载用户应用程序到微控制器中。 在程序中编程(IAP,In Application Programming):通过任何一种通信接口(如IO端口,USB,CAN,UART,I2C,SPI等)下载程…

2024 年综合网络安全审计清单

在网络威胁不断演变的时代&#xff0c;确保组织数据和系统的安全至关重要。 全面的网络安全审核清单可以在实现这一目标方面发挥关键作用。但是&#xff0c;什么才是有效的网络安全审核清单呢&#xff1f;以及如何对其进行定制以满足您组织的独特需求&#xff1f; 了解网络安…

【设计模式-2.3】创建型——原型模式

说明&#xff1a;本文介绍设计模式中&#xff0c;创建型中的原型模式&#xff1b; 飞机大战 创建型设计模式关注于对象的创建&#xff0c;原型模式也不例外。如简单工厂和工厂模式中提到过的飞机大战这个例子&#xff0c;游戏中飞机、坦克对象会创建许许多多的实例&#xff0…

水利安全监测方案——基于RTU200的解决方案

引言&#xff1a; 水资源是人类赖以生存的重要基础&#xff0c;对于保障水利系统安全运行以及应对自然灾害起着关键作用。为了实现水利安全监测的目标&#xff0c;我们提出了基于RTU200的解决方案。本方案将结合RTU200的可靠性、灵活性和高效性&#xff0c;为您打造一个全面的…

vue使用实现录音功能js-audio-recorder

前言 最近项目中需要实现一个录音上传功能&#xff0c;用于语音评论可以上录音。 下载插件&#xff1a; npm i js-audio-recorder完整代码 <template><div style"padding: 20px;"><h3>录音上传</h3><div style"font-size:14px"…

[Java学习日记]网络编程

目录 一.常见的软件架构、网络编程三要素、IP 二.利用UDP发送与接收数据 三.改聊天室 四.组播案例 五.TCP通信案例 一.常见的软件架构、网络编程三要素、IP 网络编程&#xff1a;在网络通信协议下&#xff0c;不同的计算机上运行的程序进行的数据传输 在Java中可以使用java…

Promise的resolve和reject方法(手写题)

1.resolve 2.reject 3.手写 1.resolve //构造函数上添加 resolve 方法 Promise.resolve function (value) {return new Promise((resolve, reject) > {if (value instanceof Promise) {value.then((val) > {resolve(val)},(err) > {reject(err)})} else {resolve(v…

springboot项目中注入bean后,调用时报n

需求&#xff1a; 在socket接收到上报数据后&#xff0c;在handler中调用工具类中ProtocolAnalyse的conAnalyse(byte[] data, int dataLen)解析数据。解析数据后&#xff0c;将解析后的结果保存至数据库。注入了三个bean&#xff1a; Autowiredprivate PersonTeService person…