Ø  YUVYCbCr格式的区别

Y'CbCr is often confused with the YUV color space, and typically the terms YCbCr and YUV are used interchangeably, leading to some confusion; when referring to signals in video or digital form, the term "YUV" mostly means "Y'CbCr".

--源自wikipedia—YUV

Y'UV is often used as the term for YCbCr. However, they are different formats. Y'UV is an analog system with scale factors different from the digital Y'CbCr system.

In digital video/image systems, Y'CbCr is the most common way to express color in a way suitable for compression/transmission. The confusion stems from computer implementations and text-books erroneously using the term YUV where Y'CbCr would be correct.

--源自wikipedia—YcbCr

实际上,数字视频编码中所说的YUV就是YCbCr

Ø  YCbCrRGB格式的相互转换

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

如果是rgb12bit的话

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个加法器和移位器,来完成RGBYUV的转换工作。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核的验证

这是最关键的一步。首先要保证所做的“YCbCrRGB格式的相互转换”和“系数整型化”这两节的操作是没有错误的。先保证有浮点数操作转化成整型数操作时没有错误,再验证整型数运算与用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!\n");
 
};
 
  fprintf
(f_r2b_v, "rgb2yuv testcase:\n");
 
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, "\tyuv_f: %9.3f, %9.3f, %9.3f", y_f, u_f, v_f);
      fprintf
(f_r2b_v, "\tyuv_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, "\tpass\n");
     
} 
     
else {
       
//for debug
        fprintf(f_r2b_v, "\tdiff: \t%f,\t%f,\t%f",fabsf(y_f - y_h), fabsf(u_f - u_h), fabsf(v_f - v_h)); 
        fprintf
(f_r2b_v, "\tRatio: \t%f,\t%f,\t%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, "\tnot pass\n");
     
}
 
}
}

输出结果如下:

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,验证采用alteraALTMULT_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: \t%d,\t%d,\t%d, yuv: \t%d,\t%d,\t%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

其它的一些脚本文件见附件。

Ø  总结

讨论了YCrCbYUV格式的区别,YCrCb来自于YUV,在数字图像处理领域所说的YUV也就是YCrCb;对RGBYCrCb转换的算法作了处理转换成整型数操作;用Verilog结合AlteraALT的库设计了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设计的整个流程

参考文献

(原創) 如何使用VerilogYCbCrRGB? (SOC) (Verilog) (DE2-70)

作者: 任怀鲁 发表于 2011-07-31 15:40 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"