用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        iCLKiRESETiDVAL;
  input [11:0] iRediGreeniBlue;
  
  //   Output
  output reg [11:0] oYoCboCr;
  output reg oDVAL;
  //   Internal Registers/Wires
  reg     [3:0] oDVAL_d;
  reg   [15:0] tY_rtU_rtV_r;
  wire  [25:0] tYtUtV;

  always@(posedge iCLK
  begin
    ifiRESET
    begin
      oDVAL<=0;
      oDVAL_d<=0;
      oY <=0;
      oCr<=0;
      oCb<=0;
    end
    else
    begin
      // Red
      iftY_r[15])
        oY<=0;
      else iftY_r[14:0]>4095
        oY<=4095;
      else
        oY<=tY_r[11:0];
      
      // Green
      iftU_r[15])
        oCr<=0;
      else iftU_r[14:0]>4095
        oCr<=4095;
      else
        oCr<=tU_r[11:0];
      
      // Blue
      iftV_r[15])
        oCb<=0;
      else iftV_r[14:0]>4095
        oCb<=4095;
      else
        oCb<=tV_r[11:0];
      
      // Control
      {oDVALoDVAL_d}<={oDVAL_diDVAL};
    end
  end

  always@(posedge iCLK
  begin
    ifiRESET
    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
  
  ifNULL==(f_r2b_v = fopen"rgb2yuv.log""w"))){
    printf"open file rgb2yuv.log error! ");
  }; 
  
  fprintff_r2b_v "rgb2yuv testcase: ");
  fori=0;i<10;i=i+1 {
      r = i+1)*(i+2)*(i+3)*19*23*29*414096;
      g = i+1)*(i+2)*(i+3)*17*13*31*374096;
      b = i+1)*(i+2)*(i+3)*13*11*37*414096;
      
      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;
      
      fprintff_r2b_v "%2d, rgb: %5d, %5d, %5d" i r g b);
      fprintff_r2b_v " yuv_f: %9.3f, %9.3f, %9.3f" y_f u_f v_f);
      fprintff_r2b_v " yuv_h: %5d, %5d, %5d" y_h u_h v_h);
      
      if (((fabsfy_f - y_h))/y_f)<0.1 && 
          (((fabsfu_f - u_h))/u_f)<0.1 &&
          (((fabsfv_f - v_h))/v_f)<0.1 ){
        fprintff_r2b_v " pass ");
      }  
      else {
        //for debug
        fprintff_r2b_v " diff: %f, %f, %f"fabsfy_f - y_h), fabsfu_f - u_h), fabsfv_f - v_h));  
        fprintff_r2b_v " Ratio: %f, %f, %f"
                        ((fabsfy_f - y_h))/y_f), 
                        ((fabsfu_f - u_h))/u_f), 
                        ((fabsfv_f - v_h))/v_f));  
        fprintff_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;
      fori=0;i<10;i=i+1
        begin
          r = i+1)*(i+2)*(i+3)*19*23*29*414096;
          g = i+1)*(i+2)*(i+3)*17*13*31*374096;
          b = i+1)*(i+2)*(i+3)*13*11*37*414096;
          
          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
    ifreset
      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)别林斯基

posted on 2013-01-23 14:31  龙骑士_01  阅读(1594)  评论(0编辑  收藏  举报