【轉】用verilog實現RGB格式圖像到YCbCr或YUV格式的轉換及其驗證方法 (RGB2YCrCb)(RGB2YUV)


  YUV和YCbCr格式的區別

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。

  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!\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,驗證采用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: \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中

 

其它的一些腳本文件見附件。

  • 總結

討論了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設計的整個流程

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM