FPGA图像处理之均值滤波
文章目录
- 一、什么是图像滤波?
- 1.1 噪声类型
- 1.2 滤波类型
- 二、均值滤波原理
- 2.1 3*3窗口滑动过程
- 2.2 图像扩展
- 三、Matlab实现均值滤波
- 四、FPGA实现均值滤波
- 4.1 生成 3*3 矩阵
- 4.2 仿真3*3矩阵
- 4.3 计算均值
- 4.4 仿真均值滤波
一、什么是图像滤波?
图像滤波是一种图像处理技术,旨在通过对图像进行修改来改善图像质量或提取特定特征。滤波通常用于去除噪声、平滑图像、增强边缘或提取特征等。
1.1 噪声类型
在图像数据采集、处理、传输等过程中图像数据会受到噪声损坏;因为噪声无处不在,严重的会直接影响用户视觉感受,因此在图像处理中降噪是非常关键的步骤,对于不同类型的噪声使用不同的滤波器会有更好的效果。以下是被不同类型噪声污染的图片,matlab代码:
% 读取原始图像
originalImage = imread('yuanyang.bmp'); % 替换为你的图像文件名% 将图像转换为灰度图像(如果是彩色图像)
if size(originalImage, 3) == 3originalImage = rgb2gray(originalImage);
end% 添加高斯噪声
gaussianNoiseImage = imnoise(originalImage, 'gaussian', 0, 0.01); % 均值0,方差0.01% 添加椒盐噪声
saltPepperNoiseImage = imnoise(originalImage, 'salt & pepper', 0.02); % 2% 椒盐噪声% 添加泊松噪声
poissonNoiseImage = imnoise(originalImage, 'poisson');% 添加斑点噪声
speckleNoiseImage = imnoise(originalImage, 'speckle', 0.04); % 方差0.04% 显示原始图像和不同类型的噪声图像
figure;subplot(2, 3, 1);
imshow(originalImage);
title('原始图像');subplot(2, 3, 2);
imshow(gaussianNoiseImage);
title('高斯噪声');subplot(2, 3, 3);
imshow(saltPepperNoiseImage);
title('椒盐噪声');subplot(2, 3, 4);
imshow(poissonNoiseImage);
title('泊松噪声');subplot(2, 3, 5);
imshow(speckleNoiseImage);
title('斑点噪声');
- 高斯噪声:服从高斯分布的随机噪声,通常表现为图像的亮度随机波动;通常由传感器噪声或其他随机因素引起。
- 椒盐噪声:图像中随机出现的黑色和白色像素,类似于撒盐和胡椒;通常由图像传输错误或传感器有坏点引起的。
- 泊松噪声:与光子计数相关的噪声,通常在低光照条件下出现;常见于医学成像和天文成像,在低亮度区域,噪声会显得更加明显。
- 斑点噪声:通常在医学影像中出现,表现为图像中的随机斑点。
1.2 滤波类型
图像滤波通常涉及对图像中每个像素的值进行修改,通常是通过考虑该像素及其邻域像素的值。滤波器通常使用一个称为“卷积核”或“滤波器”的小矩阵来实现这一点。卷积核在图像上滑动,并对覆盖的像素值进行加权平均或其他运算,常见的图像滤波器有:
- 均值滤波器
- 高斯滤波器
- 中值滤波器
- 双边滤波器
- 拉普拉斯滤波器
- Sobel滤波器
- Canny滤波器
不同滤波器对应的应用场景也不同,有的是做降噪,有的是提取图像特征,本篇文章先来介绍均值滤波器的实现。
二、均值滤波原理
我们知道图像滤波就是通过判断当前像素周边的像素来修改,可以想象成一个矩形框。所以滤波关键的因素就是矩形框的大小以及矩形框里的像素值所占的权重。均值滤波的原理:就是用矩形框里面所有的像素值的平均值来替换矩形框中心的像素,如下图所示,表示一幅图像数据:
上面是一幅10*6 大小的图像数据,假如这幅图像数据被椒盐噪声污染,我们污染后的像素点标志为红色,如下图所示:
这些噪点在视觉上表现为亮度特别突出的点,比周围像素点的亮度高很多,因此才会影响到视觉效果。均值滤波的计算窗口有 33 、55、77等等,窗口越大计算量越大,效果也越好,这里用33的窗口来演示均值滤波。
2.1 3*3窗口滑动过程
第一个3*3窗口从上到下,从左到右依次滑动,每次滑动窗口后计算并且修改中心值的像素值。如上图所示第一次窗口滑动计算了这9个像素的均值然后替换了中心点的像素值,这样第一个噪点就被平均了,不会那么突出了。这也是均值滤波的局限性,噪声并没有被消除而是被平均了;如果窗口很大,那么图片也会变得有点模糊,也会平滑图像的边缘特征。
第二个窗口滑动后,计算替代了(1,2)的像素点,其它的以此类推。
上图是窗口滑动到最后一行后,我们可以看到在经过33窗口滑动计算后,新的图像会少两行两列;如果是55的窗口,滑动后会少4行4列,因此在实际处理过程中需要对图像进行扩展来确保计算后的图像大小不变。
2.2 图像扩展
通常图像扩展会通过补0或者补1,或者使用原值来填充,这里我们使用原值填充,就是判断当前计算的点为边缘时,不用计算直接用原值填充就行,我们需要在原始图像进行扩展2行2列,如下图所示:
如上所示,我们用x值来填充原始图像,假如原始图像大小为NM,那么填充后的图像大小为(N+2) * (M+2),我们再用新的图像大小做33窗口的滑动,如下所示:
第一次窗口滑动后计算了新图像左上角的像素。
第二次窗口滑动计算了新图像的第一行左边第二个的像素,其它的依次类推。
在滑动完最后一个窗口后,图像也就计算完成。原始图像大小为 NM 经过图像扩展变成 (N+2) * (M+2),再经过33窗口计算变成了 N*M,这样新的图像大小就和原始图像大小一致了。
三、Matlab实现均值滤波
在Matlab里可以直接调用均值滤波的函数,再滤波之前需要将图像数据转成灰度图像,因为噪声污染的大多是是异常的亮度,跟颜色无关,在均值的时候也是平均亮度,matlab代码如下:
clc;
close all;
% 读取原始图像
originalImage = imread('yuanyang.bmp'); % 将图像转换为灰度图像(如果是彩色图像)
if size(originalImage, 3) == 3originalImage = rgb2gray(originalImage);
end% 添加噪声
noisyImage = imnoise(originalImage, 'salt & pepper', 0.02); % 添加椒盐噪声% 均值滤波
filterSize = 3; % 滤波器大小
meanFilteredImage = imfilter(noisyImage, fspecial('average', filterSize));% 显示原始图像、带噪声的图像和均值滤波后的图像
figure;subplot(1, 3, 1);
imshow(originalImage);
title('原始图像');subplot(1, 3, 2);
imshow(noisyImage);
title('带噪声的图像');subplot(1, 3, 3);
imshow(meanFilteredImage);
title('均值滤波后的图像');
我们放大滤波后的图像来看。
可以看到噪点其实都没有被消除,只是被平均了而已,这也是均值滤波的局限性。我们把窗口设成5*5,再放大看一下。
可以看到窗口变大后,降噪效果也变好了,但是图像变得模糊了。
四、FPGA实现均值滤波
实现均值滤波的关键就是开窗和扩展,关于生成3*3的矩阵,我们可以参考《FPGA图像处理之三行缓存》这里面,稍作修改一下。
4.1 生成 3*3 矩阵
我们在三行缓存的基础上增加两行两列,使得原图像扩展一下,最后通过一个3*3的矩阵依次缓存下来:
always @(posedge sys_clk) beginif(sys_rst == 1'b1)begin{ro_matrix_11, ro_matrix_12, ro_matrix_13} <= 'd0;{ro_matrix_21, ro_matrix_22, ro_matrix_23} <= 'd0;{ro_matrix_31, ro_matrix_32, ro_matrix_33} <= 'd0;endelse if(w_img_data_valid == 1'b1)begin{ro_matrix_11, ro_matrix_12, ro_matrix_13} <= {ro_matrix_12, ro_matrix_13,w_img_data_1line};{ro_matrix_21, ro_matrix_22, ro_matrix_23} <= {ro_matrix_22, ro_matrix_23,w_img_data_2line};{ro_matrix_31, ro_matrix_32, ro_matrix_33} <= {ro_matrix_32, ro_matrix_33,w_img_data_3line};end
end
4.2 仿真3*3矩阵
为了加快我们的仿真时间,我们在tb文件里先设置图像的长宽为50*50,然后输入的图像数据为0、1、2、3、…2499。仿真代码如下:
`timescale 1ns / 1psmodule tb_img_3line_buffer();reg sys_clk ;
reg sys_rst ;
reg i_img_data_valid ;
reg [23:0] i_img_data ;
reg [12:0] cnt ;
reg [1:0] waite_cnt ;initial beginsys_clk =0;sys_rst = 1;i_img_data_valid = 0;i_img_data = 'd0;#200;sys_rst = 0;
endalways #5 sys_clk = ~sys_clk;always @(posedge sys_clk) beginif(sys_rst == 1'b1)waite_cnt <= 'd0;else if(cnt == 49)waite_cnt <= 'd0;else if(i_img_data_valid == 1'b0)waite_cnt <= waite_cnt + 1'b1;elsewaite_cnt <= waite_cnt;
endalways @(posedge sys_clk) beginif(sys_rst == 1'b1)i_img_data_valid <= 1'b0;else if(cnt == 49)i_img_data_valid <= 1'b0;else if((waite_cnt == 3) && (i_img_data <= 2499))i_img_data_valid <= 1'b1;elsei_img_data_valid <= i_img_data_valid;
endalways @(posedge sys_clk) beginif(sys_rst == 1'b1)cnt <= 'd0;else if(cnt == 49)cnt <= 'd0;else if(i_img_data_valid == 1'b1)cnt <= cnt + 1'b1;elsecnt <= cnt;
endalways @(posedge sys_clk) beginif(sys_rst == 1'b1)i_img_data <= 'd0;//else if(i_img_data == 49)// i_img_data <= 'd0;else if(i_img_data_valid == 1'b1)i_img_data <= i_img_data + 1'b1;elsei_img_data <= i_img_data;
endendmodule
在代码里,我设置的扩展行列数据用十进制66填充,这样为了更好的观察,实际上可以根据需要任意填充数据。
仿真输入的图像数据如上所示。
读出的矩阵数据应该如上所示,第一个矩阵的数据从上到下,从左到右应该是:66、66、66、66、0、1、66、50、51;第二个矩阵数据应该是:66、66、66、0、1、2、50、51、52;其他的以此类推。我们打开仿真:
我们可以看到输入的图像数据就是从0-2499的累加数,每一行50个数,一共50列,我们来看输出的矩阵:
第一个矩阵是66、66、66、66、0、1、66、50、51;第二个矩阵数据是:66、66、66、0、1、2、50、51、52;和我们设定的一致,其他的也是一样。
4.3 计算均值
均值计算比较简单,就是九个数据相加起来除以9。我们知道FPGA不擅长做除法,因此我们可以先扩大再截位,可以先把数据扩大2^15 / 9 = 3640。然后取高8位即可,Verilog代码如下:
always @(posedge sys_clk) beginif(sys_rst == 1'b1)beginsum_1_line <= 'd0;sum_2_line <= 'd0;sum_3_line <= 'd0;all_sum <= 'd0;endelse beginsum_1_line <= w_matrix_11[23:16] + w_matrix_12[23:16] + w_matrix_13[23:16];sum_2_line <= w_matrix_21[23:16] + w_matrix_22[23:16] + w_matrix_23[23:16];sum_3_line <= w_matrix_31[23:16] + w_matrix_32[23:16] + w_matrix_33[23:16];all_sum <= sum_1_line + sum_2_line + sum_3_line;end
end//延迟了1拍,avg = all_sum / 9 = all_sum * 3641 >> 15
always @(posedge sys_clk) beginif(sys_rst)sum_mult <= 'd0;elsesum_mult <= all_sum * 12'd3641;
end//延迟了1拍
always @(posedge sys_clk) beginif(sys_rst)agv_data <= 'd0;elseagv_data <= sum_mult[22:15] + sum_mult[14]; //四舍五入
end
4.4 仿真均值滤波
我们先用matlab给一张图片加上椒盐噪声然后生成灰度图片,matlab代码如下:
% 读取原始灰度图像
originalImage = imread('your_image.jpg'); % 替换为你的图像文件名% 检查图像是否为灰度图像
if size(originalImage, 3) == 1% 如果是灰度图像,直接使用grayImage = originalImage;
else% 如果是彩色图像,转换为灰度图像grayImage = rgb2gray(originalImage);
end% 将灰度图像转换为 24 位 RGB 图像
rgbImage = cat(3, grayImage, grayImage, grayImage);% 添加椒盐噪声(可以根据需要选择其他类型的噪声)
noisyImage = imnoise(rgbImage, 'salt & pepper', 0.02); % 2% 椒盐噪声% 保存带噪声的图像为 24 位 BMP 文件
imwrite(noisyImage, 'noisy_image.bmp'); % 保存为 noisy_image.bmp% 应用均值滤波
filteredImage = imfilter(noisyImage, fspecial('average', [3 3]), 'replicate');% 显示原始图像、带噪声的图像和均值滤波后的图像
figure;subplot(1, 3, 1);
imshow(rgbImage);
title('原始灰度图像(RGB)');subplot(1, 3, 2);
imshow(noisyImage);
title('带噪声的图像');subplot(1, 3, 3);
imshow(filteredImage);
title('均值滤波后的图像');
生成图片后,我们输入到仿真里面,添加均值滤波模块然后启动仿真。
我们可以看到噪点被滤波处理变得暗淡了,我们放大看一些细节。
放大后的图像依然有噪点,和matlab仿真放大的几乎一样,这就说明均值滤波不适合滤掉椒盐噪声,后续还会讲到其它滤波算法。
相关文章:

FPGA图像处理之均值滤波
文章目录 一、什么是图像滤波?1.1 噪声类型1.2 滤波类型 二、均值滤波原理2.1 3*3窗口滑动过程2.2 图像扩展 三、Matlab实现均值滤波四、FPGA实现均值滤波4.1 生成 3*3 矩阵4.2 仿真3*3矩阵4.3 计算均值4.4 仿真均值滤波 一、什么是图像滤波? 图像滤波是…...

高等数学 6.2 定积分在几何学上的应用
文章目录 一、平面图形的面积1.直角坐标情形2.极坐标情形 二、体积1.旋转体体积2.平行截面面积为已知的立体的体积 三、平面曲线的弧长 一、平面图形的面积 1.直角坐标情形 我们已经知道,由曲线 y f ( x ) ( f ( x ) ⩾ 0 ) y f(x) (f(x) \geqslant 0) yf(x)(f…...

缓存常见问题:缓存穿透、雪崩、击穿及解决方案分析
1. 什么是缓存穿透,怎么解决? 缓存穿透是指用户请求的数据在缓存中不存在即没有命中,同时在数据库中也不存在,导致用户每次请求该数据都要去数据库中查询一遍。如果有恶意攻击者不断请求系统中不存在的数据,会导致短时…...

C++:拷贝构造
拷贝构造函数是参数类型为本类的引用的构造函数,它也叫复制构造函数,它只有一个参数。当没有写拷贝构造函数时,会有一个默认的拷贝构造函数。 class AA { public:AA(AA& ra){}} 那么什么时候会调用此函数呢?有以下三种情况 …...

BGP(边界网关协议)
1、网络AS(自治系统) 边界网关协议BGP(Border Gateway Protocol)是一种实现自治系统AS(Autonomous System)之间的路由可达,并选择最佳路由的距离矢量路由协议。 AS是指在一个实体管辖下的拥有…...

Spring 概念汇总
一、Spring中的依赖注入和依赖反转 依赖注入(Dependency Injection) 概念 依赖注入是一种设计模式,它允许在对象创建时将其依赖的对象传递给它,而不是让对象自己去创建或查找依赖对象。在Spring中,依赖注入是控制反转…...

快速在找到函数的实体的方法
当我们写了许多许多的函数,那我们怎么快速的找到他们呢 我们只需要按下ctrl,在点击函数名字就可以快速的找到我们想要的函数...

05 django管理系统 - 部门管理 - 修改部门
04我们已经实现了新增部门的功能,下面开始修改部门模块的实现。 按道理来说,应该是做成弹框样式的,通过ajax悄咪咪的发数据,然后更新前端数据,但是考虑到实际情况,先用页面跳转的方式实现,后面…...

C++初阶——入门
目录 1、C发展历史 2、C版本更新 3、C参考文档 4、C书籍推荐 5、C的程序 6、命名空间 6.1 namespace的作用 6.2 namespace的定义 6.3 namespace的使用 7、C输入&输出 8、缺省参数 9、函数重载 10、引用 10.1 引用的概念和定义 10.2 引用的特性 10.3 引用的使…...

Java基于SSM微信小程序物流仓库管理系统设计与实现(源码+lw+数据库+讲解等)
选题背景 随着社会的发展,社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景,运用软件工程原理和开发方法,它主要是采用java语言技术和mysql数据库来完成对系统的设计。整个…...

82.【C语言】数据结构之顺序表的初始化和销毁
目录 1.线性表 2.分类 1.静态顺序表:使用定长数组存储元素 代码示例(写入Seqlist.h中) 2.动态顺序表:使用与动态内存管理有关的函数 代码示例(写入Seqlist.h中) 补:数据管理的四个需求:增改删查 3.操作顺序表 1.初始化顺序表 1.不开辟空间 2.开辟空间 1…...

java-推荐一个控制台输出颜色ANSI字符的类
java-推荐一个控制台输出颜色ANSI字符的类 背景代码调用输出 背景 这个类是来自hive的一段代码,大家可以参考一下,这个类名是ColorBuffer 代码 /** Licensed to the Apache Software Foundation (ASF) under one* or more contributor license agreem…...

关于定义结构体别名时 是否加*
在C语言中,使用typedef来定义结构体类型及其指针的别名时,Node和LinkList的声明方式有所不同,这是因为你对它们的目的和用途有不同的设定。 首先,看一下你的代码: typedef struct { int data; int lenght; // 注意&am…...

成语积累学习
识文断字:有一点文化知识 雨后春笋:春雨过后快速生长的竹笋;比喻大量涌现的新生事物 味同嚼蜡:如同咀嚼白蜡一样,毫无味道。形容文章或言辞枯燥乏味。 差强人意:大体上让人满意 八面玲珑:处…...

基于Java的茶叶商城设计与实现(源码+定制+开发)茶叶电商系统开发、茶叶电商平台开发、茶叶在线销售平台设计与开发
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...

桥接、NAT和仅主机三种网络模式对虚拟机IP地址分配的影响
在虚拟机中,桥接、NAT和仅主机(Host-Only)这三种网络模式会给虚拟机带来不同的IP地址分配方式及相应的网络连接特性,从而产生不同的影响,具体如下: 桥接模式 IP地址分配特点:在桥接模式下&…...

音乐播放器-0.专栏介绍
1.简介 本专栏使用Qt QWidget作为显示界面,你将会学习到以下内容: 1.大量ui美化的实例。 2.各种复杂ui布局。 3.常见显示效果实现。 4.大量QSS实例。 5.Qt音频播放,音乐歌词文件加载,展示。 6.播放器界面换肤。 相信学习了本专栏…...

单月变现3W!AI助力沙雕图文爆红小绿书,12篇阅读量破10万+!
最近有没有小伙伴注意到,在各大社交平台上,那些温馨治愈、搞笑沙雕的图文内容,能吸引大量的目光和流量,不久前,我也曾分享过这类内容,比如让人眼前一亮的人间清醒老奶奶,她的图文就属于这类流行…...

C语言复习第4章 数组
目录 一、一维数组的创建和初始化1.1数组的创建1.2 变长数组1.3 数组的初始化1.4 全局数组默认初始化为01.5 区分两种字符数组1.6 用sizeof计算数组元素个数1.7 如何访问数组元素1.8 一维数组在内存中的存储(连续存储)1.9 访问数组元素的另一种方式:指针变量1.10 数组越界是运行…...

大数据研究实训室建设方案
一、概述 本方案旨在提出一套全面的大数据研究实训室建设策略,旨在为学生打造一个集理论学习与实践操作于一体的高端教育环境。实训室将专注于培养学生在大数据处理、分析及应用领域的专业技能,通过先进的设施配置、科学的课程体系和实用的实训模式&…...

Unity3D 观察者模式
Unity3D 泛型事件系统 观察者模式 观察者模式是一种行为设计模式,通过订阅机制,可以让对象触发事件时,通知多个其他对象。 在游戏逻辑中,UI 界面通常会监听一些事件,当数据层发生变化时,通过触发事件&am…...

vue从0开始的项目搭建(含环境配置)
一、环境准备 下载node.js 检查node.js版本 替换npm下载源 1.下载node.js: Node.js — 在任何地方运行 JavaScript (nodejs.org) 2.查看版本: windowsr输入cmd进入输入node -v命令查看版本号是否出现确认是否安装 2.替换npm下载源: npm config set registry https://reg…...

力扣61~65题
题61(中等): 分析: python代码: # Definition for singly-linked list. # class ListNode: # def __init__(self, val0, nextNone): # self.val val # self.next next class Solution:def rot…...

API接口开发流程与指南
API(应用程序编程接口)是现代软件开发中不可或缺的一部分,它允许不同的软件应用之间进行交互和数据交换。无论是调用第三方服务、集成内部系统还是开发微服务架构,API都扮演着关键角色。本文将为你提供一个API接口入门的详解&…...

如何在Android中进行日志打印和调试?
在Android开发中,日志打印和调试是开发者定位问题、优化性能和提升应用质量的重要手段。以下将详细阐述如何在Android中进行日志打印和调试,包括日志工具的使用、调试技巧以及实践中的最佳实践。 一、日志工具的使用 1. Log类 Android中的日志工具类是…...

Linux基本使用和程序部署
文章目录 一. Linux背景Linux发行版 二. Linux环境搭建Linux常见命令lspwdcdtouchcatmkdirrmcpmvtailvimgreppsnetstat管道 三. 搭建java部署环境安装jdk安装mysql部署Web项目到Linux 一. Linux背景 1969−1970年,⻉尔实验室的DennisRitchie和KenTompson开发了Unix操作系统. 他…...

照片编辑成动态视频用什么软件好
在数字时代,让照片动起来确实已成为一种流行的潮流和趋势。如今,市面上涌现出众多软件,它们不仅配备了丰富多样的动态效果和特效,还支持用户进行个性化的编辑和创作。无论你是希望将家庭合影转化为充满温情的动画,还是…...

JavaWeb合集-SpringBoot项目配套知识
四、SpringBoot项目配套知识 1、Springboot项目的创建 2、HTTP 概念: Hyper Text Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则。 2.1 request 请求协议 浏览器向服务器发送请求的规则(get、post等)。 2.1.1 请…...

Electron入门笔记
Electron入门笔记 ElectronElectron 是什么Electron流程模型创建第一个Electron项目配置自动重启主进程和渲染进程通信打包应用 Electron Electron 是什么 跨平台的桌面应用开发框架使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium和 Node.js Electro…...

python 不相交集简介(并查集算法)【Introduction to Disjoint Set (Union-Find Algorithm)】
什么是不相交集数据结构? 如果两个集合没有任何共同元素,则它们被称为不相交集,集合的交集为空集。 存储不重叠或不相交元素子集的数据结构称为不相交集合数据结构。不相交集合数据结构支持以下操作: 1、将新集合添加到不相交集合…...