zl程序教程

您现在的位置是:首页 >  后端

当前栏目

【正点原子FPGA连载】第五十三章 基于OV5640摄像头的二值化实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

开发 基于 指南 实验 FPGA 摄像头 原子 连载
2023-09-11 14:20:38 时间

第五十三章 基于OV5640摄像头的二值化实验

在数字图像处理中,二值图像占有非常重要的地位,图像的二值化使图像中数据量大为减少,还能凸显出目标的轮廓。图像二值化在计算机视觉、图像分割以及人工智能等方面有着广泛应用。在本章节将进行基于OV5640的二值化实验。
本章包括以下几个部分:
51.1 简介
51.2 实验任务
51.3 硬件设计
51.4 程序设计
51.5 下载验证

53.1 简介
图像二值化( Image Binarization)就是将图像上的像素点的灰度值设置为最大(白色)或最小(黑色),也就是将整个图像呈现出明显的黑白效果的过程。这里以8bit表示的灰度图像为例(灰度值的范围为0~255),二值化就是通过选取适当的阈值,与图像中的256个亮度等级进行比较。亮度高于阈值的像素点设置为白色(255),低于阈值的像素点设置为黑色(0),从而明显地反映出图像的整体和局部特征。
在数字图像处理中,二值图像占有非常重要的地位,特别是在实时的图像处理中,通过二值图像来构成的系统有很多。要进行二值图像的处理与分析,首先要把灰度图像二值化,得到二值化图像,这样有利于在对图像做进一步处理时,图像的集合性质只与像素值为0或255的点的位置有关,不再涉及像素的多级值,使处理变得简单。为了得到理想的二值图像,一般采用封闭、连通的边界定义不交叠的区域。所有灰度大于或等于阈值的像素被判定为属于特定物体,其灰度值用255表示,否则这些像素点被排除在物体区域以外,灰度值为0,表示背景或者例外的物体区域。
实现二值化有两种方法,一种是手动指定一个阈值,通过阈值来进行二值化处理;另一种是一个自适应阈值二值化方法(OTSU算法和Kittle算法等)。使用第一种方法计算量小速度快,但在处理不同图像时颜色分布差别很大;使用第二种方法适用性强,能直接观测出图像的轮廓,但计算相对更复杂。本章节实验将使用第一种方法来实现图像的二值化。
如果某特定物体在内部有均匀一致的灰度值,并且其处在一个具有其他等级灰度值的均匀背景下,使用制定阈值的方法,可以得到比较有效的分割效果。如果物体同背景的差别表现不在灰度值上(比如纹理不同),可以将这个差别特征转换为灰度的差别,然后利用阈值选取技术来分割该图像。
53.2 实验任务
本节实验任务是在新起点开发板上使用OV5640摄像头采集RGB565数据,将数据转化成YCbCr格式,然后进行中值滤波,最后在将灰度值二值化,并通过HDMI显示。
53.3 硬件设计
本章节中硬件设计与“OV5640摄像头HDMI显示实验”完全相同,此处不再赘述。
53.4 程序设计
根据实验任务,首先设计如图 52.4.1所示的系统框图,本章实验的系统框架延续了“OV5640摄像头HDMI灰度显示实验”的整体架构。本次实验包括以下模块:时钟模块、SDRAM控制器模块、IIC驱动模块、IIC配置模块、摄像头采集模块、图像处理模块和HDMI顶层模块。其中时钟模块、SDRAM控制器模块、IIC驱动模块、IIC配置模块、摄像头采集模块和HDMI顶层模块本次实验没有做任何修改,这些模块在“OV5640摄像头HDMI显示实验”中已经说明过,这里不再详述,本次实验只是修改了图像处理模块。
OV5640的二值化实验系统框图如下图所示:

图 52.4.1 顶层系统框图
由上图可知,时钟模块(pll和pll_hdmi)为HDMI顶层模块、SDRAM控制模块以及IIC驱动模块提供驱动时钟。IIC驱动模块和IIC配置模块控制着传感器初始化的开始与结束,传感器初始化完成后将采集到的数据写入摄像头采集模块。数据在摄像头采集模块处理完成后写入图像处理模块,图像处理模块将摄像头数据进行处理后存入SDRAM控制模块。顶层模块从SDRAM控制模块中读出数据并驱动显示器显示,这时整个系统才完成了数据的采集、缓存与显示。需要注意的是图像数据采集模块是在SDRAM和传感器都初始化完成之后才开始输出数据的,避免了在SDRAM初始化过程中向里面写入数据。
顶层模块代码如下所示:

