正点原子 发表于 2020-8-13 10:58:53

【正点原子FPGA连载】第二十五章OV5640摄像头灰度图显示实验--摘自【正点原子】领航者 ZYNQ 之嵌入式开发指南

本帖最后由 正点原子 于 2020-10-24 10:48 编辑

1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:876744900
5)关注正点原子公众号,获取最新资料





第二十五章OV5640摄像头灰度图显示实验




前面的实验我们介绍了OV5640摄像头LCD显示实验,在该实验中摄像头输出的是RGB格式的图像数据。而在数字图像处理领域中YUV是一种更常用的图像格式,其特点是将亮度和色度进行分离,更适合运用于图像处理领域。在本章实验中,我们将进行RGB到YUV的颜色空间转换,并通过LCD显示灰度图的实验。
本章包括以下几个部分:
12525.1OV5640简介
25.2实验任务
25.3软件设计
25.4程序设计
25.5下载验证



25.1简介
人眼中的锥状细胞是负责彩色视觉的传感器,可分为三个主要的感知类别,分别对应红色、绿色、蓝色,我们人眼看到的彩色实际上是红、绿、蓝三原色的各种组合。前面我们用到的RGB就是以红、绿、蓝为三原色的颜色空间模型,通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。
YUV(YCbCr)是欧洲电视系统所采用的一种颜色编码方法。“Y”表示明亮度(Luminance或Luma),也就是灰阶值,“U”和“V”表示色度,用于描述影像的饱和度和色调。RGB与YUV的转换实际上是色彩空间的转换,即将RGB的三原色色彩空间转换为YUV所表示的亮度与色度的色彩空间模型。YUV 主要应用在模拟系统中,而 YCbCr 是通过 YUV 信号的发展,并通过校正的主要应用在数字视频中的一种编码方法。YUV适用于PAL和SECAM彩色电视制式,而YCrCb适用于计算机用的显示器。
RGB着重于人眼对色彩的感应,YUV则着重于视觉对于亮度的敏感程度。使用YUV描述图像的好处在于,(1)亮度(Y)与色度(U、V)是独立的;(2)人眼能够识别数千种不同的色彩,但只能识别20多种灰阶值,采用YUV标准可以降低数字彩色图像所需的储存容量。因而YUV在数字图像处理中是一种很常用的颜色标准。
YUV 信号的提出,是因为国际上出现彩色电视,为了兼容黑白电视的信号而设计的,在视频码率,压缩,兼容性等方面有很大优势,我们最常用的主要是YUV4:4:4和V4:2:2两中采样格式的YUV信号。下面我们将介绍这两种格式的信号。
(1)YUV4:4:4
在YUV4:4:4格式中,YUV三个信道的采样率相同。因此在生成的图像里,每个像素都有各自独立的三个分量,每个分量通常为8bit,故每个像素占用3个字节。下图为YUV444单个像素的模型图,可以看出,每个Y都对应一组U、V数据,共同组成一个像素。

图 25.1.1 单个V444的素
(2)YUV4:2:2
在YUV4:2:2格式中,U和V的采样率是Y的一半(两个相邻的像素共用一对U、V数据)。如下图所示,图中包含两个相邻的像素。第一个像素的三个YUV分量分别是Y1、U1、V1,第二个像素的三个YUV分量分别是Y2、U1、V1,两个像素共用一组U1、V1。

图 25.1.2 两个相邻的YUV422像素
YUV4:4:4格式和YUV4:2:2格式的数据流也是不同的。如一组连续的四个像素P1、P2、P3、P4,采用YUV444的采样格式时,数据流为Y0 U0 V0、Y1 U1 V1、Y2 U2 V2、Y3 U3 V3,每组数据代表一个像素点。而用YUV422的采样格式时,数据流为Y0 U0 Y1 V1、Y2 U2 Y3 V3。其中,Y0 U0 Y1 V1表示P1、P2两个像素,Y2 U2 Y3 V3表示P3、P4两个像素。
一般意义上 YCbCr 即为 YUV 信号,没有严格的划分。我们通常说的YUV就是指YCbCr。 CbCr 分别为蓝色色度分量、红色色度分量。下面为 RGB 与 YCbCr 色彩空间转换的算法公式, RGB 转 YCbCr 的公式如下所示:

图 25.1.3 RGB 转 YcbCr算法
由于 Verilog HDL 无法进行浮点运算,因此使用扩大 256 倍,再向右移 8Bit的方式,来转换公式,(0.083=00010101) 如下所示:

图 25.1.4 RGB 转 YcbCr算法

为了防止运算过程中出现负数,我们对上述公式进行进一步变换,得到如下公式:

图 25.1.5 RGB 转 YcbCr算法
实际上OV5640本身支持输出RGB、YUV格式的数据,本章节实验是着重于实RGB转YUV的HDL算法实现,因此我们把摄像头设置为RGB565格式。当我们需要显示器显示灰度图时,我们只需要将转换后的Y值作为R、G、B三原色通道的输入就可以实现了。
25.2实验任务
本节实验任务是使用启明星开发板及OV5640摄像头采集RGB565格式的数据,并通过算法转换,将RGB格式的图像数据转换为YCbCr格式,然后通过LCD实时显示灰度图。
25.3硬件设计
图 25.3.1是根据本章实验任务画出的系统框图。对比OV5640摄像头LCD显示实验,本实验中仅添加了RGB转Ycbcr模块,该模块用于将摄像头采集到的RGB数据转换成YCbCr格式数。
OV5640摄像头LCD显示灰度图系统框图如下图所示:

图 25.3.1 OV5640摄像头LCD显示灰度图系统框图
如下图 25.3.2我们给出了rgb2ycbcr的ip核以及其接口的连线图:

