1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html 4)对正点原子FPGA感兴趣的同学可以加群讨论:994244016 5)关注正点原子公众号,获取最新资料更新
在数字图像处理中,无论是直接获取的灰度图像,还是由彩色图像转换得到的灰度图像,里面都有噪点的存在,噪点对图像质量有很大的影响。空间滤波是一种常用的降噪方法,进行中值滤波(一种空间滤波)不仅可以去除孤立噪点,而且可以保持图像的边缘特性,不会使图像产生显著的模糊,比较适合于实验中的人脸图像。本章实验将进行OV5640摄像头采集RGB565数据,转化为YUV数据,然后进行中值滤波的实验。 本章包括以下几个部分: 50.1 简介 50.2 实验任务 50.3 硬件设计 50.4 程序设计 50.5 下载验证 51.1 简介 滤波是指接收(通过)或过滤掉信号中一定的频率分量,例如,通过低频率的滤波器称为低通滤波器。空间滤波是图像处理领域应用非常广泛的工具之一,它可以改善图像质量,包括去除高频噪点的干扰、图像平滑等。大家常见的空间滤波有中值滤波和均值滤波。 图像可以看成是一个定义在二维平面上的信号,该信号的幅值对应像素的灰度(彩色图像对应RGB三个分量)。图像的频率指的是空间频率,它和平常认知的物理频率是不同的。图像的频率是表征图像中灰度变化剧烈程度的指标,是灰度在平面空间上的梯度。不同频率信息在图像结构中有不同的作用。图像的主要成分是低频信息,它形成了图像的基本灰度等级,对图像结构的决定作用较小;中频信息决定了图像的基本结构,形成了图像的主要边缘结构;高频信息形成了图像的边缘和细节,是在中频信息上对图像内容的进一步强化。 大家也可以通过空间滤波器(也称为空间掩模、模板或窗口)直接作用于图像本身从而对图像进行滤波处理。空间滤波器由两部分组成:(1)邻域,(2)对该邻域包围的图像像素执行的预定义操作。领域是指一个像素点及其附近像素点所组成的空间。滤波会产生一个新像素,像素的坐标就是邻域中心的坐标,像素的值就是滤波操作的结果。 中值滤波就是一种很常见的空间滤波,它是一种非线性平滑技术。它将每一像素点及该像素点的邻域作为一个滤波模板,计算出模板中所有像素点灰度值的中值,然后用它代替模板中心像素点的值。图 51.1.1为像素点P及其周围8个像素点所组成的3x3滤波模板: 图 51.1.1 中值滤波模板 中值滤波是一种基于排序统计理论的非线性信号处理技术,它可以消除孤立的噪点,从而让图像中的像素值更接近真实值。红外图像中的盲元就是一种孤立噪点的例子。由于红外探测器制造过程中的缺陷,传感器中某些像元的输出可能会非常大,导致图像中对应的像素点非常亮,通常称之为盲元,如下图中红色箭头所示:
图 51.1.2 红外图像中的盲元 中值滤波对类似于上图中的脉冲噪点有良好的滤除作用,特别是在滤除噪点的同时,能够保护信号的边缘,使之不被模糊。这些优良特性是线性滤波方法所不具备的。此外,中值滤波的算法比较简单,也易于用硬件实现。所以,中值滤波方法一经提出后,便在数字信号处理领域得到广泛的应用。 关于中值滤波如何快速求得中值,有多种方法实现,例如冒泡排序法、选择排序法等方法。但是用Verilog实现这些排序算法不仅很复杂而且运算速率同时也会大大降低。在本章实验中采用流水线操作的方式,在图像的3x3矩阵中实现快速排序。下面是算法流程框图:
图 51.1.3 中值滤波算法框图 首先生成一个3x3的像素阵列,然后在分别对每行3个像素进行排序,得出每行的最大、 中值和最小值(如上图Max1、Med1和Min1)。接着,对排序后的矩阵进行处理,即提取三个最大值中的最小值(Minz_of_Max),三个中间值的中间值(Med_of_Med),以及三个最小值中的最大值(Max_of_Min)。最后,将得到的三个值,再次取中值,最终求得9个像素的中值。 51.2 实验任务 本次实验任务是利用OV5640摄像头采集RGB565数据,将采集的数据转换为YUV数据,然后对YUV数据进行中值滤波处理,最后在HDMI显示器上显示。 51.3 硬件设计 本章节中硬件设计与“OV5640摄像头HDMI显示实验”完全相同,此处不再赘述。 51.4 程序设计 根据实验任务,首先设计如图 51.4.1所示的系统框图,本章实验的系统框架延续了“OV5640摄像头HDMI灰度显示实验”的整体架构。本次实验包括以下模块:时钟模块、SDRAM控制器模块、IIC驱动模块、IIC配置模块、摄像头采集模块、图像处理模块和HDMI顶层模块。其中时钟模块、SDRAM控制器模块、IIC驱动模块、IIC配置模块、摄像头采集模块和HDMI顶层模块本次实验没有做任何修改,这些模块在“OV5640摄像头HDMI显示实验”中已经说明过,这里不再详述,本次实验只是修改了图像处理模块。 OV5640的中值滤波实验系统框图如下图所示:
图 51.4.1 顶层系统框图 由上图可知,时钟模块(pll和pll_hdmi)为HDMI顶层模块、SDRAM控制模块以及IIC驱动模块提供驱动时钟。IIC驱动模块和IIC配置模块控制着传感器初始化的开始与结束,传感器初始化完成后将采集到的数据写入摄像头采集模块。数据在摄像头采集模块处理完成后写入图像处理模块,图像处理模块将摄像头数据进行处理后存入SDRAM控制模块。顶层模块从SDRAM控制模块中读出数据并驱动显示器显示,这时整个系统才完成了数据的采集、缓存与显示。需要注意的是图像数据采集模块是在SDRAM和传感器都初始化完成之后才开始输出数据的,避免了在SDRAM初始化过程中向里面写入数据。 顶层模块代码如下所示:
1 module ov5640_hdmi_median_filter(
2 input sys_clk , //系统时钟
3 input sys_rst_n , //系统复位,低电平有效
4 //摄像头
5 input cam_pclk , //cmos 数据像素时钟
6 input cam_vsync , //cmos 场同步信号
7 input cam_href , //cmos 行同步信号
8 input [7:0] cam_data , //cmos 数据
9 output cam_rst_n , //cmos 复位信号,低电平有效
10 output cam_pwdn , //cmos 电源休眠模式选择信号
11 output cam_scl , //cmos SCCB_SCL线
12 inout cam_sda , //cmos SCCB_SDA线
13 //SDRAM
14 output sdram_clk , //SDRAM 时钟
15 output sdram_cke , //SDRAM 时钟有效
16 output sdram_cs_n , //SDRAM 片选
17 output sdram_ras_n, //SDRAM 行有效
18 output sdram_cas_n, //SDRAM 列有效
19 output sdram_we_n , //SDRAM 写有效
20 output [1:0] sdram_ba , //SDRAM Bank地址
21 output [1:0] sdram_dqm , //SDRAM 数据掩码
22 output [12:0] sdram_addr , //SDRAM 地址
23 inout [15:0] sdram_data , //SDRAM 数据
24 //HDMI接口
25 output tmds_clk_p, // TMDS 时钟通道
26 output tmds_clk_n,
27 output [2:0] tmds_data_p, // TMDS 数据通道
28 output [2:0] tmds_data_n
29 );
30
31 //parameter define
32 parameter SLAVE_ADDR = 7'h3c ; //OV5640的器件地址7'h3c
33 parameter BIT_CTRL = 1'b1 ; //OV5640的字节地址为16位 0:8位 1:16位
34 parameter CLK_FREQ = 27'd50_000_000 ; //i2c_dri模块的驱动时钟频率
35 parameter I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率,不超过400KHz
36 parameter V_CMOS_DISP = 11'd800 ; //CMOS分辨率--行
37 parameter H_CMOS_DISP = 11'd1280 ; //CMOS分辨率--列
38 parameter TOTAL_H_PIXEL = 13'd2570 ; //CMOS分辨率--行
39 parameter TOTAL_V_PIXEL = 13'd980 ;
40
41 //wire define
42 wire clk_100m ; //100mhz时钟,SDRAM操作时钟
43 wire clk_100m_shift ; //100mhz时钟,SDRAM相位偏移时钟
44 wire clk_50m ;
45 wire hdmi_clk ;
46 wire hdmi_clk_5 ;
47 wire locked ;
48 wire locked_hdmi ;
49 wire rst_n ;
50 wire sys_init_done ; //系统初始化完成(sdram初始化+摄像头初始化)
51 wire i2c_exec ; //I2C触发执行信号
52 wire [23:0] i2c_data ; //I2C要配置的地址与数据(高8位地址,低8位数据)
53 wire i2c_done ; //I2C寄存器配置完成信号
54 wire i2c_dri_clk ; //I2C操作时钟
55 wire [ 7:0] i2c_data_r ; //I2C读出的数据
56 wire i2c_rh_wl ; //I2C读写控制信号
57 wire cam_init_done ; //摄像头初始化完成
58 wire wr_en ; //sdram_ctrl模块写使能
59 wire [15:0] wr_data ; //sdram_ctrl模块写数据
60 wire rd_en ; //sdram_ctrl模块读使能
61 wire [15:0] rd_data ; //sdram_ctrl模块读数据
62 wire sdram_init_done ; //SDRAM初始化完成
63 wire [10:0] pixel_xpos_w ; //HDMI横坐标
64 wire [10:0] pixel_ypos_w ; //HDMI纵坐标
65 wire post_frame_vsync ; //处理后的场信号
66 wire post_frame_hsync ; //处理后的行信号
67 wire post_frame_de ; //处理后的数据使能
68 wire [15:0] post_rgb ; //处理后的数据
69
70 //*****************************************************
71 //** main code
72 //*****************************************************
73
74 assign rst_n = sys_rst_n & locked & locked_hdmi;
75 //系统初始化完成:SDRAM和摄像头都初始化完成
76 //避免了在SDRAM初始化过程中向里面写入数据
77 assign sys_init_done = sdram_init_done & cam_init_done;
78 //电源休眠模式选择 0:正常模式 1:电源休眠模式
79 assign cam_pwdn = 1'b0;
80 assign cam_rst_n = 1'b1;
81
82 //锁相环
83 pll u_pll(
84 .areset (~sys_rst_n),
85 .inclk0 (sys_clk),
86 .c0 (clk_100m),
87 .c1 (clk_100m_shift),
88 .c2 (clk_50m),
89 .locked (locked)
90 );
91
92 pll_hdmi pll_hdmi_inst (
93 .areset ( ~sys_rst_n ),
94 .inclk0 ( sys_clk ),
95 .c0 ( hdmi_clk ),//hdmi pixel clock 71Mhz
96 .c1 ( hdmi_clk_5 ),//hdmi pixel clock*5 355Mhz
97 .locked ( locked_hdmi )
98 );
99
100 //I2C配置模块
101 i2c_ov5640_rgb565_cfg u_i2c_cfg(
102 .clk (i2c_dri_clk),
103 .rst_n (rst_n ),
104
105 .i2c_exec (i2c_exec ),
106 .i2c_data (i2c_data ),
107 .i2c_rh_wl (i2c_rh_wl ), //I2C读写控制信号
108 .i2c_done (i2c_done ),
109 .i2c_data_r (i2c_data_r ),
110
111 .cmos_h_pixel (H_CMOS_DISP ), //CMOS水平方向像素个数
112 .cmos_v_pixel (V_CMOS_DISP ) , //CMOS垂直方向像素个数
113 .total_h_pixel (TOTAL_H_PIXEL), //水平总像素大小
114 .total_v_pixel (TOTAL_V_PIXEL), //垂直总像素大小
115
116 .init_done (cam_init_done)
117 );
118
119 //I2C驱动模块
120 i2c_dri #(
121 .SLAVE_ADDR (SLAVE_ADDR ), //参数传递
122 .CLK_FREQ (CLK_FREQ ),
123 .I2C_FREQ (I2C_FREQ )
124 )
125 u_i2c_dr(
126 .clk (clk_50m ),
127 .rst_n (rst_n ),
128
129 .i2c_exec (i2c_exec ),
130 .bit_ctrl (BIT_CTRL ),
131 .i2c_rh_wl (i2c_rh_wl ), //固定为0,只用到了IIC驱动的写操作
132 .i2c_addr (i2c_data[23:8]),
133 .i2c_data_w (i2c_data[7:0] ),
134 .i2c_data_r (i2c_data_r ),
135 .i2c_done (i2c_done ),
136 .scl (cam_scl ),
137 .sda (cam_sda ),
138 .dri_clk (i2c_dri_clk ) //I2C操作时钟
139 );
140
141 //CMOS图像数据采集模块
142 cmos_capture_data u_cmos_capture_data( //系统初始化完成之后再开始采集数据
143 .rst_n (rst_n & sys_init_done),
144
145 .cam_pclk (cam_pclk ),
146 .cam_vsync (cam_vsync),
147 .cam_href (cam_href ),
148 .cam_data (cam_data ),
149
150 .cmos_frame_vsync (cmos_frame_vsync),
151 .cmos_frame_href (cmos_frame_href),
152 .cmos_frame_valid (wr_en ), //数据有效使能信号
153 .cmos_frame_data (wr_data ) //有效数据
154 );
155
156 //图像处理模块
157 vip u_vip(
158 //module clock
159 .clk (cam_pclk), // 时钟信号
160 .rst_n (rst_n ), // 复位信号(低有效)
161 //图像处理前的数据接口
162 .pre_frame_vsync (cmos_frame_vsync ),
163 .pre_frame_hsync (cmos_frame_href ),
164 .pre_frame_de (wr_en ),
165 .pre_rgb (wr_data),
166 .xpos (pixel_xpos_w ),
167 .ypos (pixel_ypos_w ),
168 //图像处理后的数据接口
169 .post_frame_vsync (post_frame_vsync ), // 场同步信号
170 .post_frame_hsync ( ), // 行同步信号
171 .post_frame_de (post_frame_de ), // 数据输入使能
172 .post_rgb (post_rgb) // RGB565颜色数据
173
174 );
175
176 //SDRAM 控制器顶层模块,封装成FIFO接口
177 //SDRAM 控制器地址组成: {bank_addr[1:0],row_addr[12:0],col_addr[8:0]}
178 sdram_top u_sdram_top(
179 .ref_clk (clk_100m), //sdram 控制器参考时钟
180 .out_clk (clk_100m_shift), //用于输出的相位偏移时钟
181 .rst_n (rst_n), //系统复位
182
183 //用户写端口
184 .wr_clk (cam_pclk), //写端口FIFO: 写时钟
185 .wr_en (post_frame_de), //写端口FIFO: 写使能
186 .wr_data (post_rgb), //写端口FIFO: 写数据
187
188 .wr_min_addr (24'd0), //写SDRAM的起始地址
189 .wr_max_addr (V_CMOS_DISP*H_CMOS_DISP-1), //写SDRAM的结束地址
190 .wr_len (10'd512), //写SDRAM时的数据突发长度
191 .wr_load (~rst_n), //写端口复位: 复位写地址,清空写FIFO
192
193 //用户读端口
194 .rd_clk (hdmi_clk), //读端口FIFO: 读时钟
195 .rd_en (rd_en), //读端口FIFO: 读使能
196 .rd_data (rd_data), //读端口FIFO: 读数据
197 .rd_min_addr (24'd0), //读SDRAM的起始地址
198 .rd_max_addr (V_CMOS_DISP*H_CMOS_DISP-1), //读SDRAM的结束地址
199 .rd_len (10'd512), //从SDRAM中读数据时的突发长度
200 .rd_load (~rst_n), //读端口复位: 复位读地址,清空读FIFO
201
202 //用户控制端口
203 .sdram_read_valid (1'b1), //SDRAM 读使能
204 .sdram_pingpang_en (1'b1), //SDRAM 乒乓操作使能
205 .sdram_init_done (sdram_init_done), //SDRAM 初始化完成标志
206
207 //SDRAM 芯片接口
208 .sdram_clk (sdram_clk), //SDRAM 芯片时钟
209 .sdram_cke (sdram_cke), //SDRAM 时钟有效
210 .sdram_cs_n (sdram_cs_n), //SDRAM 片选
211 .sdram_ras_n (sdram_ras_n), //SDRAM 行有效
212 .sdram_cas_n (sdram_cas_n), //SDRAM 列有效
213 .sdram_we_n (sdram_we_n), //SDRAM 写有效
214 .sdram_ba (sdram_ba), //SDRAM Bank地址
215 .sdram_addr (sdram_addr), //SDRAM 行/列地址
216 .sdram_data (sdram_data), //SDRAM 数据
217 .sdram_dqm (sdram_dqm) //SDRAM 数据掩码
218 );
219
220 //例化HDMI顶层模块
221 hdmi_top u_hdmi_top(
222 .hdmi_clk (hdmi_clk ),
223 .hdmi_clk_5 (hdmi_clk_5 ),
224 .rst_n (rst_n ),
225
226 .rd_data (rd_data ),
227 .rd_en (rd_en ),
228 .h_disp (),
229 .v_disp (),
230 .pixel_xpos (pixel_xpos_w),
231 .pixel_ypos (pixel_ypos_w),
232 .video_vs (),
233 .tmds_clk_p (tmds_clk_p ),
234 .tmds_clk_n (tmds_clk_n ),
235 .tmds_data_p (tmds_data_p),
236 .tmds_data_n (tmds_data_n)
237 );
238
239 endmodule
FPGA顶层模块(ov5640_hdmi_median_filter)例化了以下八个模块:时钟模块1(pll)、时钟模块2(pll_hdmi)、I2C驱动模块(i2c_dri)、I2C配置模块(i2c_ov5640_rgb565_cfg)、图像采集模块(cmos_capture_data)、图像处理模块(vip)、SDRAM控制模块(sdram_top)和HDMI顶层模块(hdmi_top)。 时钟模块:时钟模块通过调用PLL IP核实现,共输出5个时钟,频率分别为100M时钟、100M偏移-75度时钟、50M时钟、71Mhz时钟和355M时钟(HDMI像素时钟的5倍频)。其中pll 产生了50M时钟、100M时钟和100M偏移-75度时钟,pll_hdmi 产生了71Mhz时钟和355M时钟,这里之所以用两个锁相环是因为HDMI所用的时钟71Mhz与SDRAM控制模块使用的100M时钟不是整数倍,使用一个锁相环不符合设计要求。100Mhz时钟作为SDRAM控制模块的驱动时钟,100M偏移-75度时钟用来输出给外部SDRAM芯片使用,50Mhz时钟作为I2C驱动模块的驱动时钟,71Mhz时钟和355M时钟(HDMI像素时钟的5倍频)负责驱动HDMI顶层模块。 I2C驱动模块(i2c_dri):I2C驱动模块负责驱动OV5640 SCCB接口总线,用户可根据该模块提供的用户接口可以很方便的对OV5640的寄存器进行配置,该模块和“EEPROM读写实验”章节中用到的I2C驱动模块为同一个模块,有关该模块的详细介绍请大家参考“EEPROM读写实验”章节。 I2C配置模块(i2c_ov5640_rgb565_cfg):I2C配置模块的驱动时钟是由I2C驱动模块输出的时钟提供的,这样方便了I2C驱动模块和I2C配置模块之间的数据交互。该模块寄存需要配置的寄存器地址、数据以及控制初始化的开始与结束,同时该模块输出OV5640的寄存器地址和数据以及控制I2C驱动模块开始执行的控制信号,直接连接到I2C驱动模块的用户接口,从而完成对OV5640传感器的初始化。 图像采集模块(cmos_capture_data):摄像头采集模块在像素时钟的驱动下将传感器输出的场同步信号、行同步信号以及8位数据转换成写使能信号和16位写数据信号,完成对OV5640传感器图像的采集。OV5640和OV7725图像输出时序非常相似,有关该模块的详细介绍请大家参考“OV7725摄像头LCD显示实验”章节。 图像处理模块(vip):对采集后的图像数据进行处理,并将处理后的数据存入SDRAM控制模块。 SDRAM控制模块(sdram_top):SDRAM读写控制器模块负责驱动SDRAM片外存储器,缓存图像传感器输出的图像数据。有关该模块的详细介绍请大家参考“SDRAM读写测试实验”章节。 HDMI顶层模块(hdmi_top):HDMI顶层模块负责驱动HDMI显示器的驱动信号的输出,同时为其他模块提供显示器参数、场同步信号和数据请求信号。关HDMI顶层模块的详细介绍请大家参考“OV5640摄像头HDMI显示实验”章节。 vip模块框图如下图所示:
图 51.4.2 vip模块框图 vip模块例化了RGB转YCbCr模块(rgb2ycbcr)和中值滤波模块(vip_gray_median_jilter)。RGB转YCbCr模块负责将摄像头采集的RGB565格式数据到转换为YUV格式的数据。中值滤波模块负责将YUV格式的视频图像进行中值滤波后输出。有关RGB转YCbCr模块的详细介绍请大家参考“OV5640摄像头HDMI灰度显示实验”章节。 vip模块原理图如下图所示:
图 51.4.3 vip模块原理图 如上图所示,摄像头采集到16位rgb565输入vip模块,经过“rgb2ycbcr”模块转化为8位的yuv444数据,然后在将转化后的灰度数据(img_y)作为“vip_gray_median_jilter”模块的输入,对灰度进行中值滤波处理,最后输出处理后的灰度数据“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 pe_frame_vsync;
26 wire pe_frame_href;
27 wire pe_frame_clken;
28
29 //*****************************************************
30 //** main code
31 //*****************************************************
32
33 assign post_rgb = {post_img_y[7:3],post_img_y[7:2],post_img_y[7:3]};
34
35 //RGB转YCbCr模块
36 rgb2ycbcr u_rgb2ycbcr(
37 //module clock
38 .clk (clk ), // 时钟信号
39 .rst_n (rst_n ), // 复位信号(低有效)
40 //图像处理前的数据接口
41 .pre_frame_vsync (pre_frame_vsync), // vsync信号
42 .pre_frame_hsync (pre_frame_hsync), // href信号
43 .pre_frame_de (pre_frame_de ), // data enable信号
44 .img_red (pre_rgb[15:11] ),
45 .img_green (pre_rgb[10:5 ] ),
46 .img_blue (pre_rgb[ 4:0 ] ),
47 //图像处理后的数据接口
48 .post_frame_vsync(pe_frame_vsync), // vsync信号
49 .post_frame_hsync(pe_frame_href), // href信号
50 .post_frame_de (pe_frame_clken), // data enable信号
51 .img_y (img_y), //灰度数据
52 .img_cb (),
53 .img_cr ()
54 );
55
56 //灰度图中值滤波
57 vip_gray_median_filter u_vip_gray_median_filter(
58 .clk (clk),
59 .rst_n (rst_n),
60
61 //处理前图像数据
62 .pe_frame_vsync (pe_frame_vsync), // vsync信号
63 .pe_frame_href (pe_frame_href), // href信号
64 .pe_frame_clken (pe_frame_clken), // data enable信号
65 .pe_img_y (img_y),
66
67 //处理后的图像数据
68 .pos_frame_vsync (post_frame_vsync), // vsync信号
69 .pos_frame_href (post_frame_hsync), // href信号
70 .pos_frame_clken (post_frame_de), // data enable信号
71 .pos_img_y (post_img_y) //中值滤波后的灰度数据
72 );
73
74 endmodule
代码的第33行表示对滤波后的8bit灰度数据进行位拼接,形成16bit的RGB565格式的数据输出。 代码的第36行至54行是对灰度转换模块的例化,在该模块以摄像头采集的16位RGB565红、绿、蓝三原色数据作为输入数据,通过算法实现RGB到YCbCr的转换,并输出8位灰度数据,并输出数据输出使能信号。有关RGB转YCbCr模块的详细介绍请大家参考“OV5640摄像头HDMI灰度显示实验”章节。 代码的第57行至72行是对中值滤波模块的例化,该模块负责将YUV格式的视频图像进行中值滤波后输出。 中值滤波模块的层次结构如下图:
图 51.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模块代码如下:
1 module vip_matrix_generate_3x3_8bit
2 (
3 input clk,
4 input rst_n,
5
6 input per_frame_vsync,
7 input per_frame_href,
8 input per_frame_clken,
9 input [7:0] per_img_y,
10
11 output matrix_frame_vsync,
12 output matrix_frame_href,
13 output matrix_frame_clken,
14 output reg [7:0] matrix_p11,
15 output reg [7:0] matrix_p12,
16 output reg [7:0] matrix_p13,
17 output reg [7:0] matrix_p21,
18 output reg [7:0] matrix_p22,
19 output reg [7:0] matrix_p23,
20 output reg [7:0] matrix_p31,
21 output reg [7:0] matrix_p32,
22 output reg [7:0] matrix_p33
23 );
24
25 //wire define
26 wire [7:0] row1_data;
27 wire [7:0] row2_data;
28 wire read_frame_href;
29 wire read_frame_clken;
30
31 //reg define
32 reg [7:0] row3_data;
33 reg [1:0] per_frame_vsync_r;
34 reg [1:0] per_frame_href_r;
35 reg [1:0] per_frame_clken_r;
36
37 //*****************************************************
38 //** main code
39 //*****************************************************
40
41 assign read_frame_href = per_frame_href_r[0] ;
42 assign read_frame_clken = per_frame_clken_r[0];
43 assign matrix_frame_vsync = per_frame_vsync_r[1];
44 assign matrix_frame_href = per_frame_href_r[1] ;
45 assign matrix_frame_clken = per_frame_clken_r[1];
46
47 //当前数据放在第3行
48 always@(posedge clk or negedge rst_n) begin
49 if(!rst_n)
50 row3_data
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?