RGB YUV色域转换Verilog实现
在最近一次的FPGA比赛中,其他指标都没做出来,就这个做出来了,狠狠地寄掉了。但是查了一圈网上似乎没有该算法的Verilog实现,所以现在来写写。(才不是因为图床挂了更不了复习笔记)
RGB888是最常见的色彩空间表示,但在我们的显示设备例如电视,显示屏中其实都是走的YUV色域,这种色域可以方便地接收黑白信号和彩色信号,通常看见的YCbCr其实就是YUV色域。
YUV色域其实还分很多种规格,有模拟信号的,有数字信号的,还有高清,超高清等等很多种。在测试过程中发现如果变换公式没有匹配上,会出现非常严重的色偏现象。
这里就贴一下个人电脑常用的数字信号RGB到YCbCr色域的变换矩阵:
逆变换矩阵如下:
有了矩阵就好办了,Verilog中只能使用定点数,选取10位定点计算出变换方程:
逆变换方程:
Verilog实现分三拍,第一拍用寄存器存一下乘法器输出,第二拍做加减法,第三拍右移,输出的时候需要打三拍不然时序会乱。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
reg [31:0] img_red_r0, img_red_r1, img_red_r2; reg [31:0] img_green_r0, img_green_r1, img_green_r2; reg [31:0] img_blue_r0, img_blue_r1, img_blue_r2; always@(posedge clk) begin img_red_r0 <= per_img_red * 10'd306; img_green_r0 <= per_img_green * 10'd601; img_blue_r0 <= per_img_blue * 10'd117; img_red_r1 <= per_img_red * 10'd173; img_green_r1 <= per_img_green * 10'd339; img_blue_r1 <= per_img_blue * 10'd512; img_red_r2 <= per_img_red * 10'd512; img_green_r2 <= per_img_green * 10'd429; img_blue_r2 <= per_img_blue * 10'd83; end
reg signed[31:0] img_Y_r0; reg signed[31:0] img_Cb_r0; reg signed[31:0] img_Cr_r0; always@(posedge clk) begin img_Y_r0 <= img_red_r0 + img_green_r0 + img_blue_r0; img_Cb_r0 <= img_blue_r1 - img_red_r1 - img_green_r1+18'd131072 ; img_Cr_r0 <= img_red_r2 - img_green_r2 - img_blue_r2+18'd131072 ; end
reg [7:0] img_Y_r1; reg [7:0] img_Cb_r1; reg [7:0] img_Cr_r1; always@(posedge clk) begin img_Y_r1 <= img_Y_r0[31:10]; img_Cb_r1 <= img_Cb_r0[31:10]; img_Cr_r1 <= img_Cr_r0[31:10]; end
|
寄存器用的signed型32位主要是怕有溢出和负数风险,但实际上应该是没有的,不过现在编译器都已经很智能了,开这么大它布线的时候会计算具体需要多少位然后优化的(编译器比我聪明多了)
正变换的代码是我在一本书里看到的,逆变换就是我如法炮制的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
reg signed[31:0] img_y_r0; reg signed[31:0] img_v_r0, img_v_r1; reg signed[31:0] img_u_r0, img_u_r1; always@(posedge clk) begin img_y_r0 <= per_img_y*12'd1024; img_v_r0 <= (per_img_v-8'd128) * 11'd1437; img_u_r0 <= (per_img_u-8'd128) * 11'd352; img_v_r1 <= (per_img_v-8'd128) * 11'd731; img_u_r1 <= (per_img_u-8'd128) * 11'd1815; end
reg signed[31:0] img_r_r0; reg signed[31:0] img_g_r0; reg signed[31:0] img_b_r0; always@(posedge clk) begin img_r_r0 <= img_y_r0+ img_v_r0; img_g_r0 <= img_y_r0- img_u_r0 - img_v_r1; img_b_r0 <= img_y_r0+img_u_r1; end
reg [7:0] img_r_r1; reg [7:0] img_g_r1; reg [7:0] img_b_r1; always@(posedge clk) begin img_r_r1 <= img_r_r0[31:10]; img_g_r1 <=img_g_r0[31:10]; img_b_r1 <=img_b_r0[31:10]; end
|
最后接上HDMI输出就可以啦,能够实现保证色度不变的情况下调整亮度。