图 25.3.2 rgb2ycbcr模块连接图
有关其他各个模块的功能可以参考“OV5640摄像头LCD显示实验”的相关描述,在本实验中我们将对rgb2ycbcr模块的功能进行描述。摄像头采集模块输出24为RGB数据经“rgb2ycbcr”模块转换,输出24位的Ycbcr数据。
RGB转Ycbcr模块的代码如下:
1   module rgb2ycbcr
2   (
3       //module clock
4       input               clk          ,// 模块驱动时钟
5       input               rst_n      ,// 复位信号
6   
7       //图像处理前的数据接口
8       input               rgb_vsync    ,// vsync信号
9       input               rgb_clken    ,// 时钟使能信号
10      input               rgb_valid    ,// 数据有效信号
11      input       rgb_data   ,// 输入图像数据RGB
12   
13
14      //图像处理后的数据接口
15      output            ycbcb_vsync,// vsync信号
16      output            ycbcbr_clken ,// 时钟使能信号
17      output            ycbcr_valid,// 数据有效信号
18      output      gray_data       // 输出图像Y数据
19   
20);
21
22//reg define
23reg   rgb_r_m0, rgb_r_m1, rgb_r_m2;
24reg   rgb_g_m0, rgb_g_m1, rgb_g_m2;
25reg   rgb_b_m0, rgb_b_m1, rgb_b_m2;
26reg   img_y0 ;
27reg   img_cb0;
28reg   img_cr0;
29reg[ 7:0]   img_y1 ;
30reg[ 7:0]   img_cb1;
31reg[ 7:0]   img_cr1;
32reg[ 2:0]   rgb_vsync_d;
33reg[ 2:0]   rgb_clken_d;
34reg[ 2:0]   rgb_valid_d   ;
35
36//wire define
37wire [ 7:0]   rgb888_r;
38wire [ 7:0]   rgb888_g;
39wire [ 7:0]   rgb888_b;
40wire [ 7:0]   img_y;
41wire [ 7:0]   img_cb;
42wire [ 7:0]   img_cr;
43
44//*****************************************************
45//**                  main code
46//*****************************************************
47
48//RGB565 to RGB 888
49assign rgb888_r         = rgb_data;
50assign rgb888_g   = rgb_data;
51assign rgb888_b   = rgb_data ;
52//同步输出数据接口信号
53assign ycbcb_vsync= rgb_vsync_d ;
54assign ycbcbr_clken = rgb_clken_d ;
55assign ycbcr_valid= rgb_valid_d ;
56assign img_y      = ycbcbr_clken ? img_y1 : 8'd0;
57assign img_cb       = ycbcbr_clken ? img_cb1: 8'd0;
58assign img_cr       = ycbcbr_clken ? img_cr1: 8'd0;
59assign gray_data    = {img_y,img_y,img_y};
60//--------------------------------------------
61//RGB 888 to YCbCr
62
63/********************************************************
64            RGB888 to YCbCr
65   Y= 0.299R +0.587G + 0.114B
66   Cb = 0.568(B-Y) + 128 = -0.172R-0.339G + 0.511B + 128
67   CR = 0.713(R-Y) + 128 = 0.511R-0.428G -0.083B + 128
68
69   Y= (77 *R    +    150*G    +    29 *B)>>8
70   Cb = (-43*R    -    85 *G    +    128*B)>>8 + 128
71   Cr = (128*R    -    107*G    -    21 *B)>>8 + 128
72
73   Y= (77 *R    +    150*G    +    29 *B      )>>8
74   Cb = (-43*R    -    85 *G    +    128*B + 32768)>>8
75   Cr = (128*R    -    107*G    -    21 *B + 32768)>>8
76*********************************************************/
77
78//step1 计算括号内的各乘法项
79always @(posedge clk or negedge rst_n) begin
80      if(!rst_n) begin
81          rgb_r_m0 <= 16'd0;
82          rgb_r_m1 <= 16'd0;
83          rgb_r_m2 <= 16'd0;
84          rgb_g_m0 <= 16'd0;
85          rgb_g_m1 <= 16'd0;
86          rgb_g_m2 <= 16'd0;
87          rgb_b_m0 <= 16'd0;
88          rgb_b_m1 <= 16'd0;
89          rgb_b_m2 <= 16'd0;
90      end
91      else begin
92          rgb_r_m0 <= rgb888_r * 8'd77 ;
93          rgb_r_m1 <= rgb888_r * 8'd43 ;
94          rgb_r_m2 <= rgb888_r * 8'd128;
95          rgb_g_m0 <= rgb888_g * 8'd150;
96          rgb_g_m1 <= rgb888_g * 8'd85 ;
97          rgb_g_m2 <= rgb888_g * 8'd107;
98          rgb_b_m0 <= rgb888_b * 8'd29 ;
99          rgb_b_m1 <= rgb888_b * 8'd128;
100         rgb_b_m2 <= rgb888_b * 8'd21 ;
101   end
102 end
103
104 //step2 括号内各项相加
105 always @(posedge clk or negedge rst_n) begin
106   if(!rst_n) begin
107         img_y0<= 16'd0;
108         img_cb0 <= 16'd0;
109         img_cr0 <= 16'd0;
110   end
111   else begin
112         img_y0<= rgb_r_m0 + rgb_g_m0 + rgb_b_m0;
113         img_cb0 <= rgb_b_m1 - rgb_r_m1 - rgb_g_m1 + 16'd32768;
114         img_cr0 <= rgb_r_m2 - rgb_g_m2 - rgb_b_m2 + 16'd32768;
115   end
116
117 end
118
119 //step3 括号内计算的数据右移8位
120 always @(posedge clk or negedge rst_n) begin
121   if(!rst_n) begin
122         img_y1<= 8'd0;
123         img_cb1 <= 8'd0;
124         img_cr1 <= 8'd0;
125   end
126   else begin
127         img_y1<= img_y0 ;
128         img_cb1 <= img_cb0;
129         img_cr1 <= img_cr0;
130   end
131 end
132
133 //延时3拍以同步数据信号
134 always@(posedge clk or negedge rst_n) begin
135   if(!rst_n) begin
136         rgb_vsync_d <= 3'd0;
137         rgb_clken_d <= 3'd0;
138         rgb_valid_d <= 3'd0;
139   end
140   else begin
141         rgb_vsync_d <= {rgb_vsync_d, rgb_vsync};
142         rgb_clken_d <= {rgb_clken_d, rgb_clken};
143         rgb_valid_d <= {rgb_valid_d, rgb_valid};
144   end
145 end
146
147 endmodule
在RGB转成Ycbcr格式的算法换算过程中,我们需要提取RGB信号的红绿蓝分量,我们只需取24位RGB数据的高8位、中8位和低8位我作为红绿蓝三原色分量就可以了,如代码第48之51行所示;下面就是进行RGB转YCbCr算法的HDL实现:第一步,先计算出算法公式中括号里每一个乘法的乘积,如代码第78至102行所示;第二步,计算出Y、Cb、Cr括号内的值,代码104至117行;第三步,右移 8Bit,由于 Step2 计算结果为 16Bit, 因此直接提取高8位 即可,代码如119至131行所示。下图为RGB转YUV的算法公式:

图 25.3.3 RGB转YUV算法公式
实际上从第一步到第三步的运算, 均直接通过寄存器描述,没有顾虑时序问题。但在实际电路中会有一个数据流上的先后顺序,这三步操作同时对连续数据进行处理,通过这种方式实现硬件加速的方法称为流水线设计。前面计算出 Y、cb、 cr 我们消耗了step1、step2、step3这三个时钟, 因此需要将输入的场信号、时钟使能信号和数据有效信号同步移动 3 个时钟,如代码第134值145行。
根据实验任务,我们需要输出的是灰度数据,因此我们需要用计算得到的灰度Y来作为图像cb、cr分量,如代码第59行所示:
59assign gray_data   = {img_y,img_y,img_y};
我们在设计完代码后,需要将设计好的代码封装成IP核,以便于后续设计,有关IP核的封装请参考第十七章“IP核封装与接口定义”实验。
连线后的 Block Design 如下图所示:

图 25.3.4 Block Design整体示意图
接下来验证当前设计。验证完成后弹出对话框提示没有错误或者关键警告,点击“OK”。如果验证结果
报出错误或者警告,则需要重新检查设计。
为工程添加的约束文件与“OV5640 摄像头 LCD 显示”完全相同,有关这一部分内容请读者参考“OV5640 摄像头 LCD 显示”实验。
最后在左侧 Flow Navigator 导航栏中找到 PROGRAM AND DEBUG,点击该选项中的“ Generate
Bitstream”,对设计进行综合、实现、并生成 Bitstream 文件。在生成 Bitstream 之后,在菜单栏中选择 File > Export > Export hardware 导出硬件, 并在弹出的对话框中,勾选“Include bitstream”。然后在菜单栏选择 File > Launch SDK, 启动 SDK 软件。
25.4软件设计
本实验软件设计与OV5640摄像头LCD显示实验像是完全相同,在本章节就不再赘述,有需要的小伙伴可以参考OV5640摄像头LCD显示实验软件设计部分。
25.5下载验证
编译完工程之后我们就可以开始下载程序了。将 OV5640 摄像头模块插在启明星Zynq开发板的“OLED/CAMERA”插座上,并将 LCD 的排线接头插入开发板上的 LCD 接线座。将下载器一端连电脑,
另一端与开发板上的 JTAG 端口连接,连接电源线并打开电源开关。
在 SDK 软件下方的 SDK Terminal 窗口中点击右上角的加号设置并连接串口。然后下载本次实验硬件
设计过程中所生成的 BIT 文件,来对 PL 进行配置。最后下载软件程序,下载完成后, 在下方的 SDK Terminal中可以看到应用程序打印的信息,如下图所示:

图 25.5.1 串口打印信息窗口
同时, RGB LCD 液晶屏上显示出 OV5640 摄像头采集的图像的灰度图,说明本次 OV5640 摄像头 LCD 屏显示的实验在启明星 ZYQN 开发板上验证成功,如下图所示:

图 25.5.2 LCD屏显示结果

armok. 发表于 2020-8-23 12:13:14

打赏!

庆祝论坛“打赏”功能实施, 现在开始发技术主题,可以获得打赏
https://www.amobbs.com/thread-5735948-1-1.html
页: [1]
查看完整版本: 【正点原子FPGA连载】第二十五章OV5640摄像头灰度图显示实验--摘自【正点原子】领航者 ZYNQ 之嵌入式开发指南