当前位置: 首页 > news >正文

【盘古100Pro+开发板实验例程】FPGA学习 | 均值滤波 | 图像实验指导手册

本原创文章由深圳市小眼睛科技有限公司创作,版权归本公司所有,如需转载,需授权并注明出处(www.meyesemi.com)

1. 实验简介

实验目的:

      完成图像的均值滤波

实验环境:

      Window11

      PDS2022.SP6.4

      Modelsim10.6c

      MatlabR2023b

硬件环境:

      MES2L676-100HP

2. 实验原理

2.1. 均值滤波概念介绍

      均值滤波(Mean Filtering)是一种简单的图像处理技术,用于平滑图像和处理噪声。其基本思想是用像素 点周围的邻域像素的平均值来代替该像素的值,其原理是使用一个固定大小的窗口(如 3x3 或 5x5)覆盖目标 像素及其邻域,计算该区域内所有像素值的均值,并用这个均值替代窗口中心像素的值,再将窗口滑动到图像的 每个像素位置重复该操作。

      均值滤波简单易实现,计算量小,能够有效平滑图像和减少噪声,但同时也会导致图像细节和边缘模糊,可 能会损失某些特征,因此对于需要保留边缘细节的应用,通常会选择更高级的滤波方法。

2.2. 均值滤波原理介绍

      我们以 3x3 大小的区域为例子,它其实是将这个范围内的 9 个值进行求和的平均值,以这个新值来代替这个区域的中心值。

      我们配合下图进行理解:

可以看到 3x3 区域中中间像素值由 40 被替换为 107,其计算过程为:

(197+25+106+107+40+107+163+149+71)/9=107

接着移动 3x3 窗口对图片进行逐行逐列进行均值运算,即可完成图片的均值滤波操作

具体操作过程如下:

假设有一张 8 位灰度图(其像素值区间在 0~255)进行第一次运算后值 145 替换为 127

第二次运算后 242 被替换为 123:

      以此类推逐行逐列进行均值运算(其实就是使用一个 3x3 卷积核对整张图片进行卷积操作再除以 9,只不 过卷积核为全 1),最终实现整张图片的滤波操作。

3. 接口列表

      以下为 aver_filter.v 模块的接口列表

4. 工程说明

      上图为本次工程架构,HDMI 输入后经过 MS7200 芯片解码出 RGB 数据,然后经过灰度化后进行均值滤波, 然后将数据存到 DDR3 之中,然后再从 DDR3 中读出到 HDMI 上显示。

4.1. 代码模块说明

本章节主要对均值滤波模块进行说明,以下是均值滤波 verilog 代码:

// 均值滤波
module aver_filter (input wire video_clk /*synthesis PAP_MARK_DEBUG="1"*/,input wire rst_n,// 矩阵数据输入input wire matrix_de /*synthesis PAP_MARK_DEBUG="1"*/,input wire matrix_vs /*synthesis PAP_MARK_DEBUG="1"*/,input wire [7:0] matrix11,input wire [7:0] matrix12,input wire [7:0] matrix13,input wire [7:0] matrix21,input wire [7:0] matrix22,input wire [7:0] matrix23,input wire [7:0] matrix31,input wire [7:0] matrix32,input wire [7:0] matrix33,output wire aver_filter_vs,output wire aver_filter_de,output wire [7:0] aver_filter_data
);/***************************************************************************
* step1 每行相加 delay:1clk
***************************************************************************/
reg [9:0] line1_sum;
reg [9:0] line2_sum;
reg [9:0] line3_sum;always @(posedge video_clk or negedge rst_n) beginif (!rst_n) beginline1_sum <= 10'd0;line2_sum <= 10'd0;line3_sum <= 10'd0;endelse if (matrix_de) beginline1_sum <= matrix11 + matrix12 + matrix13;line2_sum <= matrix21 + matrix22 + matrix23;line3_sum <= matrix31 + matrix32 + matrix33;endelse beginline1_sum <= 10'd0;line2_sum <= 10'd0;line3_sum <= 10'd0;end
end/***************************************************************************
* step2 矩阵总和 delay:1clk
***************************************************************************/
reg [11:0] data_sum;always @(posedge video_clk or negedge rst_n) beginif (!rst_n)data_sum <= 12'd0;elsedata_sum <= line1_sum + line2_sum + line3_sum;
end/***************************************************************************
* step3 求均值 /9 delay:1clk
* 除法转乘法 *228>>11
***************************************************************************/
reg [18:0] aver_filter_mux; // 均值always @(posedge video_clk or negedge rst_n) beginif (!rst_n)aver_filter_mux <= 19'd0;elseaver_filter_mux <= data_sum * 228; // 后续要>>11
end/***************************************************************************
* step4 中心像素点与阈值进行比较 delay:1clk
***************************************************************************/
reg [7:0] aver_filter_reg;always @(posedge video_clk or negedge rst_n) beginif (!rst_n)aver_filter_reg <= 1'd0;elseaver_filter_reg <= aver_filter_mux[18:11];
end/***************************************************************************
* 时钟延迟 一共延迟 4clk
***************************************************************************/
reg [3:0] video_de_reg;
reg [3:0] video_vs_reg;always @(posedge video_clk or negedge rst_n) beginif (!rst_n) beginvideo_de_reg <= 4'd0;video_vs_reg <= 4'd0;endelse beginvideo_de_reg <= {video_de_reg[2:0], matrix_de};video_vs_reg <= {video_vs_reg[2:0], matrix_vs};end
endassign aver_filter_vs = video_vs_reg[3];
assign aver_filter_de = video_de_reg[3];
assign aver_filter_data = aver_filter_reg;endmodule

      这个模块实现了一个简单的均值滤波器,用于处理图像矩阵数据。其主要功能是对输入的 3x3 图像矩阵进 行均值计算,并输出处理后的中心像素值。

      代码在第 34 - 53 行,在 matrix_de 为高时(输入矩阵数据有效时)对每行的 3 个像素值进行相加,分别 存储到 `ine1_sum、`ine2_sum 和 line3_sum 中。

      代码在第 58 - 64 行,将 3 行的和相加,得到 3x3 矩阵的总和,并存储在 data_sum 中。注意 data_sum 需要考虑位宽,例如如果 9 个像素的值都是 255,那么 data_sum 在计算过程中的最大值为 2295,转换为二进 制为 12 位宽,为了防止数据溢出`data_sum`位宽声明需要大于 12 位。

      为了提高硬件电路的效率,通常避免直接使用除法操作,因为除法在硬件中实现相对复杂且占用资源较多。 为了简化运算,常常将除法转化为乘法和移位的形式。代码在第 70 - 90行,利用乘法代替除法的方式(*228>>11) 来计算均值,将总和乘以 228,然后右移 11 位(即除以 9),得到的结果存储在 aver_filter_mux 中。

      这里使用了除法转乘法的思想,具体解释如下:

      我们知道右移操作在硬件上是快速高效的,对数据右移 1 位相当于将数据除以 2,右移 n 位相当于将数据 除以 2^n。要实现 data_sum / 9,可以找到一个常数 C,使得 data_sum * C >> n, 接近于 data_sum / 9。 这里选择 C = 228,n=11,这是因为 228/2^11 =0.1113

      而 `1/9 ≈ 0.1111`,这个常数 `228` 近似达到了 `1/9` 的效果。乘以 228 后,右移 11 位 (>> 11) 来实现除法的效果。移位操作相当于除以 2^{11},这与之前的乘法结合起来实现接近于 除以 9 的运算。

      通过乘以 228 然后右移 11 位(代码中以截取的方式实现,这两种方式都是可行的),成功地将原本复杂 的除以 9 操作转化为一个较简单的乘法和移位操作。这种方法在硬件设计中非常常见,可以有效减少运算延迟 和硬件资源的使用。

      最后代码 98-116 行将 matrix_de 和 matrix_vs 信号延后了一个时钟,使其与输出数据对齐。 video_de_reg 和 video_vs_reg 用于存储这些延迟后的信号。将延迟后的信号和数据输出到 aver_filter_vs、 aver_filter_de 和 aver_filter_data。

      通过这些步骤,整个模块实现了对输入的 3x3 图像矩阵进行均值滤波的功能。

