FPGA图像处理之中值滤波

news2024/10/21 19:46:15

文章目录

  • 一、什么是中值滤波?
  • 二、均值滤波和中值滤波对比
  • 三、FPGA实现
    • 3.1 Verilog代码
    • 3.2 仿真验证


一、什么是中值滤波?

  在前一篇《FPGA图像处理之均值滤波》中,我们了解到了图像处理中常遇到的一些噪声类型以及均值滤波的原理以及实现。我们知道均值滤波实际上没有去掉噪声,而是把噪声平均到了每一个像素点上。这样就会导致图像变得很模糊,而且如果噪声是椒盐噪声的话,均值滤波后放大看图像会有一些灰度值不均匀的小块。
  而中值滤波就能很好的消除噪声,特别是椒盐噪声。中值滤波算法就是取滤波窗口的中间值来替换掉原来的值,因为噪声通常是亮度异常的点,过亮或者过暗。因此在一个滤波窗口内灰度值排序的话,噪点就要么靠前要么靠后,此时我们使用中间值来替换掉滤波窗口中心像素值的话,噪点就不存在了,理论上能消除噪声。

二、均值滤波和中值滤波对比

  以下是中值滤波和均值滤波对椒盐噪声降噪的对比,matlab代码如下:

% 读取原始图像
originalImage = imread('yuanyang.bmp'); % 替换为你的图像文件名

% 将图像转换为灰度图像
grayImage = rgb2gray(originalImage);

% 添加椒盐噪声(2% 椒盐噪声)
noisyImage = imnoise(grayImage, 'salt & pepper', 0.02);

% 应用均值滤波
meanFilteredImage = imfilter(noisyImage, fspecial('average', [3 3]), 'replicate');

% 应用中值滤波
medianFilteredImage = medfilt2(noisyImage, [3 3]); % 使用 3x3 的窗口

% 显示图像
figure;

subplot(1, 3, 1);
imshow(noisyImage);
title('添加噪声后的图像');

subplot(1, 3, 2);
imshow(meanFilteredImage);
title('均值滤波后的图像');

subplot(1, 3, 3);
imshow(medianFilteredImage);
title('中值滤波后的图像');

在这里插入图片描述
  我们可以很明显看到,中值滤波对椒盐噪声的降噪更好,我们把中值滤波和均值滤波放大来看一下
在这里插入图片描述

三、FPGA实现

  对于中值滤波最重要的是找出滤波窗口里几个数的中间值。最经典的算法就是冒泡排序法,其基本思想是:在待排序的一组数中,将相邻的两个数进行比较,若前面的数比后面的数大就交换两数,否则不交换;如此下去,直至最终完成排序。就像气泡在水里往上冒一样,但是冒泡排序法的时间复杂度为 O ( N 2 ) O(N^2) O(N2) ,虽然我们只需要排序一半即可,但是也需要很长的时间周期。例如一个3*3的滤波窗口,我们对需要在9个灰度值取出中间值,我们需要比较 8 + 7 + 6 + 5 = 26个周期。
  因此我们可以充分利用FPGA并行的优势以面积换时间:

  1. 在得到一个3*3矩阵后,一个时钟周期可以比较出每一行的最大值,最小值以及中间值,总共得到3行的最大,最小以及中间值。
  2. 在第二个时钟周期我们可以比较出每一行的最大值之间的最大值,最小值之间的最小值,和中间值之间的中间值,一共得到了三个数,这三行数据中的 最大值,中间值,和最小值。
  3. 第三个时钟周期我们找出中间值然后替换掉滤波窗口的中心像素即可。

  我们利用FPGA的优势可以将中值滤波的算法从26个时钟周期变成3个时钟周期。

3.1 Verilog代码

  前面的图像扩展以及矩阵生成还是用均值滤波里面的,主要是后面的找中值。我们先定义9个寄存器,来存放每一行的最大值,最小值以及中间值。

