数据压缩实验[4] H264参考编码器模式选择及运动矢量信息

实验内容

  1. 熟悉H.264参考编码器JM software编码参数(本文采用JM18.6);
  2. 输出foreman_qcif序列第0、1帧指定宏块如下信息:
    • 编码模式(mode);
    • 运动矢量(MV);
    • 量化参数(QP);

H264参考编码器参数设置

JM编码器的参数通过cfg文件设置,运行编码器时调用配置文件的命令(以Windows平台的vs项目为例)为:

-d [配置文件路径]

示例:
请添加图片描述

配置文件encoder.cfg的关键参数为:

InputFile             = "foreman_part_qcif.yuv"       # Input sequence
                                                      # 输入序列,为420采样的YUV文件
StartFrame            = 0      # Start frame for encoding. (0-N)
                               # 起始帧号
FramesToBeEncoded     = 3      # Number of frames to be coded
                               # 编码的帧数
FrameRate             = 30.0   # Frame Rate per second (0.1-100.0)
SourceWidth           = 176    # Source frame width
SourceHeight          = 144    # Source frame height
                               # 输入图像尺寸
SourceResize          = 0      # Resize source size for output
OutputWidth           = 176    # Output frame width
OutputHeight          = 144    # Output frame height
                               # 输出图像尺寸

TraceFile             = "trace_enc.txt"      # Trace file 
ReconFile             = "test_rec.yuv"       # Recontruction YUV file
OutputFile            = "test.264"           # Bitstream
StatsFile             = "stats.dat"          # Coding statistics file

IntraPeriod           = 0   # Period of I-pictures   (0=only first)
                            # Intra帧周期(I帧,不表示一个GOP结束)
IDRPeriod             = 0   # Period of IDR pictures (0=only first)
                            # Intra IDR帧周期(I帧,表示一个GOP结束)
                            # 该选项确定了一个GOP的长度                            
EnableIDRGOP          = 1   # Support for IDR closed GOPs (0: disabled, 1: enabled)
                            # 使用对IDR封闭的GOP

QPISlice              = 28  # Quant. param for I Slices (0-51)
QPPSlice              = 28  # Quant. param for P Slices (0-51)
                            # 在不启用码率控制时,该选项指定
                            # I帧与P帧的QP

NumberBFrames          = 1  # Number of B coded frames inserted (0=not used)
                            # B帧数,与IntraPeriod、IDRPeriod共同确定GOP结构
QPBSlice               = 30 # Quant. param for B slices (0-51)
                            # B帧的QP

RateControlEnable       = 0     # 0 Disable, 1 Enable
                                # 禁用码率控制 

编码模式与运动预测信息输出

Trace文件分析

JM编码器自带trace功能,启用trace即可生成trace文件输出编码过程中的关键参数。启用条件如下:

  1. 在cfg配置文件中TraceFile指定输出的文件名;
  2. 在defines.h文件中将TRACE的宏定义为1;
    #if defined _DEBUG
    # define TRACE           1     
    #else
    # define TRACE           0      
    #endif
    

运行程序对3帧的实验序列foreman_part_qcif.yuv进行编码,查看Trace文件获得第2行第3个宏块的mode、MV、QP等信息:

对于第一帧I帧

请添加图片描述

通过计算可知该宏块编号为13,在Trace文件中找到如下内容:

*********** Pic: 0 (I/P) MB: 13 Slice: 0 **********

@4456  mb_type (I_SLICE) ( 2, 1) =   9                                 (  0) 
@4456  Intra 4x4 mode  =   7 (context: 0)                              (  7) 
@4458  Intra 4x4 mode  =   7 (context: 1)                              (  7) 
@4460  Intra 4x4 mode  = predicted (context: 2)                        ( -1) 
@4461  Intra 4x4 mode  = predicted (context: 3)                        ( -1) 
@4463  Intra 4x4 mode  =   5 (context: 4)                              (  5) 
@4467  Intra 4x4 mode  =   7 (context: 5)                              (  7) 
@4468  Intra 4x4 mode  =   7 (context: 6)                              (  7) 
@4470  Intra 4x4 mode  = predicted (context: 7)                        ( -1) 
@4471  Intra 4x4 mode  =   2 (context: 8)                              (  2) 
@4478  Intra 4x4 mode  =   7 (context: 9)                              (  7) 
@4480  Intra 4x4 mode  = predicted (context: 10)                       ( -1) 
@4481  Intra 4x4 mode  = predicted (context: 11)                       ( -1) 
@4482  Intra 4x4 mode  = predicted (context: 12)                       ( -1) 
@4483  Intra 4x4 mode  = predicted (context: 13)                       ( -1) 
@4484  Intra 4x4 mode  = predicted (context: 14)                       ( -1) 
@4485  Intra 4x4 mode  =   6 (context: 15)                             (  6) 
@4490  intra_chroma_pred_mode                                          (  0) 
@4490  CBP ( 2, 1) =  23                                               ( 23) 
@4497  Delta QP ( 2, 1) =   0                                          (  0) 
@4497  Luma4x4 sng( 0) level = -1 run = 0                              ( -1) 
@4497  Luma4x4 sng( 1) level =  1 run = 1                              (  1) 
@4497  Luma4x4 sng( 2) level = -1 run = 5                              ( -1) 
(此处省略熵编码相关信息)
@4671  DC Chroma  0: level =  1 run = 0                                (  1) 
@4671  DC Chroma  1: level =  0 run = 1                                (  0) 
(此处省略熵编码相关信息)
      CABAC terminating bit = 0

程序中对于各个模式的定义如下:

//  Available MB modes
enum {
  PSKIP        =  0,
  BSKIP_DIRECT =  0,
  P16x16       =  1,
  P16x8        =  2,
  P8x16        =  3,
  SMB8x8       =  4,
  SMB8x4       =  5,
  SMB4x8       =  6,
  SMB4x4       =  7,
  P8x8         =  8,
  I4MB         =  9,
  I16MB        = 10,
  IBLOCK       = 11,
  SI4MB        = 12,
  I8MB         = 13,
  IPCM         = 14,
  MAXMODE      = 15
} MBModeTypes;

对各个帧内预测模式的规定如下:

// 4x4 intra prediction modes 
enum {
  VERT_PRED            = 0,
  HOR_PRED             = 1,
  DC_PRED              = 2,
  DIAG_DOWN_LEFT_PRED  = 3,
  DIAG_DOWN_RIGHT_PRED = 4,
  VERT_RIGHT_PRED      = 5,
  HOR_DOWN_PRED        = 6,
  VERT_LEFT_PRED       = 7,
  HOR_UP_PRED          = 8
} I4x4PredModes;

// 16x16 intra prediction modes
enum {
  VERT_PRED_16   = 0,
  HOR_PRED_16    = 1,
  DC_PRED_16     = 2,
  PLANE_16       = 3
} I16x16PredModes;

// 8x8 chroma intra prediction modes
enum {
  DC_PRED_8     =  0,
  HOR_PRED_8    =  1,
  VERT_PRED_8   =  2,
  PLANE_8       =  3
} I8x8PredModes;

H264标准中对帧内预测模式的定义如下:
请添加图片描述

以上信息中:

  • mb_type字段表明该宏块使用的mode;
  • Intra 4x4 mode字段表明对于4x4宏块使用的帧内预测mode选择;
    • 其中predicted模式表明该块的预测模式由相邻块预测得到。

仔细观察发现非predicated的块输出结果的模式均比实际块的模式小1(参考上图码率分析软件结果和输出结果,如第一个块采用HOR_UP_PRED模式,结果应该为8但实际输出7),其原因是因为编码器中存在如下语句:

currMB->intra_pred_modes[4*b8+b4] = (char) mostProbableMode == best_ipmode ? -1 : (best_ipmode < mostProbableMode ? best_ipmode : best_ipmode-1)

当最优模式大于预测得到的模式时,模式的编号将被-1。

此外,在TRACE文件中并未输出QP,但该参数在后续的代码分析中可以看到。
实验采用的QP在之前的配置文件中设置为28。

对于第二帧P帧

该帧编码顺序为第2帧,显示顺序为第3帧!
请添加图片描述

Trace文件输出:

*********** Pic: 2 (I/P) MB: 13 Slice: 0 **********

@21949 mb_skip_flag                                                    (  0) 
      CABAC terminating bit = 0

表明该MB采用P_SKIP编码。

对于第三帧B帧

该帧编码顺序为第3帧,显示顺序为第2帧!

Trace文件输出:

*********** Pic: 1 (I/P) MB: 13 Slice: 0 **********

@29408 mb_skip_flag                                                    (  0) 
      CABAC terminating bit = 0

表明该MB采用BSKIP_DIRECT编码。

程序分析

存储macroblock关键信息的结构体中需要关注的点:

//! Macroblock
typedef struct macroblock_enc
{
  //  省略其它信息
  int                 mbAddrX;                    //!< current MB address
  short               mb_type;                    //!< current MB mode type
  short               mb_x;                       //!< current MB horizontal
  short               mb_y;                       //!< current MB vertical

  short               qp;                         //!< QP luma  
  short               qpc[2];                     //!< QP chroma

  short               mvd[2][BLOCK_MULTIPLE][BLOCK_MULTIPLE][2];          
                      //!< indices correspond to [list][block_y][block_x][x,y]
 
  char                i16mode;
  Info8x8             b8x8[4];    
  
  byte                write_mb;        // 为1表明该MB当前进行正式编码
  byte                is_intra_block;

  int                 skip_flag;

  char                intra_pred_modes   [MB_BLOCK_PARTITIONS];
  char                intra_pred_modes8x8[MB_BLOCK_PARTITIONS];           
                      //!< four 8x8 blocks in a macroblock
  
  short               best_mode;
  char                best_i16mode;
  //  省略其它信息
} Macroblock;

由于编码器执行mode decision需要进行预编码计算代价,在预编码过程中宏块的信息会不断改变。当write_mb==1 时表明该宏块的相关参数已经确定,为最终输出结果。

帧内编码相关

程序计算各个帧内编码相关代价后,best_mode将指示当前的模式(模式编号见上述定义),确定采用I16x16、I8x8或I4x4编码模式,其中:

  • 对于I16x16,使用best_i16mode指示最佳编码模式;
  • 对于I8x8,使用intra_pred_modes8x8指示16个4x4子块的编码模式,调用writeIntra8x8Modes()写入相关信息;
  • 对于I4x4,使用intra_pred_modes指示16个4x4子块的编码模式,调用writeIntra4x4Modes()写入相关信息;

对于第2行第3个宏块,可在程序中观察到mode和QP,与Trace文件输出一致:
请添加图片描述

帧间编码相关(P帧为例)

程序计算各个帧内编码相关代价后,best_mode将指示当前16x16宏块的模式(模式编号见上述定义),如果采用了小于等于8x8的宏块划分,则b8x8将依次指示4个8x8子块的划分方式。

write_p_slice_motion_info_to_NAL()函数将对当前currMB的运动信息进行编码,其中的两个for循环会依次遍历每个4x4的子宏块:

    //===== write forward motion vectors =====
    for (j0=0; j0<4; j0+=step_v0)
    {
      for (i0=0; i0<4; i0+=step_h0)
      {
        k = j0 + (i0 >> 1);
        if (currMB->b8x8[k].mode !=0 && (currMB->b8x8[k].pdir == 0 || currMB->b8x8[k].pdir == 2))//has forward vector
        {

          no_bits  += writeMotionVector8x8 (currMB, i0, j0, i0 + step_h0, j0 + step_v0, motion[currMB->block_y + j0][currMB->block_x + i0].ref_idx[LIST_0], 
            LIST_0, currMB->b8x8[k].mode, currMB->b8x8[k].bipred);
        }
      }
    }

其中:

  • i0指示水平方向4x4块的起始坐标,i1指示水平4x4块的结束坐标;
  • j0指示垂直方向4x4块的起始坐标,j1指示垂直4x4块的结束坐标;

比如,当前16x16宏块的最佳划分模式为P8x8,需要对每个8x8的块(包含4个4x4块,位置由i0 i1 j0 j1指示)调用writeMotionVector8x8()进一步确定每个4x4块运动矢量。

