• 用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)别林斯基

  • 相关阅读:
    记录我发现的第一个关于 Google 的 Bug
    iOS 中的 Delayed Transition
    Appstore|IPA
    地图|定位
    开发者账号
    App跳转
    国际化
    短信|彩信
    闪光灯
    Cornerstone|SVN
  • 原文地址:https://www.cnblogs.com/zcf287/p/2873086.html
Copyright © 2020-2023  润新知