1   module ov5640_hdmi_img_binarization(    
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      ) 
124125 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_img_binarization)例化了以下八个模块:时钟模块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模块框图如下图所示:

图 52.4.2 vip模块框图
vip模块例化了RGB转YCbCr模块(rgb2ycbcr)、中值滤波模块(vip_gray_median_jilter)和二值化模块(binarization)。RGB转YCbCr模块负责将摄像头采集的RGB565格式数据到转换为YUV格式的数据。中值滤波模块负责将YUV格式的视频图像进行中值滤波后输出。二值化模块负责将中值滤波后的视频图像进行二值化处理再输出。有关RGB转YCbCr模块的详细介绍请大家参考“OV5640摄像头HDMI灰度显示实验”章节。有关中值滤波模块的详细介绍请大家参考“基于OV5640的中值滤波实验”章节。
vip模块原理图如下图所示:

图 52.4.3 vip模块原理图
如上图所示,摄像头采集到16位rgb565输入vip模块,经过“rgb2ycbcr”模块转化为8位的yuv444数据,然后在将转化后的灰度数据(img_y)作为“vip_gray_median_filter”模块的输入,对灰度进行中值滤波处理,再将中值滤波后的数据输入进二值化模块,对数据进行二值化处理,最后输出经过二值化处理后的灰度数据“monoc”。
图像处理模块负责图像数据的格式转换,代码如下:

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 wire                  ycbcr_vsync;
29 wire                  ycbcr_hsync;
30 wire                  ycbcr_de;
31 wire                  monoc;
32 
33 //*****************************************************
34 //**                    main code
35 //*****************************************************
36 
37 assign  post_rgb = {16{monoc}};
38 
39 //RGB转YCbCr模块
40 rgb2ycbcr u_rgb2ycbcr(
41     //module clock
42     .clk             (clk    ),            // 时钟信号
43     .rst_n           (rst_n  ),            // 复位信号(低有效)
44     //图像处理前的数据接口
45     .pre_frame_vsync (pre_frame_vsync),    // vsync信号
46     .pre_frame_hsync (pre_frame_hsync),    // href信号
47     .pre_frame_de    (pre_frame_de   ),    // data enable信号
48     .img_red         (pre_rgb[15:11] ),
49     .img_green       (pre_rgb[10:5 ] ),
50     .img_blue        (pre_rgb[ 4:0 ] ),
51     //图像处理后的数据接口
52     .post_frame_vsync(pe_frame_vsync),     // vsync信号
53     .post_frame_hsync(pe_frame_href),      // href信号
54     .post_frame_de   (pe_frame_clken),     // data enable信号
55     .img_y           (img_y),              //灰度数据
56     .img_cb          (),
57     .img_cr          ()
58;
59 
60 //灰度图中值滤波
61 vip_gray_median_filter u_vip_gray_median_filter(
62     .clk    (clk),   
63     .rst_n  (rst_n), 
64     
65     //预处理图像数据
66     .pe_frame_vsync (pe_frame_vsync),      // vsync信号
67     .pe_frame_href  (pe_frame_href),       // href信号
68     .pe_frame_clken (pe_frame_clken),      // data enable信号
69     .pe_img_y       (img_y),               
70                                            
71     //处理后的图像数据                     
72     .pos_frame_vsync (ycbcr_vsync),        // vsync信号
73     .pos_frame_href  (ycbcr_hsync),        // href信号
74     .pos_frame_clken (ycbcr_de),           // data enable信号
75     .pos_img_y       (post_img_y)          //中值滤波后的灰度数据
76;
77 
78 //二值化模块
79 binarization  u_binarization(
80     .clk         (clk),
81     .rst_n       (rst_n),
82     //图像处理前的数据接口     
83     .ycbcr_vsync (ycbcr_vsync),
84     .ycbcr_hsync (ycbcr_hsync),
85     .ycbcr_de    (ycbcr_de),
86     .luminance   (post_img_y),
87     //图像处理后的数据接口     
88     .post_vsync  (post_frame_vsync),
89     .post_hsync  (post_frame_hsync),
90     .post_de     (post_frame_de),
91     .monoc       (monoc)                   //二值化后的数据
92;
93 
94 endmodule

代码的第37行表示对二值化后的1bit灰度数据进行位拼接,形成16bit的RGB565格式的数据输出。
代码的第40行至58行是对灰度转换模块的例化,在该模块以摄像头采集的16位RGB565红、绿、蓝三原色数据作为输入数据,通过算法实现RGB到YCbCr的转换,并输出8位灰度数据,并输出数据输出使能信号。有关RGB转YCbCr模块的详细介绍请大家参考“OV5640摄像头HDMI灰度显示实验”章节。
代码的第61行至76行是对中值滤波模块的例化,该模块负责将YUV格式的视频图像进行中值滤波后输出。有关中值滤波模块的详细介绍请大家参考“基于OV5640的中值滤波实验”章节。
代码的第83行至96行是对二值化模块的例化,该模块主要是根据设定阈值,将图像化分为黑白两种颜色。
中值滤波模块和灰度转换模块在前面的章节已经讲解过,本次实验不在讲述。本次实验重点讲解二值化模块,下面是二值化模块的代码。

1  module binarization(
2      //module clock
3      input               clk             ,   // 时钟信号
4      input               rst_n           ,   // 复位信号(低有效)
5  
6      //图像处理前的数据接口
7      input               ycbcr_vsync     ,   // vsync信号
8      input               ycbcr_hsync     ,   // hsync信号
9      input               ycbcr_de        ,   // data enable信号
10     input   [7:0]       luminance       ,
11 
12     //图像处理后的数据接口
13     output              post_vsync      ,   // vsync信号
14     output              post_hsync      ,   // hsync信号
15     output              post_de         ,   // data enable信号
16     output   reg        monoc               // monochrome(1=白,0=黑)
17;
18 
19 //reg define
20 reg    ycbcr_vsync_d;
21 reg    ycbcr_hsync_d;
22 reg    ycbcr_de_d   ;
23 
24 //*****************************************************
25 //**                    main code
26 //*****************************************************
27 
28 assign  post_vsync = ycbcr_vsync_d  ;
29 assign  post_hsync = ycbcr_hsync_d  ;
30 assign  post_de    = ycbcr_de_d     ;
31 
32 //二值化
33 always @(posedge clk or negedge rst_n) begin
34     if!rst_n)
35         monoc <= 1'b0;
36     else if(luminance > 8'd64)  //阈值
37         monoc <= 1'b1;
38     else
39         monoc <= 1'b0;
40 end
41 
42 //延时1拍以同步时钟信号
43 always@(posedge clk or negedge rst_n) begin
44     if!rst_n) begin
45         ycbcr_vsync_d <= 1'd0;
46         ycbcr_hsync_d <= 1'd0;
47         ycbcr_de_d    <= 1'd0;
48     end
49     else begin
50         ycbcr_vsync_d <= ycbcr_vsync;
51         ycbcr_hsync_d <= ycbcr_hsync;
52         ycbcr_de_d    <= ycbcr_de   ;
53     end
54 end
55 
56 endmodule 

二值化的主要原理就是,给出一个设定的阈值,将灰度值与该阈值比较,若灰度值大于该阈值则monoc为1,若小于阈值,则monoc为0,如代码第33到40行。理论上阈值可以是0到255中的任意值,但阈值过大,会提取多余的部分;而阈值过小,又会丢失所需的部分,因此阈值选取就是一个很重要的步骤。
在代码第43到54行,通过寄存操作对gray_vsync、gray_clken等信号作了一个时钟周期的延迟。这是因为在进行二值判定时消耗了一个时钟,因此相应的同步信号也要延迟一个时钟周期,以实现与数据的同步。
53.5 下载验证
编译完工程之后就可以开始下载程序了。将OV5640摄像头模块插在新起点开发板的“OLED/CAMERA”插座上,并将HDMI电缆一端连接到开发板上的HDMI插座、另一端连接到显示器。将下载器一端连电脑,另一端与开发板上的JTAG端口连接,连接电源线并打开电源开关。接下来我们下载程序,下载完成后观察HDMI显示器显示的二值化后的图案。如下图所示:

图 52.5.1HDMI实时显示二值化图像