FPGA|CPLD|ASIC论坛
直播中

正点原子运营官

5年用户 1793经验值
擅长:模拟技术 嵌入式技术 控制/MCU
私信 关注
[资料]

正点原子开拓者FPGA开发板资料连载第五十四章OV5640 摄像头显示实验

1)实验平台:正点原子开拓者FPGA 开发板
2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html


第五十三章 OV5640 摄像头灰度图 VGA 显示实验
前面的实验我们介绍了OV5640摄像头的VGA显示实验,而在数字图像处理领域YUV是一种很
常用的图像格式,其特点是将亮度和色度进行分离。在本章实验中,我们将进行RGB565到YUV
的颜色空间转换,并通过VGA显示摄像头采集到的图像的灰度图的实验。
本章包括以下几个部分:
53.1 YUV 简介
53.2 实验任务
53.3 硬件设计
53.4 程序设计
53.5 下载验证
YUV 简介
人眼中的锥状细胞是负责彩色视觉的传感器,可分为三个主要的感知类别,分别对应红色、
绿色、蓝色,我们人眼看到的彩色实际上是红、绿、蓝三原色的各种组合。前面我们用到的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 信号的提出,是因为国际上出现彩色电视,为了兼容黑白电视的信号而设计的,在视
频码率,压缩,兼容性等方面有很大优势。一般意义上 YCbCr即为 YUV信号,没有严格的划
分。CbCr 分别为蓝色色度分量、红色色度分量。其主要采样格式有YCbCr4:2:2、YCbCr4:2:0、
YCbCr4:4:4、YCbCr4:1:1。下面将介绍这四种采样格式。
(1)YCbCr4:4:4:
YUV三个信道的抽样率相同,因此在生成的图像里,每个象素的三个分量信息完整(每个
分量通常8比特),经过8比特量化之后,未经压缩的每个像素占用3个字节。
下面的四个像素为: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放的码流为: Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3


图 53.1.1 YUV444像素格式
根据存储顺序不同,YUV444又分为以下两种形式:



图 53.1.2 两种不同格式的YUV444像素
(2)YCbCr4:2:2:
每个色差信道的抽样率是亮度信道的一半,所以水平方向的色度抽样率只是4:4:4的一半。
对非压缩的8比特量化的图像来说,每个由两个水平方向相邻的像素组成的宏像素需要占用4字
节内存。
下面的四个像素为: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放的码流为: Y0 U0 Y1 V1 Y2 U2 Y3 V3
映射出像素点为:[Y0 U0 V1] [Y1 U0 V1] [Y2 U2 V3] [Y3 U2 V3]


图 53.1.3 YUV422像素格式
同样,根据存储顺序,YUV422可以分为六种不同格式:



图 53.1.4 六种不同的YUV422格式
(3)YCbCr4:2:0
4:2:0并不意味着只有Y,Cb而没有Cr分量。它指得是对每行扫描线来说,只有一种色度分
量以2:1的抽样率存储。相邻的扫描行存储不同的色度分量,也就是说,如果一行是4:2:0的话,
下一行就是4:0:2,再下一行是4:2:0...以此类推。对每个色度分量来说,水平方向和竖直方
向的抽样率都是2:1,所以可以说色度的抽样率是4:1。对非压缩的8比特量化的视频来说,每
个由2x2个2行2列相邻的像素组成的宏像素需要占用6字节内存。
下面八个像素为:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3][Y5 U5 V5] [Y6 U6
V6] [Y7U7 V7] [Y8 U8 V8]
存放的码流为:Y0 U0 Y1 Y2 U2 Y3Y5 V5 Y6 Y7 V7 Y8
映射出的像素点为:[Y0 U0 V5] [Y1 U0 V5] [Y2 U2 V7] [Y3 U2 V7][Y5 U0 V5] [Y6 U0
V5] [Y7U2 V7] [Y8 U2 V7]
(4)YCbCr4:1:1:
4:1:1的色度抽样,是在水平方向上对色度进行4:1抽样。对于低端用户和消费类产品这仍
然是可以接受的。对非压缩的8比特量化的视频来说,每个由4个水平方向相邻的像素组成的宏
像素需要占用6字节内存。
下面的四个像素为: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放的码流为: Y0 U0 Y1 Y2 V2 Y3
映射出像素点为:[Y0 U0 V2] [Y1 U0 V2] [Y2 U0 V2] [Y3 U0 V2]
下面为 RGB 与 YCbCr 色彩空间转换的算法公式, RGB 转 YCbCr 的公式如下所示:



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



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



