昨天才更新了两篇博客,今天又要更新了,并不是我垃圾产,只不过这些在上个月就已经写好了,只是因为比赛忙,一直腾不出时间整理出来发表而已,但是做完一件事情总感觉不写一博文总结一下就少点什么,所以之后的一段时间里我会把我这学期学到的一些东西陆续整理出来发表,给自己一个总结交代。
将彩色图像转化为灰度的方法有两种,一个是令RGB三个分量的数值相等,输出后便可以得到灰度图像,另一种是转化为YCbCr格式,将Y分量提取出来,YCbCr格式中的Y分量表示的是图像的亮度和浓度所以只输出Y分量,得到的图像就是灰度图像了。我在这里选择第二种方法实现。
YCBCr是通过有序的三元组来表示的,三元由Y(Luminance)、Cb(Chrominance-Blue)和Cr(Chrominance-Red)组成,其中Y表示颜色的明亮度和浓度,而Cb和Cr则分别表示颜色的蓝色浓度偏移量和红色浓度偏移量。人的肉眼对由YCbCr色彩空间编码的视频中的Y分量更敏感,而Cb和Cr的微小变化不会引起视觉上的不同,根据该原理,通过对Cb和Cr进行子采样来减小图像的数据量,使得图像对存储需求和传输带宽的要求大大降低,从而达到在完成图像压缩的同时也保证了视觉上几乎没有损失的效果,进而使得图像的传输速度更快,存储更加方便。我们要的到灰度图像,首先要将采集到的彩色图像转化为YCbCr。
我们配置摄像头采集到的数据是RGB565的格式,官方给出的转化公式是RGB888->YCbCr,所以先需要将RGB565转化为RGB888,转化方法如下:
24bit RGB888 -> 16bit RGB565 的转换(只取高位)
24ibt RGB888 {R7 R6 R5 R4 R3 R2 R1 R0} {G7 G6 G5 G4 G3 G2 G1 G0} {B7 B6 B5 B4 B3 B2 B1 B0}
16bit RGB656 {R7 R6 R5 R4 R3} {G7 G6 G5 G4 G3 G2} {B7 B6 B5 B4 B3}
同样也可以恢复回去。
16bit RGB565 -> 24bit RGB888 的转换(高位补低位)
16bit RGB656 {R4 R3 R2 R1 R0} {G5 G4 G3 G2 G1 G0} {B4 B3 B2 B1 B0}
24ibt RGB888 {R4 R3 R2 R1 R0 R2 R1 R0} {G5 G4 G3 G2 G1 G0 G1 G0} {B4 B3 B2 B1 B0 B2 B1 B0}
1 //--------------------------------------------
2 //RGB565 to RGB 888
3 wire [7:0] cmos_R0;
4 wire [7:0] cmos_G0;
5 wire [7:0] cmos_B0;
6
7 assign cmos_R0 = {cmos_R, cmos_R[4:2]};
8 assign cmos_G0 = {cmos_G, cmos_G[5:4]};
9 assign cmos_B0 = {cmos_B, cmos_B[4:2]};
采用高位补低位的方法直接转化即可。
这是官方给的RGB888 to YCbCr的算法公式,我们可以直接把算法移植到FPGA上,但是我们都知道FPGA无法进行浮点运算,所以我们采取将整个式子右端先都扩大256倍,然后再右移8位,这样就得到了FPGA擅长的乘法运算和加法运算了。
这个计算式子看起来是十分简单的,但是要是直接用Verilog直接写出来,那么只能说,这个人的代码写的一塌糊涂,所以这里就引出FPGA中流水线的设计思想。
在这里我们选择加3级流水线,就第一个Y分量而言,先计算括号中得乘法运算,消耗一个时钟,然后将括号中的数据求和,消耗一个时钟,这里为了计算方便,将128也扩大256倍,放到括号中,最终结果除以256就行了也就是右移8位,在FPGA中我们只需要舍弃低8位取高8位就行。具体代码如下
1 //--------------------------------------------
2 /*Refer to <OV7725 Camera Module Software Applicaton Note> page 5
3 Y = (77 *R + 150*G + 29 *B)>>8
4 Cb = (-43*R - 85 *G + 128*B)>>8 + 128
5 Cr = (128*R - 107*G - 21 *B)>>8 + 128
6 --->
7 Y = (77 *R + 150*G + 29 *B)>>8
8 Cb = (-43*R - 85 *G + 128*B + 32768)>>8
9 Cr = (128*R - 107*G - 21 *B + 32768)>>8*/
10 //--------------------------------------------
11 //RGB888 to YCrCb
12 //step1 conmuse 1clk
13 reg [15:0] cmos_R1, cmos_R2, cmos_R3;
14 reg [15:0] cmos_G1, cmos_G2, cmos_G3;
15 reg [15:0] cmos_B1, cmos_B2, cmos_B3;
16 always @(posedge clk or negedge rst_n)
17 begin
18 if(!rst_n)begin
19 cmos_R1 <= 16'd0;
20 cmos_G1 <= 16'd0;
21 cmos_B1 <= 16'd0;
22 cmos_R2 <= 16'd0;
23 cmos_G2 <= 16'd0;
24 cmos_B2 <= 16'd0;
25 cmos_R3 <= 16'd0;
26 cmos_G3 <= 16'd0;
27 cmos_B3 <= 16'd0;
28 end
29 else begin
30 cmos_R1 <= cmos_R0 * 8'd77;
31 cmos_G1 <= cmos_G0 * 8'd150;
32 cmos_B1 <= cmos_B0 * 8'd29;
33 cmos_R2 <= cmos_R0 * 8'd43;
34 cmos_G2 <= cmos_G0 * 8'd85;
35 cmos_B2 <= cmos_B0 * 8'd128;
36 cmos_R3 <= cmos_R0 * 8'd128;
37 cmos_G3 <= cmos_G0 * 8'd107;
38 cmos_B3 <= cmos_B0 * 8'd21;
39 end
40 end
41
42 //-----------------------------------------------
43 //step2 consume 1clk
44 reg [15:0] img_Y0;
45 reg [15:0] img_Cb0;
46 reg [15:0] img_Cr0;
47
48 always @(posedge clk or negedge rst_n)
49 begin
50 if(!rst_n)begin
51 img_Y0 <= 16'd0;
52 img_Cb0 <= 16'd0;
53 img_Cr0 <= 16'd0;
54 end
55 else begin
56 img_Y0 <= cmos_R1 + cmos_G1 + cmos_B1;
57 img_Cb0 <= cmos