reg             [7:0]                               row1_min_data   ;  //第一行3个数最小的值
reg             [7:0]                               row1_mid_data   ;  //第一行3个数中间的值
reg             [7:0]                               row1_max_data   ;  //第一行3个数最大的值
reg             [7:0]                               row2_min_data   ;  //第二行3个数最小的值
reg             [7:0]                               row2_mid_data   ;  //第二行3个数中间的值
reg             [7:0]                               row2_max_data   ;  //第二行3个数最大的值
reg             [7:0]                               row3_min_data   ;  //第三行3个数最小的值
reg             [7:0]                               row3_mid_data   ;  //第三行3个数中间的值
reg             [7:0]                               row3_max_data   ;  //第三行3个数最大的值

//延迟一拍计算3*3矩阵中,每一行的中间值和最大值和最小值
always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        row1_min_data  <= 'd0;
    else if((w_matrix_11 <= w_matrix_12)&&(w_matrix_11 <= w_matrix_13))
        row1_min_data  <= w_matrix_11;
    else if((w_matrix_12 <= w_matrix_11)&&(w_matrix_12 <= w_matrix_13))
        row1_min_data  <= w_matrix_12;
    else
        row1_min_data <= w_matrix_13;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        row1_mid_data  <= 'd0;
    else if(((w_matrix_11 <= w_matrix_12)&&(w_matrix_11 >= w_matrix_13))||((w_matrix_11 >= w_matrix_12)&&(w_matrix_11 <= w_matrix_13)))
        row1_mid_data  <= w_matrix_11;
    else if(((w_matrix_12 <= w_matrix_11)&&(w_matrix_12 >= w_matrix_13))||((w_matrix_12 >= w_matrix_11)&&(w_matrix_12 <= w_matrix_13)))
        row1_mid_data  <= w_matrix_12;
    else
        row1_mid_data <= w_matrix_13;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        row1_max_data  <= 'd0;
    else if((w_matrix_11 >= w_matrix_12)&&(w_matrix_11 >= w_matrix_13))
        row1_max_data  <= w_matrix_11;
    else if((w_matrix_12 >= w_matrix_11)&&(w_matrix_12 >= w_matrix_13))
        row1_max_data  <= w_matrix_12;
    else
        row1_max_data <= w_matrix_13;
end

//在这一拍内,同时计算第二行的中间值和最大值和最小值
always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        row2_min_data  <= 'd0;
    else if((w_matrix_21 <= w_matrix_22)&&(w_matrix_21 <= w_matrix_23))
        row2_min_data  <= w_matrix_21;
    else if((w_matrix_22 <= w_matrix_21)&&(w_matrix_22 <= w_matrix_23))
        row2_min_data  <= w_matrix_22;
    else
        row2_min_data <= w_matrix_23;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        row2_mid_data  <= 'd0;
    else if(((w_matrix_21 <= w_matrix_22)&&(w_matrix_21 >= w_matrix_23))||((w_matrix_21 >= w_matrix_22)&&(w_matrix_21 <= w_matrix_23)))
        row2_mid_data  <= w_matrix_21;
    else if(((w_matrix_22 <= w_matrix_21)&&(w_matrix_22 >= w_matrix_23))||((w_matrix_22 >= w_matrix_21)&&(w_matrix_22 <= w_matrix_23)))
        row2_mid_data  <= w_matrix_22;
    else
        row2_mid_data <= w_matrix_23;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        row2_max_data  <= 'd0;
    else if((w_matrix_21 >= w_matrix_22)&&(w_matrix_21 >= w_matrix_23))
        row2_max_data  <= w_matrix_21;
    else if((w_matrix_22 >= w_matrix_21)&&(w_matrix_22 >= w_matrix_23))
        row2_max_data  <= w_matrix_22;
    else
        row2_max_data <= w_matrix_23;
