本文繼續討論編碼器的初始化過程,即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,大家可以按照相同的思路進行分析,這裏就不再重複上述過程了。