writeMotionVector8x8()函数将计算每个4x4小块的运动矢量,该函数中:

  • cur_mv->mv_x、cur_mv->mv_y指示实际运动矢量;
  • predMV.mv_x、predMV.mv_y指示预测运动矢量;

计算得到的运动矢量差值mvd被保存在currMB->mvd[list][88index][44index]当中。

以编码顺序第二帧第0个宏块为例:
请添加图片描述

Trace输出:

*********** Pic: 2 (I/P) MB: 0 Slice: 0 **********

@21136 mb_skip_flag                                                    (  1) 
@21137 mb_type (P_SLICE) ( 0, 0) =   8                                 (  4) 
@21140 8x8 mode/pdir( 0) =   7/0                                       (  3) 
@21144 8x8 mode/pdir( 1) =   4/0                                       (  0) 
@21145 8x8 mode/pdir( 2) =   6/0                                       (  2) 
@21147 8x8 mode/pdir( 3) =   4/0                                       (  0) 
@21148 mvd_l0 (0) =   0  (org_mv   0 pred_mv   0)                      (  0) 
@21149 mvd_l0 (1) =   0  (org_mv   0 pred_mv   0)                      (  0) 
@21150 mvd_l0 (0) =   0  (org_mv   0 pred_mv   0)                      (  0) 
@21151 mvd_l0 (1) =   0  (org_mv   0 pred_mv   0)                      (  0) 
@21151 mvd_l0 (0) =  -1  (org_mv  -1 pred_mv   0)                      ( -1) 
@21154 mvd_l0 (1) =  -3  (org_mv  -3 pred_mv   0)                      ( -3) 
@21160 mvd_l0 (0) =  -3  (org_mv  -3 pred_mv   0)                      ( -3) 
@21166 mvd_l0 (1) =   0  (org_mv   0 pred_mv   0)                      (  0) 
@21167 mvd_l0 (0) =   0  (org_mv   0 pred_mv   0)                      (  0) 
@21168 mvd_l0 (1) =   0  (org_mv   0 pred_mv   0)                      (  0) 
@21169 mvd_l0 (0) =   1  (org_mv   0 pred_mv  -1)                      (  1) 
@21172 mvd_l0 (1) =  -1  (org_mv  -1 pred_mv   0)                      ( -1) 
@21175 mvd_l0 (0) =  -3  (org_mv  -3 pred_mv   0)                      ( -3) 
@21180 mvd_l0 (1) =   1  (org_mv   1 pred_mv   0)                      (  1) 
@21183 mvd_l0 (0) =  -1  (org_mv  -4 pred_mv  -3)                      ( -1) 
@21186 mvd_l0 (1) =   1  (org_mv   1 pred_mv   0)                      (  1) 
@21189 CBP ( 0, 0) =   1                                               (  1) 
@21193 Delta QP ( 0, 0) =   0                                          (  0) 
@21194 Luma4x4 sng( 0) level =  0 run = 0                              (  0) 
@21195 Luma4x4 sng( 0) level =  0 run = 0                              (  0) 
@21196 Luma4x4 sng( 0) level =  1 run = 1                              (  1) 
@21196 Luma4x4 sng( 1) level = -2 run = 4                              ( -2) 
@21196 Luma4x4 sng( 2) level =  1 run = 2                              (  1) 
@21196 Luma4x4 sng( 3) level =  0 run = 1                              (  0) 
@21216 Luma4x4 sng( 0) level =  0 run = 1                              (  0) 
      CABAC terminating bit = 0

程序调试:
请添加图片描述

通过对分析,该16x16宏块模式为P8x8:

  • 第一个8x8块使用SMB4x4划分,四个子宏块运动矢量分别为:
    • (0,0), (0,0), (-1,-3), (-3,0);
  • 第二个8x8块使用SMB8x8划分,运动矢量为:
    • (0,0);
  • 第三个8x8块使用SMB4x8划分,二个子宏块运动矢量分别为:
    • (0,-1), (-3,1);
  • 第四个8x8块使用SMB8x8划分,运动矢量为:
    • (-4,1);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值