end
//在这一拍内,同时计算第三行的中间值和最大值和最小值
always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        row3_min_data  <= 'd0;
    else if((w_matrix_31 <= w_matrix_32)&&(w_matrix_31 <= w_matrix_33))
        row3_min_data  <= w_matrix_31;
    else if((w_matrix_32 <= w_matrix_31)&&(w_matrix_32 <= w_matrix_33))
        row3_min_data  <= w_matrix_32;
    else
        row3_min_data <= w_matrix_33;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        row3_mid_data  <= 'd0;
    else if((w_matrix_valid == 1'b1)&&(((w_matrix_31 <= w_matrix_32)&&(w_matrix_31 >= w_matrix_33)))||((w_matrix_31 >= w_matrix_32)&&(w_matrix_31 <= w_matrix_33)))
        row3_mid_data  <= w_matrix_31;
    else if((w_matrix_valid == 1'b1)&&(((w_matrix_32 <= w_matrix_31)&&(w_matrix_32 >= w_matrix_33)))||((w_matrix_32 >= w_matrix_31)&&(w_matrix_32 <= w_matrix_33)))
        row3_mid_data  <= w_matrix_32;
    else
        row3_mid_data <= w_matrix_33;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        row3_max_data  <= 'd0;
    else if((w_matrix_valid == 1'b1)&&(w_matrix_31 >= w_matrix_32)&&(w_matrix_31 >= w_matrix_33))
        row3_max_data  <= w_matrix_31;
    else if((w_matrix_valid == 1'b1)&&(w_matrix_32 >= w_matrix_31)&&(w_matrix_32 >= w_matrix_33))
        row3_max_data  <= w_matrix_32;
    else
        row3_max_data <= w_matrix_33;
end

   我们找到每一行的最大值,最小值,中间值后,再找出 其中的最大值,中间值,以及最小值:

reg             [7:0]                               mix_min_data   ;  //三行最小的值
reg             [7:0]                               mix_mid_data   ;  //三行中间的值
reg             [7:0]                               mix_max_data   ;  //三行最大的值

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        mix_min_data  <= 'd0;
    else if((row1_min_data <= row2_min_data)&&(row1_min_data <= row3_min_data))
        mix_min_data  <= row1_min_data;
    else if((row2_min_data <= row1_min_data)&&(row2_min_data <= row3_min_data))
        mix_min_data  <= row2_min_data;
    else
        mix_min_data <= row3_min_data;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        mix_mid_data  <= 'd0;
    else if(((row1_mid_data <= row2_mid_data)&&(row1_mid_data >= row3_mid_data))||((row1_mid_data >= row2_mid_data)&&(row1_mid_data <= row3_mid_data)))
        mix_mid_data  <= row1_mid_data;
    else if(((row2_mid_data <= row1_mid_data)&&(row2_mid_data >= row3_mid_data))||((row2_mid_data >= row1_mid_data)&&(row2_mid_data <= row3_mid_data)))
        mix_mid_data  <= row2_mid_data;
    else
        mix_mid_data <= row3_mid_data;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        mix_max_data  <= 'd0;
    else if((row1_max_data >= row2_max_data)&&(row1_max_data >= row3_max_data))
        mix_max_data  <= row1_max_data;
    else if((row2_max_data >= row1_max_data)&&(row2_max_data >= row3_max_data))
        mix_max_data  <= row2_max_data;
    else
        mix_max_data <= row3_max_data;
end

   最后再把中间值复制给输出像素点:

reg             [7:0]                               matrix_mid_data ;  //一个3*3矩阵中中间的值

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        matrix_mid_data  <= 'd0;
    else if(((mix_min_data <= mix_mid_data)&&(mix_min_data >= mix_max_data))||((mix_min_data >= mix_mid_data)&&(mix_min_data <= mix_max_data)))
        matrix_mid_data  <= mix_min_data;
    else if(((mix_mid_data <= mix_min_data)&&(mix_mid_data >= mix_max_data))||((mix_mid_data >= mix_min_data)&&(mix_mid_data <= mix_max_data)))
        matrix_mid_data  <= mix_mid_data;
    else
        matrix_mid_data <= mix_max_data;
end

3.2 仿真验证

   仿真输入图像依然用均值滤波那幅加了椒盐噪声的图片,然后仿真生成滤波后的图像,再做对比。
在这里插入图片描述
在这里插入图片描述
   仿真读取文件成功,然后直接运行

在这里插入图片描述
   可以看出,椒盐噪声很好的被去除了,我们放大一下细节,对比一下和均值滤波的区别。

在这里插入图片描述
   左边为中值滤波,右边为均值滤波,可以看出效果对比还是很明显。

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

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

相关文章

【Linux】实现倒计时、进度条、gdb

文章目录 缓冲区1.概念2.作用3.刷新策略4.缓冲区位置 实现倒计时实现进度条Linux调试器----gdb 缓冲区 1.概念 缓冲区是计算机内存的一部分&#xff0c;用于暂时存储数据。它在数据传输过程中起到一个缓冲桥梁的作用&#xff0c;帮助协调数据传输的速度差异。缓冲区可以是磁盘…

魔百和 HG680-MC免拆 2+8免拆机卡刷固件

HG680MC免拆完美bin卡刷24年10月21日版本【修复语音、ADB端口9999、开启ADB的Root权限】 更新特点&#xff1a; 1.更新开机桌面为SMART动画&#xff1b; 2.安装app自动安装&#xff0c;无需点击确认、下一步等提示按钮内容; 3.ADB端口号为9999&#xff1b; 4.ADB权限为完美RO…

wireshark 解密浏览器https数据包

一、导出浏览器证书有两种方法 1、在浏览器快捷方式追加启动参数&#xff1a; --ssl-key-log-file"d:\log\2.log" C:\Users\Administrator\AppData\Local\Google\Chrome\Application\chrome.exe --ssl-key-log-file"d:\log\2.log" 2、环境变量中新建用…

SpringbootSpringcloud面试题(含代码及展示图)

1 简单说一下springboot? Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化Spring应用的初始搭建以及开发过程。 -使用springboot以后,搭建一个spring应用和开发&#xff0c;部署变得很简单. 2 怎么简单实现一个springboot应用 1&#xff09;新建ma…

SpringBoot获取bean的几种方式

目录 一、BeanFactory与ApplicationContext的区别 二、通过BeanFactory获取 三、通过BeanFactoryAware获取 四、启动获取ApplicationContext 五、通过继承ApplicationObjectSupport 六、通过继承WebApplicationObjectSupport 七、通过WebApplicationContextUtils 八、通…

Chrome DevTools 三: Performance 性能面板扩展—— 性能优化

Performance 性能 &#xff08;一&#xff09;性能指标 首次内容绘制 (First Contentful Paint&#xff0c;FCP)&#xff1a; 任意内容在页面上完成渲染的时间 最大内容绘制 (Largest Contentful Paint&#xff0c;LCP)&#xff1a; 最大内容在页面上完成渲染的时间 第一字节…

创客项目秀|基于XIAO ESP32C3的本地个人助理Mr.M

作者&#xff1a;Matthew Yu 来自&#xff1a;Fab academy 在数字化时代的浪潮中&#xff0c;柴火创客空间作为创新与实践的摇篮&#xff0c;不仅为Fab Academy 2024的学员们提供了一个充满活力的学习和创作环境&#xff0c;更是将科技的力量与人文关怀深度融合。今天&#x…

Redis——事务

文章目录 Redis 事务Redis 的事务和 MySQL 事务的区别:事务操作MULTIEXECDISCARDWATCHUNWATCHwatch的实现原理 总结 Redis 事务 什么是事务 Redis 的事务和 MySQL 的事务 概念上是类似的. 都是把⼀系列操作绑定成⼀组. 让这⼀组能够批量执行 Redis 的事务和 MySQL 事务的区别:…

【Next.js 项目实战系列】07-分配 Issue 给用户

原文链接 CSDN 的排版/样式可能有问题&#xff0c;去我的博客查看原文系列吧&#xff0c;觉得有用的话&#xff0c;给我的库点个star&#xff0c;关注一下吧 上一篇【Next.js 项目实战系列】06-身份验证 分配 Issue 给用户 本节代码链接 Select Button​ # /app/issues/[i…

【Java】正则表达式详解

目录 引言 一、基本概念 1.1 元字符 1.2 预定义字符类 1.3 边界匹配符 1.4 数量标识符 1.5 捕获与非捕获分组 二、Java中的正则表达式支持 三、正则表达式的使用示例 3.1 匹配字符串 3.2 替换字符串 3.3 分割字符串 3.4 使用Pattern和Matcher 3.5 捕获组和后向…

【电商项目】1分布式基础篇

1 项目简介 1.2 项目架构图 1.2.1 项目微服务架构图 1.2.2 微服务划分图 2 分布式基础概念 3 Linux系统环境搭建 查看网络IP和网关 linux网络环境配置 补充P123&#xff08;修改linux网络设置&开启root密码访问&#xff09; 设置主机名和hosts映射 主机名解析过程分析&…

金九银十互联网大厂Java高频面试题(2024最新含答案)

2024 年的互联网行业竞争越来越严峻&#xff0c;面试也是越来越难&#xff0c;一直以来我都想整理一套完美的面试宝典&#xff0c;奈何难抽出时间&#xff0c;这套 1200道的 Java 面试手册我整理了整整 1 个月&#xff0c;上传到 Git 上目前 star 数达到了 30K 这套互联网 Jav…

冲击美团!已成功 OC

这是一位训练营学员的美团面经&#xff0c;目前已经 OC 。 在此之前他已经拿到了不少公司的offer&#xff0c;但是都达不到他的预期&#xff0c;美团给的待遇就非常不错&#xff0c;大厂不愧是大厂&#xff0c;就是不知道工作强度如何。 他经历了一共三场面试&#xff0c;一面…

实现省略号查看详情样式

1.期望实现效果 2.目前实现效果 3.实现代码 1.wxml<view class"desc-text"><view class"show-more">查看详情 >></view><!-- <rich-text nodes"{{富文本接口数据内容 }}"></rich-text> --><text&…

python反爬

1.无限debug无法f12 关闭掉 Deactivate Breakpoints

【闲谈程序设计例三则:抛弃传统单步进初级阶段,用推导归纳出来的规律写代码,进入进阶阶段,人类自性的高级活动。】2024-10-21

闲谈程序设计三则&#xff1a;抛弃传统单步进&#xff0c;用推导归纳出来的规律写代码。 本论坛常见新学提问都是一些入门级别的问题&#xff0c;近来AI活跃抢答&#xff0c;然而&#xff0c;对于有些问题AI可以说是答非所问&#xff0c;令人哭笑不得&#xff0c;而AI能回答的…

MacOS安装BurpSuite

文章目录 一、下载地址二、下载注册机三、安装教程四、启动burpsuit五、免责声明 一、下载地址 https://portswigger-cdn.net/burp/releases/download?productpro&version2024.7.1&typeMacOsx二、下载注册机 https://github.com/NepoloHebo/BurpSuite-BurpLoaderKey…

B站协议登录到实现各种功能完整代码(专栏总结)

B站协议登录、点赞、收藏、转发实现及代码 关注、动态转发实现动态抽奖实现及代码 直播预约抽奖实现及代码 本文为本专栏的总结文章 一、扫码登录 请求获取二维码包&#xff0c;得到二维码链接和qrcode_key参数之后&#xff0c;利用qrcode_key循环GET请求登录状态包即可&#x…

【word】页眉横线无法取消

小伙伴们日常想在页眉里加横线&#xff0c;直接双击页眉&#xff0c;然后在页眉横线里选择自己喜欢的横线样式就可以了。 但今天我遇到的这个比较奇特&#xff0c;有些页有这个横线&#xff0c;有些页没有&#xff0c;就很奇怪。 最后排查完&#xff0c;发现是只有标题2的页…

WPF入门_02依赖属性

1、依赖属性主要有以下三个优点 1)依赖属性加入了属性变化通知、限制、验证等功能。这样可以使我们更方便地实现应用,同时大大减少了代码量 2)节约内存:在WinForm中,每个UI控件的属性都赋予了初始值,这样每个相同的控件在内存中都会保存一份初始值。而WPF依赖属性很好地…