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 数组越界是运行…...

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

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...