HEVC學習(二十一) —— 熵編碼之二

本文繼續討論編碼器的初始化過程,即draft 9.3.1.1。

上一篇介紹的是各個context對應到標準相應表格的值,以及相關變量和函數,前面提到initBuffer函數對context進行初始化,但是沒有深入解析這個函數的實現,這就是本文的主要任務。

首先介紹下面討論過程中會涉及到的一個類ContextModel3DBuffer,重點關注該類的構造函數:

/// context model 3D buffer class
class ContextModel3DBuffer
{
protected:
  ContextModel* m_contextModel; ///< array of context models 
  const UInt    m_sizeX;        ///< X size of 3D buffer
  const UInt    m_sizeXY;       ///< X times Y size of 3D buffer
  const UInt    m_sizeXYZ;      ///< total size of 3D buffer
  
public:
  ContextModel3DBuffer  ( UInt uiSizeZ, UInt uiSizeY, UInt uiSizeX, ContextModel *basePtr, Int &count ); //!< 構造函數
  ~ContextModel3DBuffer () {}
  ... ...
  ... ...    
  // initialization & copy functions
  Void initBuffer( SliceType eSliceType, Int iQp, UChar* ctxModel );          ///< initialize 3D buffer by slice type & QP
  ... ...
};
ContextModel3DBuffer::ContextModel3DBuffer( UInt uiSizeZ, UInt uiSizeY, UInt uiSizeX, ContextModel *basePtr, Int &count )
: m_sizeX  ( uiSizeX )
, m_sizeXY ( uiSizeX * uiSizeY )
, m_sizeXYZ( uiSizeX * uiSizeY * uiSizeZ )
{
  // allocate 3D buffer
  m_contextModel = basePtr;	//!< m_contextModel由basePtr賦值,即指向指定的context的內存區
  count += m_sizeXYZ; 	//!< count記錄的是到目前爲止所有context的尺寸
}

下面再看類TEncSbac的構造函數,因爲實際熵編碼就是該類定義的一個對象來完成的,重點關注該類的私有數據成員是如何初始化的:

TEncSbac::TEncSbac()
// new structure here
: m_pcBitIf                   ( NULL )
, m_pcSlice                   ( NULL )
, m_pcBinIf                   ( NULL )
, m_uiCoeffCost               ( 0 )
, m_numContextModels          ( 0 ) //!< context model的計數值,接下來的所有除了assert的語句都是對句法元素對應的context進行初始化
, m_cCUSplitFlagSCModel       ( 1,             1,               NUM_SPLIT_FLAG_CTX            , m_contextModels + m_numContextModels, m_numContextModels )
, m_cCUSkipFlagSCModel        ( 1,             1,               NUM_SKIP_FLAG_CTX             , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUMergeFlagExtSCModel    ( 1,             1,               NUM_MERGE_FLAG_EXT_CTX        , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUMergeIdxExtSCModel     ( 1,             1,               NUM_MERGE_IDX_EXT_CTX         , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUPartSizeSCModel        ( 1,             1,               NUM_PART_SIZE_CTX             , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUPredModeSCModel        ( 1,             1,               NUM_PRED_MODE_CTX             , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUIntraPredSCModel       ( 1,             1,               NUM_ADI_CTX                   , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUChromaPredSCModel      ( 1,             1,               NUM_CHROMA_PRED_CTX           , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUDeltaQpSCModel         ( 1,             1,               NUM_DELTA_QP_CTX              , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUInterDirSCModel        ( 1,             1,               NUM_INTER_DIR_CTX             , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCURefPicSCModel          ( 1,             1,               NUM_REF_NO_CTX                , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUMvdSCModel             ( 1,             1,               NUM_MV_RES_CTX                , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUQtCbfSCModel           ( 1,             2,               NUM_QT_CBF_CTX                , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUTransSubdivFlagSCModel ( 1,             1,               NUM_TRANS_SUBDIV_FLAG_CTX     , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUQtRootCbfSCModel       ( 1,             1,               NUM_QT_ROOT_CBF_CTX           , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUSigCoeffGroupSCModel   ( 1,             2,               NUM_SIG_CG_FLAG_CTX           , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUSigSCModel             ( 1,             1,               NUM_SIG_FLAG_CTX              , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCuCtxLastX               ( 1,             2,               NUM_CTX_LAST_FLAG_XY          , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCuCtxLastY               ( 1,             2,               NUM_CTX_LAST_FLAG_XY          , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUOneSCModel             ( 1,             1,               NUM_ONE_FLAG_CTX              , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUAbsSCModel             ( 1,             1,               NUM_ABS_FLAG_CTX              , m_contextModels + m_numContextModels, m_numContextModels)
, m_cMVPIdxSCModel            ( 1,             1,               NUM_MVP_IDX_CTX               , m_contextModels + m_numContextModels, m_numContextModels)
, m_cCUAMPSCModel             ( 1,             1,               NUM_CU_AMP_CTX                , m_contextModels + m_numContextModels, m_numContextModels)
, m_cSaoMergeSCModel          ( 1,             1,               NUM_SAO_MERGE_FLAG_CTX   , m_contextModels + m_numContextModels, m_numContextModels)
, m_cSaoTypeIdxSCModel        ( 1,             1,               NUM_SAO_TYPE_IDX_CTX          , m_contextModels + m_numContextModels, m_numContextModels)
, m_cTransformSkipSCModel     ( 1,             2,               NUM_TRANSFORMSKIP_FLAG_CTX    , m_contextModels + m_numContextModels, m_numContextModels)
, m_CUTransquantBypassFlagSCModel( 1,          1,               NUM_CU_TRANSQUANT_BYPASS_FLAG_CTX, m_contextModels + m_numContextModels, m_numContextModels)
{
  assert( m_numContextModels <= MAX_NUM_CTX_MOD );
}

所有的私有數據成員(指m_cCUSplitFlagSCModel及其接下來的變量)都是ContextModel3DBuffer定義的對象,因此,在TEncSbac類的構造函數的成員初始化列表中,將會調用ContextModel3DBuffer類的構造函數對相應變量初始化。

接下來看initBuffer函數的實現:

/**
 * Initialize 3D buffer with respect to slicetype, QP and given initial probability table
 *
 * \param  eSliceType      slice type
 * \param  iQp             input QP value
 * \param  CtxModel      given probability table
 */
Void ContextModel3DBuffer::initBuffer( SliceType sliceType, Int qp, UChar* ctxModel )
{
  ctxModel += sliceType * m_sizeXYZ; //!< 根據當前slice的類型(I,P,B)選擇對應的context,爲什麼這麼做,下面會解釋
  
  for ( Int n = 0; n < m_sizeXYZ; n++ )
  {
    m_contextModel[ n ].init( qp, ctxModel[ n ] ); //!< 完成context的各個狀態變量的初始化工作。
    m_contextModel[ n ].setBinsCoded( 0 );
  }
}

接下來進入到m_contextModel[ n ].init( qp, ctxModel[ n ] )中去:

/**
 - initialize context model with respect to QP and initialization value
 .
 \param  qp         input QP value
 \param  initValue  8 bit initialization value
 */
Void ContextModel::init( Int qp, Int initValue )
{
  qp = Clip3(0, 51, qp);
  //!< 與draft 9.3.1.1基本呈一一對應關係
  Int  slope      = (initValue>>4)*5 - 45;	//!< m
  Int  offset     = ((initValue&15)<<3)-16; 	//!< n
  Int  initState  =  min( max( 1, ( ( ( slope * qp ) >> 4 ) + offset ) ), 126 ); 	//!< preCtxState
  UInt mpState    = (initState >= 64 ); 	//!< valMPS
  m_ucState       = ( (mpState? (initState - 64):(63 - initState)) <<1) + mpState;	//!< pStateIdx,與(9-5)式略有不同,這裏的m_ucState的值實際上是draft中pStateIdx<<1+valMPS,這麼做的目的應該是爲了節省內存
}

本文至此還剩下最後一個問題,即initBuffer函數中這麼一句:

ctxModel += sliceType * m_sizeXYZ;

這句實際上是在根據sliceType計算initType並將context指針移動到正確的位置上,在draft 9.3.1.1有這麼一段:

The variable initType in  is derived as follows:

if( slice_type = = I )
     initType = 0
else if(slice_type = = P )
     initType = cabac_init_flag ? 2 : 1
else
     initType = cabac_init_flag ? 1 : 2

這個initType用於索引context model,且由slice_type來決定。在HM中,slice_type如下定義:

/// supported slice type
enum SliceType
{
  B_SLICE,
  P_SLICE,
  I_SLICE
};

即B_SLICE,P_SLICE,I_SLICE分別爲0,1,2,這個跟上面一段有所區別(I:0, P:2, B:1,cabac_init_flag默認爲true)。

方便起見,這裏舉個實例(split_cu_flag)來說明HM是如何解決這個問題的。

// initial probability for split flag
static const UChar 
INIT_SPLIT_FLAG[3][NUM_SPLIT_FLAG_CTX] =  
{
  { 107,  139,  126, },
  { 107,  139,  126, }, 
  { 139,  141,  157, }, 
}; //!< Table 9-7

與draft Table 9-7 對照後可以發現,實際上INIT_SPLIT_FLAG保存的順序是initType=2,1,0時對應的initValue,這麼做以後,對於I_SLICE,ctxModel += 6 (I_SLICE=2, m_sizeXYZ=3),對於P_SLICE,ctxModel += 3(P_SLICE=1, m_sizeXYZ=3),對於B_SLICE,

ctxModel += 0(B_SLICE=0, m_sizeXYZ=3),而ctxModel最初指向的是INIT_SPLIT_FLAG這個數組,通過簡單的計算後我們發現,HM的做法保證了最終的結果與draft中的initType是一致的。

其餘的context,大家可以按照相同的思路進行分析,這裏就不再重複上述過程了。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章