1)实验平台:正点原子超越者FPGA开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=631660290421
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-chaoyuezhe.html
4)本章实例源码下载:
45_ov5640_hdmi_median_filter.rar
(11.18 MB, 下载次数: 386)
5)正点原子FPGA交流群:905624739点击加入:![]()
6)关注正点原子公众号,获取最新资料更新
![]()
![]()
第五十二章基于OV5640的中值滤波实验
在数字图像处理中,无论是直接获取的灰度图像,还是由彩色图像转换得到的灰度图像,里面都有噪点的存在,噪点对图像质量有很大的影响。空间滤波是一种常用的降噪方法,进行中值滤波(一种空间滤波)不仅可以去除孤立噪点,而且可以保持图像的边缘特性,不会使图像产生显著的模糊,比较适合于实验中的人脸图像。本章实验将进行OV5640摄像头采集RGB565数据,转化为YUV数据,然后进行中值滤波的实验。
本章包括以下几个部分:
5252.1 简介
52.2 实验任务
52.3 硬件设计
52.4 程序设计
52.5 下载验证
52.1简介
滤波是指接收(通过)或过滤掉信号中一定的频率分量,例如,通过低频率的滤波器称为低通滤波器。空间滤波是图像处理领域应用非常广泛的工具之一,它可以改善图像质量,包括去除高频噪点的干扰、图像平滑等。大家常见的空间滤波有中值滤波和均值滤波。
图像可以看成是一个定义在二维平面上的信号,该信号的幅值对应像素的灰度(彩色图像对应RGB三个分量)。图像的频率指的是空间频率,它和平常认知的物理频率是不同的。图像的频率是表征图像中灰度变化剧烈程度的指标,是灰度在平面空间上的梯度。不同频率信息在图像结构中有不同的作用。图像的主要成分是低频信息,它形成了图像的基本灰度等级,对图像结构的决定作用较小;中频信息决定了图像的基本结构,形成了图像的主要边缘结构;高频信息形成了图像的边缘和细节,是在中频信息上对图像内容的进一步强化。
大家也可以通过空间滤波器(也称为空间掩模、模板或窗口)直接作用于图像本身从而对图像进行滤波处理。空间滤波器由两部分组成:
(1)邻域,
(2)对该邻域包围的图像像素执行的预定义操作。
领域是指一个像素点及其附近像素点所组成的空间。滤波会产生一个新像素,像素的坐标就是邻域中心的坐标,像素的值就是滤波操作的结果。
中值滤波就是一种很常见的空间滤波,它是一种非线性平滑技术。它将每一像素点及该像素点的邻域作为一个滤波模板,计算出模板中所有像素点灰度值的中值,然后用它代替模板中心像素点的值。图 52.1.1为像素点P及其周围8个像素点所组成的3x3滤波模板:
图 52.1.1 中值滤波模板
中值滤波是一种基于排序统计理论的非线性信号处理技术,它可以消除孤立的噪点,从而让图像中的像素值更接近真实值。红外图像中的盲元就是一种孤立噪点的例子。由于红外探测器制造过程中的缺陷,传感器中某些像元的输出可能会非常大,导致图像中对应的像素点非常亮,通常称之为盲元,如下图中红色箭头所示:
图 52.1.2 红外图像中的盲元
中值滤波对类似于上图中的脉冲噪点有良好的滤除作用,特别是在滤除噪点的同时,能够保护信号的边缘,使之不被模糊。这些优良特性是线性滤波方法所不具备的。此外,中值滤波的算法比较简单,也易于用硬件实现。所以,中值滤波方法一经提出后,便在数字信号处理领域得到广泛的应用。
关于中值滤波如何快速求得中值,有多种方法实现,例如冒泡排序法、选择排序法等方法。但是用Verilog实现这些排序算法不仅很复杂而且运算速率同时也会大大降低。在本章实验中采用流水线操作的方式,在图像的3x3矩阵中实现快速排序。下面是算法流程框图:
图 52.1.3 中值滤波算法框图
首先生成一个3x3的像素阵列,然后在分别对每行3个像素进行排序,得出每行的最大、中值和最小值(如上图Max1、Med1和Min1)。接着,对排序后的矩阵进行处理,即提取三个最大值中的最小值(Minz_of_Max),三个中间值的中间值(Med_of_Med),以及三个最小值中的最大值(Max_of_Min)。最后,将得到的三个值,再次取中值,最终求得9个像素的中值。
52.2实验任务
本次实验任务是利用OV5640摄像头采集RGB565数据,使用FPGA将OV5640采集到的彩色图像数据转换为灰度图像数据,然后对灰度图像数据进行中值滤波处理,最后在HDMI显示器上实时显示出来。
52.3硬件设计
本章节中硬件设计与“OV5640摄像头HDMI显示实验”完全相同,此处不再赘述。
52.4程序设计
根据实验任务,可以设计出如图 52.4.1所示的系统框图。本章实验的系统框架延续了“OV5640摄像头HDMI灰度显示实验”的整体架构。本次实验包括以下模块:时钟模块、DDR控制器模块、摄像头驱动模块、图像处理模块和HDMI顶层模块。其中时钟模块、DDR控制器模块、HDMI顶层模块和摄像头驱动模块没有做任何修改,这些模块在“OV5640摄像头HDMI显示实验”中已经介绍过,这里不再赘述,本次实验只对图像处理模块做了修改。
顶层系统框图如下所示:
图 52.4.1 顶层系统框图
由上图可知,时钟模块(clock)为HDMI顶层模块、DDR控制模块以及摄像头驱动模块提供驱动时钟。摄像头驱动模块控制着传感器初始化的开始与结束,传感器初始化完成后图像采集模块将采集到的数据写入图像处理模块,图像处理模块将处理后的数据存入DDR3,HDMI顶层模块从DDR控制模块中读出数据并驱动显示器显示,这时整个系统才完成了数据的采集、缓存与显示。需要注意的是图像数据采集模块是在DDR3和传感器都初始化完成之后才开始输出数据的,避免了在DDR3初始化过程中向里面写入数据。
顶层模块的代码如下:
FPGA顶层模块(ov5640_hdmi_median_filter)例化了以下五个模块:时钟模块(clock)、OV5640驱动模块(ov5640_dri)、图像处理模块(vip)、DDR控制模块(ddr3_control)和HDMI顶层模块(hdmi_top)。
时钟模块(clock):时钟模块通过调用DCM_CLKGEN IP核实现,共输出3个时钟,频率分别为300Mhz、50Mhz时钟和100Mhz时钟。300Mhz时钟作为DDR控制模块的运行时钟;100Mhz时钟作为DDR控制模块的驱动时钟;50Mhz时钟作为OV5640驱动模块和HDMI顶层模块的驱动时钟。
OV5640驱动模块(ov5640_dri):OV5640驱动模块负责驱动OV5640 SCCB接口总线,将像素时钟驱动下的传感器输出的场同步信号、行同步信号以及8位数据转换成DDR读写控制模块的写使能信号和16位写数据信号,完成对OV5640传感器图像的采集。
图像处理模块(vip):对采集后的图像数据进行处理,并将处理后的数据存入DDR控制模块。
DDR控制模块(ddr3_control):DDR读写控制器模块负责驱动DDR片外存储器,缓存图像传感器输出的图像数据。该模块将MIG IP核复杂的读写操作封装成类似FIFO的用户接口,非常方便用户的使用。有关DDR控制模块的详细介绍请大家参考“DDR读写测试实验”章节。
HDMI顶层模块(hdmi_top):HDMI顶层模块负责驱动HDMI显示器的驱动信号的输出,同时为其他模块提供参数和数据请求信号。HDMI顶层模块例化了HDMI驱动模块(video_driver)和RGB转DVI模块(rgbtodvi_top)。HDMI驱动模块负责产生行场信号和数据有效使能信号和像素点的横纵坐标,并将内部信号data_req(数据请求信号)输出至端口,方便从DDR控制器中读取数据。RGB转DVI模块负责将RGB888格式的视频图像转换成TMDS数据输出。有关HDMI驱动模块、RGB转DVI模块的详细介绍请大家参考“HDMI彩条显示实验”章节。
VIP模块框图如下图所示:
图 52.4.2 vip模块框图
vip模块例化了RGB转YCbCr模块(rgb2ycbcr)和中值滤波模块(vip_gray_median_jilter)。RGB转YCbCr模块负责将摄像头采集的RGB565格式的彩色图像转换为灰度图像。中值滤波模块负责对灰度图像进行中值滤波后输出。有关RGB转YCbCr模块的详细介绍请大家参考“OV5640摄像头HDMI灰度显示实验”章节。
vip模块原理图如下图所示:
图 52.4.3 vip模块原理图
如上图所示,摄像头采集到16位rgb565输入vip模块,经过“rgb2ycbcr”模块转化为8位的灰度值img_y(YUV中的亮度Y),然后在将转化后的灰度数据(img_y)作为“vip_gray_median_filter”模块的输入,对灰度值进行中值滤波处理,最后输出处理后的灰度数据“pos_img_y”。
图像处理模块负责图像数据的格式转换,代码如下:
- 1 module vip(
- 2 //module clock
- 3 input clk , // 时钟信号
- 4 input rst_n , // 复位信号(低有效)
- 5
- 6 //图像处理前的数据接口
- 7 input pre_frame_vsync ,
- 8 input pre_frame_hsync ,
- 9 input pre_frame_de ,
- 10 input [15:0] pre_rgb ,
- 11 input [10:0] xpos ,
- 12 input [10:0] ypos ,
- 13
- 14 //图像处理后的数据接口
- 15 output post_frame_vsync , // 场同步信号
- 16 output post_frame_hsync , // 行同步信号
- 17 output post_frame_de , // 数据输入使能
- 18 output [15:0] post_rgb // RGB565颜色数据
- 19
- 20 );
- 21
- 22 //wire define
- 23 wire [ 7:0] img_y;
- 24 wire [ 7:0] post_img_y;
- 25 wire [15:0] post_rgb;
- 26 wire post_frame_vsync;
- 27 wire post_frame_hsync;
- 28 wire post_frame_de;
- 29 wire pe_frame_vsync;
- 30 wire pe_frame_href;
- 31 wire pe_frame_clken;
- 32
- 33 //*****************************************************
- 34 //** main code
- 35 //*****************************************************
- 36
- 37 assign post_rgb = {post_img_y[7:3],post_img_y[7:2],post_img_y[7:3]};
- 38
- 39
- 40 //RGB转YCbCr模块
- 41 rgb2ycbcr u_rgb2ycbcr(
- 42 //module clock
- 43 .clk (clk ), // 时钟信号
- 44 .rst_n (rst_n ), // 复位信号(低有效)
- 45 //图像处理前的数据接口
- 46 .pre_frame_vsync (pre_frame_vsync), // vsync信号
- 47 .pre_frame_hsync (pre_frame_hsync), // href信号
- 48 .pre_frame_de (pre_frame_de ), // data enable信号
- 49 .img_red (pre_rgb[15:11] ),
- 50 .img_green (pre_rgb[10:5 ] ),
- 51 .img_blue (pre_rgb[ 4:0 ] ),
- 52 //图像处理后的数据接口
- 53 .post_frame_vsync(pe_frame_vsync), // vsync信号
- 54 .post_frame_hsync(pe_frame_href), // href信号
- 55 .post_frame_de (pe_frame_clken), // data enable信号
- 56 .img_y (img_y), //灰度数据
- 57 .img_cb (),
- 58 .img_cr ()
- 59 );
- 60
- 61 //灰度图中值滤波
- 62 vip_gray_median_filter u_vip_gray_median_filter(
- 63 .clk (clk),
- 64 .rst_n (rst_n),
- 65
- 66 //处理前图像数据
- 67 .pe_frame_vsync (pe_frame_vsync), // vsync信号
- 68 .pe_frame_href (pe_frame_href), // href信号
- 69 .pe_frame_clken (pe_frame_clken), // data enable信号
- 70 .pe_img_y (img_y),
- 71
- 72 //处理后的图像数据
- 73 .pos_frame_vsync (post_frame_vsync), // vsync信号
- 74 .pos_frame_href (post_frame_hsync), // href信号
- 75 .pos_frame_clken (post_frame_de), // data enable信号
- 76 .pos_img_y (post_img_y) //中值滤波后的灰度数据
- 77 );
- 78 endmodule
复制代码
代码的第37行表示对滤波后的8bit灰度数据进行位拼接,形成16bit的RGB565格式的数据输出。
代码的第41行至59行是对灰度转换模块的例化。该模块以摄像头采集的16位RGB565红、绿、蓝三原色数据作为输入数据,通过算法实现RGB到YCbCr的转换,并输出8位灰度数据以及数据输出使能信号。有关RGB转YCbCr模块的详细介绍请大家参考“OV5640摄像头HDMI灰度显示实验”章节。
代码的第62行至77行是对中值滤波模块的例化,该模块负责将YUV格式的视频图像进行中值滤波后输出。
中值滤波模块的层次结构如下图:
图 52.4.4 中值滤波模块的层次结构
前面介绍过,通过对图像的3x3矩阵进行排序操作来实现中值滤波,那么在实现中值滤波时,首先要生成3x3的矩阵。“vip_matrix_generate_3x3_8bit”模块实现的是对图像生成一个3x3数据矩阵。“median_filter_3x3”模块实现的是对图像生成的3x3数据矩阵进行中值滤波处理。
中值滤波模块代码如下:
- 1 module vip_gray_median_filter(
- 2 //时钟
- 3 input clk, //50MHz
- 4 input rst_n,
- 5
- 6 //处理前图像数据
- 7 input pe_frame_vsync, //处理前图像数据场信号
- 8 input pe_frame_href, //处理前图像数据行信号
- 9 input pe_frame_clken, //处理前图像数据输入使能效信号
- 10 input [7:0] pe_img_y, //灰度数据
- 11
- 12 //处理后的图像数据
- 13 output pos_frame_vsync, //处理后的图像数据场信号
- 14 output pos_frame_href, //处理后的图像数据行信号
- 15 output pos_frame_clken, //处理后的图像数据输出使能效信号
- 16 output [7:0] pos_img_y //处理后的灰度数据
- 17 );
- 18
- 19 //wire define
- 20 wire matrix_frame_vsync;
- 21 wire matrix_frame_href;
- 22 wire matrix_frame_clken;
- 23 wire [7:0] matrix_p11; //3X3 阵列输出
- 24 wire [7:0] matrix_p12;
- 25 wire [7:0] matrix_p13;
- 26 wire [7:0] matrix_p21;
- 27 wire [7:0] matrix_p22;
- 28 wire [7:0] matrix_p23;
- 29 wire [7:0] matrix_p31;
- 30 wire [7:0] matrix_p32;
- 31 wire [7:0] matrix_p33;
- 32 wire [7:0] mid_value;
- 33
- 34 //*****************************************************
- 35 //** main code
- 36 //*****************************************************
- 37 //在延迟后的行信号有效,将中值赋给灰度输出值
- 38 assign pos_img_y = pos_frame_href ? mid_value : 8'd0;
- 39
- 40 vip_matrix_generate_3x3_8bit u_vip_matrix_generate_3x3_8bit(
- 41 .clk (clk),
- 42 .rst_n (rst_n),
- 43
- 44 //处理前图像数据
- 45 .per_frame_vsync (pe_frame_vsync),
- 46 .per_frame_href (pe_frame_href),
- 47 .per_frame_clken (pe_frame_clken),
- 48 .per_img_y (pe_img_y),
- 49
- 50 //处理后的图像数据
- 51 .matrix_frame_vsync (matrix_frame_vsync),
- 52 .matrix_frame_href (matrix_frame_href),
- 53 .matrix_frame_clken (matrix_frame_clken),
- 54 .matrix_p11 (matrix_p11),
- 55 .matrix_p12 (matrix_p12),
- 56 .matrix_p13 (matrix_p13),
- 57 .matrix_p21 (matrix_p21),
- 58 .matrix_p22 (matrix_p22),
- 59 .matrix_p23 (matrix_p23),
- 60 .matrix_p31 (matrix_p31),
- 61 .matrix_p32 (matrix_p32),
- 62 .matrix_p33 (matrix_p33)
- 63 );
- 64
- 65 //3x3阵列的中值滤波,需要3个时钟
- 66 median_filter_3x3 u_median_filter_3x3(
- 67 .clk (clk),
- 68 .rst_n (rst_n),
- 69
- 70 .median_frame_vsync (matrix_frame_vsync),
- 71 .median_frame_href (matrix_frame_href),
- 72 .median_frame_clken (matrix_frame_clken),
- 73
- 74 //第一行
- 75 .data11 (matrix_p11),
- 76 .data12 (matrix_p12),
- 77 .data13 (matrix_p13),
- 78 //第二行
- 79 .data21 (matrix_p21),
- 80 .data22 (matrix_p22),
- 81 .data23 (matrix_p23),
- 82 //第三行
- 83 .data31 (matrix_p31),
- 84 .data32 (matrix_p32),
- 85 .data33 (matrix_p33),
- 86
- 87 .pos_median_vsync (pos_frame_vsync),
- 88 .pos_median_href (pos_frame_href),
- 89 .pos_median_clken (pos_frame_clken),
- 90 .target_data (mid_value)
- 91 );
- 92
- 93 endmodule
复制代码
在vip_gray_median_filter模块中调用了vip_matrix_generate_3x3_8bit、median_filter_3x3两个模块,它们分别用于生成3x3矩阵和求得矩阵的中值。
vip_matrix_generate_3x3_8bit模块代码如下:
为了获得3x3的滤波模板,本次实验需要使用RAM来存储图像前两行的数据,而当前输入的图像数据作为第三行,如代码中第55行所示。而在代码的第59至69行,当第三行数据到达时,通过调用line_shift_ram_8bit模块,读出寄存在RAM中的前两行数据,从而获得一个“三行一列”的像素数据。三行数据分别位于row1_data、row2_data和row3_data三个变量中,其中row3_data表示当前行(第三行)图像数据。
接下来,将“三行一列”的像素数据,连续寄存三次,从而获取一个“三行三列”的像素阵列,如代码中的第99至101行所示。其中,matrix_p11、matrix_p12、 matrix_p13代表阵列中第一行中的三列像素数据,而matrix_p21、matrix_p22、matrix_p23代表阵列中第二行中的三列像素数据,以此类推。这个“三行三列”的矩阵就是本次实验所需要的3x3模板。
前面获取“三行一列”和获取“三行三列”的操作分别需要一个时钟周期,即该模块生成3x3模板共消耗两个时钟周期。因此,还要对场有效信号、数据有效信号和时钟使能信号延迟两个周期以作同步,如代码第79至81行所示。
代码的第60行调用了“line_shift_ram_8bit”模块,其代码如下:
- 1 module line_shift_ram_8bit(
- 2 input clock,
- 3 input clken,
- 4 input per_frame_href,
- 5
- 6 input [7:0] shiftin,
- 7 output [7:0] taps0x,
- 8 output [7:0] taps1x
- 9 );
- 10
- 11 //reg define
- 12 reg [2:0] clken_dly;
- 13 reg [9:0] ram_rd_addr;
- 14 reg [9:0] ram_rd_addr_d0;
- 15 reg [9:0] ram_rd_addr_d1;
- 16 reg [7:0] shiftin_d0;
- 17 reg [7:0] shiftin_d1;
- 18 reg [7:0] shiftin_d2;
- 19 reg [7:0] taps0x_d0;
- 20
- 21 //*****************************************************
- 22 //** main code
- 23 //*****************************************************
- 24
- 25 //在数据来到时,ram地址累加
- 26 always@(posedge clock)begin
- 27 if(per_frame_href)
- 28 if(clken)
- 29 ram_rd_addr <= ram_rd_addr + 1 ;
- 30 else
- 31 ram_rd_addr <= ram_rd_addr ;
- 32 else
- 33 ram_rd_addr <= 0 ;
- 34 end
- 35
- 36 //时钟使能信号延迟三拍
- 37 always@(posedge clock) begin
- 38 clken_dly <= { clken_dly[1:0] , clken };
- 39 end
- 40
- 41
- 42 //将ram地址延迟二拍
- 43 always@(posedge clock ) begin
- 44 ram_rd_addr_d0 <= ram_rd_addr;
- 45 ram_rd_addr_d1 <= ram_rd_addr_d0;
- 46 end
- 47
- 48 //输入数据延迟三拍
- 49 always@(posedge clock)begin
- 50 shiftin_d0 <= shiftin;
- 51 shiftin_d1 <= shiftin_d0;
- 52 shiftin_d2 <= shiftin_d1;
- 53 end
- 54
- 55 //用于存储前一行图像的RAM
- 56 blk_mem_gen_0 u_ram_1024x8_0(
- 57 .clka (clock),
- 58 .wea (clken_dly[2]), //在延迟的第三个时钟周期,当前行的数据写入RAM0
- 59 .addra (ram_rd_addr_d1),
- 60 .dina (shiftin_d2),
- 61 .clkb (clock),
- 62 .addrb (ram_rd_addr),
- 63 .doutb (taps0x) //延迟一个时钟周期,输出RAM0中前一行图像的数据
- 64 );
- 65
- 66 //寄存一次前一行图像的数据
- 67 always@(posedge clock ) begin
- 68 taps0x_d0 <= taps0x;
- 69 end
- 70
- 71 blk_mem_gen_0 u_ram_1024x8_1(
- 72 .clka (clock),
- 73 .wea (clken_dly[1]), //在延迟的第二个时钟周期,将前一行图像的数据写入RAM1
- 74 .addra (ram_rd_addr_d0),
- 75 .dina (taps0x_d0),
- 76 .clkb (clock),
- 77 .addrb (ram_rd_addr),
- 78 .doutb (taps1x) //延迟一个时钟周期,输出RAM1中前前一行图像的数据
- 79 );
- 80
- 81 endmodule
复制代码
line_shift_ram_8bit模块中例化了两个RAM,分别用于存储图像前两行的数据。
在上述代码中,当数据有效信号和时钟使能信号同时为高时,RAM地址开始累加,如代码第26到34行所示。由于RAM地址在per_frame_href信号为低电平时清零;而当新的一行到达时,per_frame_href信号为高电平,RAM地址开始累加,所以RAM的地址等于每行图像像素的横坐标。因此就可以根据RAM地址从而读出当前行像素点对应的前两行的图像,如代码的第62和63行,以及77和78行所示。读出的数据直接传递到模块的输出端口,用于上层模块生成“三行一列”的像素数据。
在该模块中,RAM1(u_ram_1024x8_1)中存储的是第一行(前前一行)的数据,RAM0(u_ram_1024x8_0)中存储的是第二行(前一行)的数据,而输入的图像数据则作为第三行。如下图所示:
图 52.4.5 RAM中存储的两行图像
在读出两个RAM中前两行的图像数据之后,还要将RAM0中的数据写入RAM1,如代码中第73和75行所示;然后将新行图像数据写入RAM0,如代码第58和60行所示,从而不断更新两个RAM中的图像数据。
从RAM中读取数据,以及向RAM1和RAM0中更新数据各需要花费一个时钟周期,因此在代码的第37至39行将输入的clken信号延时了三个时钟周期。并使用延迟之后的clken信号作为两个RAM中的写使能信号,如代码的第58和73行所示。
下面我们将介绍中值算法模块。median_filter_3x3模块的代码如下:
在median_3x3模块实现了简介中所介绍的取中值的快速算法,如下图所示:
图 52.4.6 取中值快速算法
图 52.4.6中所示的算法在模块中的实现共分为三步:
第一步(step1),例化了三次sort3模块,用以对矩阵的每一行数据进行排序,分别求出矩阵每一行的最小值、中值和最大值,如程序第58到95行;
第二步(step2),再例化三次sort3模块,与之前不同的是,此处sort3模块的输入是step1得到的三行数据每一行的三个最小值、三个中值和三个最大值,并输出三个最小值的最大值,三个中值的中间值以及三个最大值的最小值,如代码第98到135行;
第三步(step3),再次例化sort3,并以step2中得到的三个最小值、中值及最大值作为输入,取三个值的中值,如代码第138行到149行。
经过以上三步排序操作,就能得到3x3模板的中值。由于在求得中值过程中,step1、step2和step3一共需要消耗三个时钟周期,因此需要将median_frame_vsync、median_frame_href和median_frame_clken三个信号延迟三个时钟周期以作同步,如代码第152到163行。
在median_filter_3x3模块多次调用了sort3模块,sort3模块是一个针对三个数据进行排序操作的模块,它的代码如下:
- 1 module sort3(
- 2 input clk,
- 3 input rst_n,
- 4 input [7:0] data1,
- 5 input [7:0] data2,
- 6 input [7:0] data3,
- 7
- 8 output reg [7:0] max_data,
- 9 output reg [7:0] mid_data,
- 10 output reg [7:0] min_data
- 11 );
- 12
- 13 //-----------------------------------
- 14 //对三个数据进行排序
- 15 always@(posedge clk or negedge rst_n)begin
- 16 if(!rst_n)begin
- 17 max_data <= 0;
- 18 mid_data <= 0;
- 19 min_data <= 0;
- 20 end
- 21 else begin
- 22 //取最大值
- 23 if(data1 >= data2 && data1 >= data3)
- 24 max_data <= data1;
- 25 else if(data2 >= data1 && data2 >= data3)
- 26 max_data <= data2;
- 27 else//(data3 >= data1 && data3 >= data2)
- 28 max_data <= data3;
- 29 //取中值
- 30 if((data1 >= data2 && data1 <= data3) || (data1 >= data3 && data1 <= data2))
- 31 mid_data <= data1;
- 32 else if((data2 >= data1 && data2 <= data3) || (data2 >= data3 && data2 <= data1))
- 33 mid_data <= data2;
- 34 else//((data3 >= data1 && data3 <= data2) || (data3 >= data2 && data3 <= data1))
- 35 mid_data <= data3;
- 36 //取最小值
- 37 if(data1 <= data2 && data1 <= data3)
- 38 min_data <= data1;
- 39 else if(data2 <= data1 && data2 <= data3)
- 40 min_data <= data2;
- 41 else//(data3 <= data1 && data3 <= data2)
- 42 min_data <= data3;
- 43
- 44 end
- 45 end
- 46
- 47 endmodule
复制代码
我们对模块median_filter_3x3进行中值提取的结果进行了仿真,仿真结果如下图所示:
图 52.4.7 Median_Filter_3X3仿真图
如上图所示,红色标记部分,矩阵的三行数据分别为{100,140,30}、{140,30,70}和{30,70,110}按本文中所介绍的中值滤波算法可以求得中值为70。由于median_filter模块的中值提取操作共消耗了三个时钟周期,所以中值(mid_value)会在三个时钟周期后输出,上图仿真结果图也表明中值是在三个周期后输出。如图中红色圆圈所指示,模块输出的中值也是70,与本次实验计算的相同,这说明中值提取成功。
52.5下载验证
编译完工程之后就可以开始下载程序了。将OV5640摄像头模块插在超越者开发板的“OLED/CAMERA”插座上,并将HDMI电缆一端连接到开发板上的HDMI插座、另一端连接到显示器。将下载器一端连电脑,另一端与开发板上的JTAG端口连接,连接电源线并打开电源开关。接下来我们下载程序,下载完成后HDMI显示器上会显示OV5640摄像头采集到的经FPGA中值滤波处理后的灰度图像。如下图所示,大家可以看到中值滤波处理后的图像与原图几乎没有差别,这是因为现如今的彩色摄像头的采集的图像质量都很高,含有的干扰很少,因此在中值滤波前后图像差别不明显,中值滤波结果可以用仿真来验证。
图 52.5.1 中值滤波后的HDMI实时显示图像
|