4.2. 代码仿真
4.2.1. Matlab 仿真介绍
%% 读取图像
originalImage = imread('noise.png');
figure;%% 显示原图
subplot(1, 4, 1);
imshow(originalImage);
title('原图像');%% 灰度化
grayImage = rgb2gray(originalImage);%% 图像尺寸
[img_height, img_width] = size(grayImage);
medianImage = zeros(img_height, img_width);%% 定义参数
windowSize = 3; % 窗口大小,奇数%% 遍历像素 计算窗口均值
halfWindowSize = floor(windowSize / 2);for i = 1:img_heightfor j = 1:img_width% 窗口边界r1 = max(i - halfWindowSize, 1);    % 窗口上边界r2 = min(i + halfWindowSize, img_height); % 窗口下边界c1 = max(j - halfWindowSize, 1);    % 窗口左边界c2 = min(j + halfWindowSize, img_width);  % 窗口右边界% 提取窗口window = grayImage(r1:r2, c1:c2);% 计算窗口内的均值 /9 = (*228) >> 11localmedian = sum(window(:)) * 228.0;medianImage(i,j) = localmedian / 2048.0;end
end%% 无符号 8bit
medianImage = uint8(medianImage);%% 将原图像转换为二值图像
binarizedImage = imbinarize(grayImage);% 显示原图和处理后的图像
subplot(1, 4, 2);
imshow(grayImage);
imwrite(grayImage, 'gray.png');
title('灰度化图像');subplot(1, 4, 3);
imshow(medianImage);
imwrite(medianImage, 'matlab_aver.png');
title('均值滤波图像');subplot(1, 4, 4);
medianImage = imread('matlab_median.png');
imshow(medianImage);
title('中值滤波图像');

      代码第 1 行 imread('noise.png');读取名为 noise.png 的图像文件,并将其存储在变量 originalI mage 中。

      代码第 8 行 grayImage = rgb2gray(originalImage); 将原始图像转换为灰度图像,并存储在grayImage 中。灰度化的目的是减少颜色信息,仅保留亮度信息以简化后续处理。

      代码第 9 行 [img_height, img_width] = size(grayImage);获取灰度图像的高度和宽度。

      代码第 10 行 medianImage = zeros(img_height, img_width); 初始化一个与灰度图像相同大小的矩阵medianImage,用于存储均值滤波后的图像。

      代码第 12 行 windowSize = 3;定义窗口大小为 3x3,这是进行均值滤波时的窗口。

      代码第 14-23 行 halfWindowSize = floor(windowSize / 2);计算窗口的一半大小,用于确定窗口边界。 使用双重循环遍历每个像素,计算每个像素的均值滤波值。r1 和 r2 分别确定窗口的上边界和下边界。c1 和 c2 分别确定窗口的左边界和右边界。window = grayImage(r1:r2, c1:c2);提取当前像素点周围的窗口区域。

      代码第 26 行 localmedian = sum(window(:))*228.0; 计算窗口内像素值的总和并乘以 228,这是用于验证 verilog 中近似计算均值的方法。

      代码第 27 行 medianImage(i,j) = localmedian/2048.0; 将计算结果除以 2048(模拟右移 11 位),得到 最终的均值滤波后的像素值,并存储在 medianImage 中。medianImage = uint8(medianImage);`将滤波后 的图像矩阵转换为 8 位无符号整数格式,用于图像显示。

      通过这些步骤,实现了对图像进行灰度化、均值滤波处理,并展示原图像、灰度图像、均值滤波图像以及中值滤波图像。

4.2.2. Modelsim 仿真介绍

      Tb 文件其实整体框架基本一致,只需要修改中间的例化模块和最后的有效信号和场信号以及数据即可,仅 给出修改后的部分,具体波形可以看视频或者读者自行仿真,只放置最后的 matlab 复原结果。

// 读取图片数据
video_data_gen #(.TOTAL_WIDTH (12'd1650),.IMG_WIDTH  (12'd1280),.H_SYNC     (12'd40),.H_BP       (12'd220),.H_FP       (12'd110),.TOTAL_HEIGHT (12'd750),.IMG_HEIGHT (12'd720),.V_SYNC     (12'd5),.V_BP       (12'd20),.V_FP       (12'd5)
) u_video_data_gen (.video_clk  (video_clk),.rst_n      (rst_n),.video_vs   (video_vs),.video_de   (video_de),.video_data (video_data)
);// 灰度化
RGB2YCbCr u_RGB2YCbCr (.clk        (video_clk),.rst_n      (rst_n),.vsync_in   (video_vs),.hsync_in   (video_de),.de_in      (video_de),.red        (video_data[23:19]),.green      (video_data[15:10]),.blue       (video_data[7:3]),.vsync_out  (y_vs),.hsync_out  (y_hs),.de_out     (y_de),.y          (y_data),.cb         (),.cr         ()
);// 矩阵生成
matrix_3x3 #(.IMG_WIDTH  (IMG_WIDTH),.IMG_HEIGHT (IMG_HEIGHT)
) u_matrix_3x3 (.video_clk  (video_clk),.rst_n      (rst_n),.video_vs   (y_vs),.video_de   (y_de),.video_data (y_data),.matrix_de  (matrix_de),.matrix11   (matrix11),.matrix12   (matrix12),.matrix13   (matrix13),.matrix21   (matrix21),.matrix22   (matrix22),.matrix23   (matrix23),.matrix31   (matrix31),.matrix32   (matrix32),.matrix33   (matrix33)
);// 均值滤波
aver_filter u_aver_filter (.video_clk      (video_clk),.rst_n          (rst_n),.matrix_de      (matrix_de),.matrix_vs      (y_vs),.matrix11       (matrix11),.matrix12       (matrix12),.matrix13       (matrix13),.matrix21       (matrix21),.matrix22       (matrix22),.matrix23       (matrix23),.matrix31       (matrix31),.matrix32       (matrix32),.matrix33       (matrix33),.aver_filter_vs (aver_filter_vs),.aver_filter_de (aver_filter_de),.aver_filter_data (aver_filter_data)
);

读者可以看到 tb 结构一九和中值滤波一致,读取图片数据,灰度化,生成矩阵是一致的,只不过将中值滤波 模块换成了均值滤波模块,并把后续的信号也换成均值滤波后的信号即可,如下所示:

always @(posedge video_clk or negedge rst_n) beginif (!rst_n)video_vs_d <= 1'd0;elsevideo_vs_d <= aver_filter_vs;
endassign frame_flag = ~aver_filter_vs & video_vs_d;  // 下降沿检测always @(posedge video_clk or negedge rst_n) beginif (!rst_n)img_done <= 1'b0;else if (frame_flag)  // 下降沿判断一帧结束img_done <= 1'b1;elseimg_done <= img_done;
endalways @(posedge video_clk or negedge rst_n) beginif (img_done) begin$stop;  // 停止仿真endelse if (aver_filter_de) begin  // 写入数据$fdisplay(output_file, "%h\t%h\t%h", aver_filter_data, aver_filter_data, aver_filter_data);  // 16进制写入end
end

最后放到 matlab 中复原,如下所示:

4.3. 实验现象

连接好下载器,电源、HDMI_IN 口连接电脑、HDMD_OUT 口连接显示器,然后下载程序。

上图为添加了椒盐噪声的图像。

可以看到椒盐噪声变的平滑,比原图像有所改善,但效果比中值滤波差,均值滤波主要让图像平滑,无法过 滤掉椒盐噪声。

http://www.dtcms.com/a/328258.html

相关文章:

  • 【代码随想录day 18】 力扣 501.二叉搜索树中的众数
  • 免费播客翻译与转录:用中文收听全球播客
  • Langchain入门:文本摘要
  • C++学习之数据结构:AVL树
  • java八股文-MySql面试题-参考回答
  • GPFS api
  • 在 C语言 中构建安全泛型容器:使用 maybe 实现安全除法
  • 【PCB设计经验】去耦电容如何布局?
  • 力扣top100(day01-04)
  • 企业级的即时通讯平台怎么保护敏感行业通讯安全?
  • 电竞 体育数据 API 应用场景全解析
  • Day50--图论--98. 所有可达路径(卡码网),797. 所有可能的路径
  • Quartz
  • Mybatis源码解读-SqlSession 会话源码和Executor SQL操作执行器源码
  • 谷歌云代理商:用 AI 启航,Gemini 重塑旅游酒店行业新体验
  • 【SpringBoot】07 容器功能 - SpringBoot底层注解的应用与实战 - @ConfigurationProperties配置绑定
  • 从0入门LangGraph,手搓高质量Agent
  • 【自动化运维神器Ansible】playbook文件内变量定义全流程解析
  • 谷歌ADK接入文件操作MCP
  • Linux中Https配置与私有CA部署指南
  • Java 工厂方法模式
  • C++单继承虚函数表探索
  • 京东方 DV133FHM-NN1 FHD13.3寸 工业液晶模组技术档案
  • 玩转Docker | 使用Docker部署Radicale日历和联系人工具
  • [激光原理与应用-250]:理论 - 几何光学 - 透镜成像的优缺点,以及如克服缺点
  • 万物平台模型导入样例大全(实时更新中~)
  • SM4对称加密算法的加密模式介绍
  • JavaEE 初阶第十八期:叩开网络世界的大门(上)
  • ffmpeg-AVFilter 和 Filter Graph 使用指南
  • ffmpeg,ffplay, vlc,rtsp-simple-server,推拉流命令使用方法,及测试(二)