图 53.1.7 RGB 转 YcbCr算法
实际上OV5640本身支持输出RGB、YUV格式的数据,本章节实验是着重于实RGB转YUV的HDL
算法实现,因此我们把摄像头设置为RGB565格式。当我们需要显示器显示灰度图时,我们只需
要将转换后的Y值作为R、G、B三原色通道的输入就可以实现了。
实验任务
本节实验任务是使用开拓者开发板及OV5640摄像头采集RGB565格式的数据,并通过算法转
换,将RGB565格式转换为YCbCr格式,然后通过VGA显示器实时显示灰度图。
硬件设计
本章节中硬件设计与OV5640的VGA显示实验完全相同,在此就不在赘述。
程序设计
下图是根据本章实验任务画出的系统框图。对比OV5640摄像头VGA显示实验,本实验中添
加了rgb2ycbcr模块,该模块用于将摄像头采集到的RGB565数据转换成YCbCr格式数据,并将转
换后的数据写入SDRAM。
OV5640摄像头VGA显示灰度图系统框图如下图所示:



图 53.4.1 OV5640摄像头VGA显示灰度图系统框图
顶层模块原理图如下图所示:


图 53.4.2 rgb2yuv模块原理图
有关各个模块的功能可以参考“OV5640摄像头VGA显示实验”的相关描述,在本实验中我
们添加了rgb2ycbcr模块,在这里我们将对rgb2ycbcr模块的功能进行描述。
rgb2ycbcr模块的原理图如下:


图 53.4.3 rgb2ycbcr模块原理图
在该模块以摄像头采集的16位RGB565红、绿、蓝三原色数据作为输入数据,通过算法实现
RGB到YCbCr的转换,并输出三路8位数据和数据输出使能信号。
有关于摄像头、SDRAM的配置,VGA的驱动在前面的章节以有过介绍,本章节我们着重介绍
RGB转YUV的算法模块。
RGB转YUV模块的代码如下:
1 module rgb2ycbcr
2 (
3 //module clock
4 input clk , // 模块驱动时钟
5 input rst_n , // 复位信号
6
7 //图像处理前的数据接口
8 input pre_frame_vsync , // vsync信号
9 input pre_frame_hsync , // hsync信号
10 input pre_frame_de , // data enable信号
11 input [4:0] img_red , // 输入图像数据R
12 input [5:0] img_green , // 输入图像数据G
13 input [4:0] img_blue , // 输入图像数据B
14
15 //图像处理后的数据接口
16 output post_frame_vsync, // vsync信号
17 output post_frame_hsync, // hsync信号
18 output post_frame_de , // data enable信号
19 output [7:0] img_y, // 输出图像Y数据
20 output [7:0] img_cb,
21 output [7:0] img_cr
22 );
23
24 //reg define
25 reg [15:0] rgb_r_m0, rgb_r_m1, rgb_r_m2;
26 reg [15:0] rgb_g_m0, rgb_g_m1, rgb_g_m2;
27 reg [15:0] rgb_b_m0, rgb_b_m1, rgb_b_m2;
28 reg [15:0] img_y0 ;
29 reg [15:0] img_cb0;
30 reg [15:0] img_cr0;
31 reg [ 7:0] img_y1 ;
32 reg [ 7:0] img_cb1;
33 reg [ 7:0] img_cr1;
34 reg [ 2:0] pre_frame_vsync_d;
35 reg [ 2:0] pre_frame_hsync_d;
36 reg [ 2:0] pre_frame_de_d ;
37
38 //wire define
39 wire [ 7:0] rgb888_r;
40 wire [ 7:0] rgb888_g;
41 wire [ 7:0] rgb888_b;
42
43 //*****************************************************
44 //** main code
45 //*****************************************************
46
47 //RGB565 to RGB 888
48 assign rgb888_r = {img_red , img_red[4:2] };
49 assign rgb888_g = {img_green, img_green[5:4]};
50 assign rgb888_b = {img_blue , img_blue[4:2] };
51 //同步输出数据接口信号
52 assign post_frame_vsync = pre_frame_vsync_d[2] ;
53 assign post_frame_hsync = pre_frame_hsync_d[2] ;
54 assign post_frame_de = pre_frame_de_d[2] ;
55 assign img_y = post_frame_hsync ? img_y1 : 8'd0;
56 assign img_cb = post_frame_hsync ? img_cb1: 8'd0;
57 assign img_cr = post_frame_hsync ? img_cr1: 8'd0;
58
59 //--------------------------------------------
60 //RGB 888 to YCbCr
61
62 /********************************************************
63 RGB888 to YCbCr
64 Y = 0.299R +0.587G + 0.114B
65 Cb = 0.568(B-Y) + 128 = -0.172R-0.339G + 0.511B + 128
66 CR = 0.713(R-Y) + 128 = 0.511R-0.428G -0.083B + 128
67
68 Y = (77 *R + 150*G + 29 *B)>>8
69 Cb = (-43*R - 85 *G + 128*B)>>8 + 128
70 Cr = (128*R - 107*G - 21 *B)>>8 + 128
71
72 Y = (77 *R + 150*G + 29 *B )>>8
73 Cb = (-43*R - 85 *G + 128*B + 32768)>>8
74 Cr = (128*R - 107*G - 21 *B + 32768)>>8
75 *********************************************************/
76
77 //step1 计算括号内的各乘法项
78 always @(posedge clk or negedge rst_n) begin
79 if(!rst_n) begin
80 rgb_r_m0 <= 16'd0;
81 rgb_r_m1 <= 16'd0;
82 rgb_r_m2 <= 16'd0;
83 rgb_g_m0 <= 16'd0;
84 rgb_g_m1 <= 16'd0;
85 rgb_g_m2 <= 16'd0;
86 rgb_b_m0 <= 16'd0;
87 rgb_b_m1 <= 16'd0;
88 rgb_b_m2 <= 16'd0;
89 end
90 else begin
91 rgb_r_m0 <= rgb888_r * 8'd77 ;
92 rgb_r_m1 <= rgb888_r * 8'd43 ;
93 rgb_r_m2 <= rgb888_r * 8'd128;
94 rgb_g_m0 <= rgb888_g * 8'd150;
95 rgb_g_m1 <= rgb888_g * 8'd85 ;
96 rgb_g_m2 <= rgb888_g * 8'd107;
97 rgb_b_m0 <= rgb888_b * 8'd29 ;
98 rgb_b_m1 <= rgb888_b * 8'd128;
99 rgb_b_m2 <= rgb888_b * 8'd21 ;
100 end
101 end
102
103 //step2 括号内各项相加
104 always @(posedge clk or negedge rst_n) begin
105 if(!rst_n) begin
106 img_y0 <= 16'd0;
107 img_cb0 <= 16'd0;
108 img_cr0 <= 16'd0;
109 end
110 else begin
111 img_y0 <= rgb_r_m0 + rgb_g_m0 + rgb_b_m0;
112 img_cb0 <= rgb_b_m1 - rgb_r_m1 - rgb_g_m1 + 16'd32768;
113 img_cr0 <= rgb_r_m2 - rgb_g_m2 - rgb_b_m2 + 16'd32768;
114 end
115
116 end
117
118 //step3 括号内计算的数据右移8位
119 always @(posedge clk or negedge rst_n) begin
120 if(!rst_n) begin
121 img_y1 <= 8'd0;
122 img_cb1 <= 8'd0;
123 img_cr1 <= 8'd0;
124 end
125 else begin
126 img_y1 <= img_y0 [15:8];
127 img_cb1 <= img_cb0[15:8];
128 img_cr1 <= img_cr0[15:8];
129 end
130 end
131
132 //延时3拍以同步数据信号
133 always@(posedge clk or negedge rst_n) begin
134 if(!rst_n) begin
135 pre_frame_vsync_d <= 3'd0;
136 pre_frame_hsync_d <= 3'd0;
137 pre_frame_de_d <= 3'd0;
138 end
139 else begin
140 pre_frame_vsync_d <= {pre_frame_vsync_d[1:0], pre_frame_vsync};
141 pre_frame_hsync_d <= {pre_frame_hsync_d[1:0], pre_frame_hsync};
142 pre_frame_de_d <= {pre_frame_de_d[1:0] , pre_frame_de };
143 end
144 end
145
146 endmodule
在RGB转成YUV格式的算法换算过程中数据都是以8位的数据进行的,因而我们需要将
RGB565格式的数据转换成RGB888的格式,如代码第47之50行所示,此处采用的是高位补充地位
的方法。转换后就是进行RGB565转YCbCr算法的HDL实现:第一步,先计算出前面公式中括号里
每一个乘法的乘积,如代码第77至101行所示;第二步,计算出Y、Cb、Cr括号内的值,代码103
至116行;第三步,右移 8Bit,由于 Step2 计算结果为 16Bit, 因此直接提取高8位 即可,
代码如118至130行所示。
实际上从第一步到第三步的运算, 均直接通过寄存器描述,没有顾虑行场有效时序等。
但在实际电路中会有一个数据流上的先后顺序,这三步操作同时对连续数据进行处理,通过这
种方式实现硬件加速的方法称为流水线设计。前面计算出 Y、 Cb、 Cr 我们消耗了step1、
step2、step3这三个时钟, 因此需要将输入的行场信号、使能信号同步移动 3 个时钟,如代
码第132值144行。
在代码第21至22行可以看出,转换后输出的数据分别为8位的Y(灰度)、Cb(蓝色色度分
量)、Cr(红色色度分量),而我们实验中需要的是表示灰度的数据,因此我们只需取Y值的
高5位、高6位、高5位数据作为VGA显示的红、绿、蓝三通道输入数据,如顶层代码第171行所
示:
170 .wr_en (wr_en), //写端口FIFO: 写使能
171 .wr_data ({img_y[7:3],img_y[7:2],img_y[7:3]}), //写端口FIFO: 写数据
172 .wr_min_addr (24'd0), //写SDRAM的起始地址
下载验证
首 先 我 们 打 开 OV5640 摄 像 头 VGA 显 示 实 验 工 程 , 在 工 程 所 在 的 路 径 下 打 开
ov5640_rgb565_yuv_vga/par文件夹,在里面找“ov5640_rgb565_yuv_vga.qpf”并双击打开。
注意工程所在的路径名只能由字母、数字以及下划线组成,不能出现中文、空格以及特殊字符
等。工程打开后如下图所示:


图 53.5.1 OV5640摄像头VGA显示实验工程
然后将OV5640摄像头插入开发板上的摄像头扩展接口(注意摄像头镜头朝外),将VGA连
接线一端连接显示器,另一端与开发板上的VGA接口连接。再将下载器一端连电脑,另一端与
开发板上对应端口连接,最后连接电源线并打开电源开关。
开拓者开发板实物图如下所示:



图 53.5.2 ov5640摄像头连接图
接下来我们下载程序,验证OV5640摄像头VGA实时显示的功能。工程打开后通过点击工具
栏中的“Programmer”图标打开下载界面,通过“Add File”按钮选择ov5640_rgb565 _vga_yuv
/par/output_files目录下的“ov5640_rgb565_yuv_vga.sof”文件。开发板电源打开后, 在
程序下载界面点击“Hardware Setup”,在弹出的对话框中选择当前的硬件连接为“USB
Blaster[USB-0]”。然后点击“Start”将工程编译完成后得到的sof文件下载到开发板中,如
图所示:



图 53.5.3 程序下载完成界面
下载完成后观察显示器的显示图像如下所示,说明OV5640摄像头VGA显示程序下载验证成
功。


图 53.5.4 VGA实时显示图像





更多回帖

发帖
×
20
完善资料,
赚取积分