用verilog实现RGB名目图像到YCbCr或YUV名目的转换及其验证方法
现实上,数字视频编码中所说的YUV就是YCbCr。
? YCbCr与RGB名目的彼此转换
RGB to YUV Conversion
Y = (0.257 * R) + (0.504 * G) + (0.098 * B) + 16
Cr = V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128
Cb = U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128
假如是rgb是12bit的话
Y = (0.257 * R) + (0.504 * G) + (0.098 * B) + 256
Cr = V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 2048
Cb = U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 2048
YUV to RGB Conversion
B = 1.164(Y - 16) + 2.018(U - 128)
G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)
R = 1.164(Y - 16) + 1.596(V - 128)
In both these cases, you have to clamp the output values
? 系数矩阵整型化
在RGB2YCbCr的公式中摆布各乘以1024即左移10位
Y << 10 = (263 * R) + (516 * G) + (100 * B) + 262144
Cr << 10 = (450 * R) - (377 * G) - ( 73 * B) + 2097152
Cb << 10 = -(152 * R) - (298 * G) + (450 * B) + 2097152
负数取补,保守一点,系数取12位
Y << 10 = (12""h107 * R) + (12""h204 * G) + (12""h064 * B) + 24""h040000
Cr << 10 = (12""h1C2 * R) + (12""hE87 * G) + (12""hFB7 * B) + 24""h200000
Cb << 10 = (12""hF68 * R) + (12""hED6 * G) + (12""h1C2 * B) + 24""h200000
友晶的D5M中采样出来的RGB名目是各占12bit,最终YCrCb要截取成8位的,仍是在转换之前保持精度,在转换之后再截取吧。以下设计方法参考友晶DE2系列中YCbCr2RGB的参考例程。
系数12位有符号,输进的RGB数据是12位无符号,乘加之后输出数据为26位。在MegaWizard Plug-In Manager工具中设置装备摆设如下。
欠好的一点,没有找到ALTMUT_ADD核中datab为常数的设置装备摆设,该当是没有的,可以手动用其它乘法器核(设置一个相乘的系数为常数)和加法器核来搭。
? RGB2YCrCb核的设计
采用了3个乘加器,之后又各自用了3个加法器和移位器,来完成RGB到YUV的转换工作。rgb2yuv.v的代码如下
// Author(s):
// - Huailu Ren, hlren.pub@gmail.com, http://lunix.cnblogs.com
//
// Revision 1.0 21:58 2011-7-30 hlren
// created
//
// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
module RGB2YCrCb (
iCLK,
iRESET,
iRed,
iGreen,
iBlue,
iDVAL,
oY,
oCb,
oCr,
oDVAL
);
// Input
input iCLK,iRESET,iDVAL;
input [11:0] iRed,iGreen,iBlue;
// Output
output reg [11:0] oY,oCb,oCr;
output reg oDVAL;
// Internal Registers/Wires
reg [3:0] oDVAL_d;
reg [15:0] tY_r,tU_r,tV_r;
wire [25:0] tY,tU,tV;
always@(posedge iCLK)
begin
if(iRESET)
begin
oDVAL<=0;
oDVAL_d<=0;
oY <=0;
oCr<=0;
oCb<=0;
end
else
begin
// Red
if(tY_r[15])
oY<=0;
else if(tY_r[14:0]>4095)
oY<=4095;
else
oY<=tY_r[11:0];
// Green
if(tU_r[15])
oCr<=0;
else if(tU_r[14:0]>4095)
oCr<=4095;
else
oCr<=tU_r[11:0];
// Blue
if(tV_r[15])
oCb<=0;
else if(tV_r[14:0]>4095)
oCb<=4095;
else
oCb<=tV_r[11:0];
// Control
{oDVAL,oDVAL_d}<={oDVAL_d,iDVAL};
end
end
always@(posedge iCLK)
begin
if(iRESET)
begin
tY_r <= 0;
tU_r <= 0;
tV_r <= 0;
end
else
begin
tY_r <= ( tY + 262144 ) >> 10;
tU_r <= ( tU + 2097152 ) >> 10;
tV_r <= ( tV + 2097152 ) >> 10;
end
end
// Y << 10 = (12""h107 * R) + (12""h204 * G) + (12""h064 * B) + 20""h04000
MAC_3 u0(
.aclr0 ( iRESET ),
.clock0 ( iCLK ),
.dataa_0 ( iRed ),
.dataa_1 ( iGreen ),
.dataa_2 ( iBlue ),
.datab_0 ( 12""h107 ),
.datab_1 ( 12""h204 ),
.datab_2 ( 12""h064 ),
.result ( tY )
);
// Cr << 10 = (12""h1C2 * R) + (12""hE87 * G) + (12""hFB7 * B) + 20""h20000
MAC_3 u1(
.aclr0 ( iRESET ),
.clock0 ( iCLK ),
.dataa_0 ( iRed ),
.dataa_1 ( iGreen ),
.dataa_2 ( iBlue ),
.datab_0 ( 12""h1C2 ),
.datab_1 ( 12""hE87 ),
.datab_2 ( 12""hFB7 ),
.result ( tU )
);
// Cb << 10 = (12""hF68 * R) + (12""hED6 * G) + (12""h1C2 * B) + 20""h20000
MAC_3 u2(
.aclr0 ( iRESET ),
.clock0 ( iCLK ),
.dataa_0 ( iRed ),
.dataa_1 ( iGreen ),
.dataa_2 ( iBlue ),
.datab_0 ( 12""hF68 ),
.datab_1 ( 12""hED6 ),
.datab_2 ( 12""h1C2 ),
.result ( tV )
);
endmodule
? RGB2YCrCb核的验证
这是最要害的一步。首先要保证所做的“YCbCr与RGB名目的彼此转换”和“系数整型化”这两节的独霸是没有短处的。先保证有浮点数独霸转化成整型数独霸时没有短处,再验证整型数运算与用ALTMULT_ADD核运算时数据没有短处。本文中,第一步用C说话来进行验证,第二步在testbench中验证。
所写的C说话的验证代码如下:
// Author(s):
// - Huailu Ren, hlren.pub@gmail.com, http://lunix.cnblogs.com
//
// Revision 1.0 9:56 2011-7-31 hlren
// created
//
#include "stdio.h"
#include "math.h"
int main(){
FILE * f_r2b_v;//rgb2yuv_verification log file
int r, g, b;
int i;
float y_f, u_f, v_f; // floating point calculation
int y_h, u_h, v_h; // fixed point for hardware implementation
if(NULL==(f_r2b_v = fopen("rgb2yuv.log","w"))){
printf("open file rgb2yuv.log error! ");
};
fprintf(f_r2b_v, "rgb2yuv testcase: ");
for(i=0;i<10;i=i+1) {
r = (i+1)*(i+2)*(i+3)*19*23*29*41%4096;
g = (i+1)*(i+2)*(i+3)*17*13*31*37%4096;
b = (i+1)*(i+2)*(i+3)*13*11*37*41%4096;
y_f = (0.257 * r) + (0.504 * g) + (0.098 * b) + 256 ;
u_f = (0.439 * r) - (0.368 * g) - (0.071 * b) + 2048;
v_f = -(0.148 * r) - (0.291 * g) + (0.439 * b) + 2048;
y_h = ( (263 * r) + (516 * g) + (100 * b) + 262144 )>>10;
u_h = ( (450 * r) - (377 * g) - ( 73 * b) + 2097152)>>10;
v_h = (-(152 * r) - (298 * g) + (450 * b) + 2097152)>>10;
fprintf(f_r2b_v, "%2d, rgb: %5d, %5d, %5d", i, r, g, b);
fprintf(f_r2b_v, " yuv_f: %9.3f, %9.3f, %9.3f", y_f, u_f, v_f);
fprintf(f_r2b_v, " yuv_h: %5d, %5d, %5d", y_h, u_h, v_h);
if( (((fabsf(y_f - y_h))/y_f)<0.1) &&
(((fabsf(u_f - u_h))/u_f)<0.1) &&
(((fabsf(v_f - v_h))/v_f)<0.1) ){
fprintf(f_r2b_v, " pass ");
}
else {
//for debug
fprintf(f_r2b_v, " diff: %f, %f, %f",fabsf(y_f - y_h), fabsf(u_f - u_h), fabsf(v_f - v_h));
fprintf(f_r2b_v, " Ratio: %f, %f, %f",
((fabsf(y_f - y_h))/y_f),
((fabsf(u_f - u_h))/u_f),
((fabsf(v_f - v_h))/v_f));
fprintf(f_r2b_v, " not pass ");
}
}
}
输出成效如下:
rgb2yuv testcase:
0, rgb: 502, 1306, 3154 yuv_f: 1352.330, 1563.836, 2978.264 yuv_h: 1351, 1562, 2979 pass
1, rgb: 2008, 1128, 328 yuv_f: 1372.712, 2491.120, 1566.560 yuv_h: 1372, 2491, 1565 pass
2, rgb: 924, 772, 2868 yuv_f: 1163.620, 1965.912, 2945.648 yuv_h: 1162, 1965, 2946 pass
3, rgb: 1848, 1544, 1640 yuv_f: 1669.832, 2174.640, 2045.152 yuv_h: 1668, 2174, 2045 pass
4, rgb: 1186, 654, 3894 yuv_f: 1272.030, 2051.508, 3391.624 yuv_h: 1270, 2050, 3392 pass
5, rgb: 3536, 3504, 496 yuv_f: 2979.376, 2275.616, 722.752 yuv_h: 2978, 2276, 721 pass
6, rgb: 1208, 3208, 2792 yuv_f: 2456.904, 1199.536, 2161.376 yuv_h: 2455, 1198, 2162 pass
7, rgb: 2896, 1072, 1648 yuv_f: 1702.064, 2807.840, 2030.912 yuv_h: 1700, 2808, 2030 pass
8, rgb: 910, 2498, 218 yuv_f: 1770.226, 1512.748, 1282.104 yuv_h: 1769, 1512, 1281 pass
9, rgb: 3944, 600, 1656 yuv_f: 1734.296, 3441.040, 2016.672 yuv_h: 1733, 3442, 2015 pass
成效剖明,在整型化转换过程中,是没有短处的。
下面匹面劈脸写testbench,验证采用altera的ALTMULT_ADD核后是否有短处。
Testbench的代码如下
// Author(s):
// - Huailu Ren, hlren.pub@gmail.com, http://lunix.cnblogs.com
//
// Revision 1.0 9:56 2011-7-31 hlren
// created
//
// synopsys translate_off
`include "timescale.v"
// synopsys translate_on
module tb_rgb2yuv;
reg clk, reset, idval;
reg [11:0] r, g, b;
wire [11:0] y, u, v;
wire odval;
RGB2YCrCb DUT_rgb2yuv(
.iCLK (clk),
.iRESET (reset),
.iRed (r),
.iGreen (g),
.iBlue (b),
.iDVAL (idval),
.oY (y),
.oCr (u),
.oCb (v),
.oDVAL (odval)
);
initial clk = 0;
always #5 clk = ~clk;
initial
begin
reset = 1;
repeat (2) @ (posedge clk);
reset = 0;
end
reg [11:0] veri_y[9:0];
reg [11:0] veri_u[9:0];
reg [11:0] veri_v[9:0];
integer i;
initial
begin
idval = 0;
repeat (3) @ (posedge clk);
idval = 1;
for(i=0;i<10;i=i+1)
begin
r = (i+1)*(i+2)*(i+3)*19*23*29*41%4096;
g = (i+1)*(i+2)*(i+3)*17*13*31*37%4096;
b = (i+1)*(i+2)*(i+3)*13*11*37*41%4096;
veri_y[i] = ( (263 * r) + (516 * g) + (100 * b) + 262144 )>>10;
veri_u[i] = ( (450 * r) - (377 * g) - ( 73 * b) + 2097152)>>10;
veri_v[i] = (-(152 * r) - (298 * g) + (450 * b) + 2097152)>>10;
//veri_y[i] = (0.257 * r) + (0.504 * g) + (0.098 * b) + 256 ;
//veri_u[i] = (0.439 * r) - (0.368 * g) - (0.071 * b) + 2048;
//veri_v[i] = -(0.148 * r) - (0.291 * g) + (0.439 * b) + 2048;
repeat (1) @ (posedge clk);
end
idval = 0;
repeat (100) @ (posedge clk);
¥finish;
end
integer j;
always @ (posedge clk)
if(reset)
j <= 0;
else if (odval)
j <= j + 1;
always @ (posedge clk)
if (odval) begin
¥display("veri: %d, %d, %d, yuv: %d, %d, %d",
veri_y[j],
veri_u[j],
veri_v[j],
y, u, v);
end
`ifdef FSDBDUMP
initial
begin
¥fsdbDumpfile("test.fsdb");
¥fsdbDumpvars;
end
`endif
endmodule
验证成效如下
在modelsim中
在debussy中
其它的一些剧本文件见附件。
? 总结
谈判了YCrCb和YUV名目的分歧,YCrCb来自于YUV,在数字图像措置赏罚领域所说的YUV也就是YCrCb;对RGB到YCrCb转换的算法作了措置赏罚转换成整型数独霸;用Verilog连系Altera的ALT的库设计了RGB2YCrCb的核;用C说话和Verilog说话对所设计的核进行了验证。
源码在这里
rgb2yuv.7z
设计文件在顶层目录中,仿真所需文件在bench目录下,仿真情况也在bench目录下。
vsim –do tb_rgb2yuv.do –c
即可。Bench目录下还包含仿真剧本文件,debussy的旗帜记号文件,C说话验证的文件,altera的库文件。
To Do
- 把用到的乘加器ALTMULT_ADD用三个常熟系数的乘法器和一个并行加法器来代替。
- 把文中的两个验证措施,用Verilog Pli的方法合并成一个。
- 以此设计为根本,试着用数字IC设计的方法设计,试探和进修数字IC设计的全数流程
参考文献
(原創) 若何操作Verilog將YCbCr轉RGB? (SOC) (Verilog) (DE2-70)别林斯基