HEVC加權預測(Weight Prediction)原理兼解碼代碼

HEVC添加加權預測模式的原因:

現今很多視頻中都採用的一種技術:場景隨着時間光線強弱漸變(temporal illumination variation),或者相同場景,出現陰影效應。這樣的視頻,幀與幀之間,背景的相似度可能很高,明暗差別較大,若單純採用運動預測,運動補償技術,得到的殘差會不夠理想。此時,有人注意到,出現temporal illumination variation現象,相鄰幀(或者相鄰Slice)之間的像素值整體上都是漸變的(shape),逐漸變大或變小,相對前面的圖像幀,當前幀相當於整體乘了一個數(weight),再加上一個偏移量(offset),編碼效果就會大大改善。這就是加權預測。

加權預測原理解析

加權預測在H.264已經被提出來並且得到應用。加權預測應用於運動估計之後,它是以Slice爲單位,每一幀的每一個Slice都需要單獨地傳輸相應的加權預測參數。對與當前解碼圖像,它有兩個參考鏈表L0和L1,編碼端需要給L0和L1中的每一個參考圖像的YUV三個分量分別傳輸相應的加權預測參數。 
在H.264中,加權預測存在兩種模式,分別是explicit WP (EWP) 和 implicit WP(IWP)。 
IWP模式的加權預測參數都是固定的,編解碼端約定好採用相同的加權預測參數,不需要編碼端傳輸,減少了碼流傳輸的壓力,提高傳輸效率。 
由於IWP模式的參數都是固定的,所以它不能用於單向預測,因爲當前幀跟參考幀的距離是變化的,導致權重會相差較大,採用固定的權重,效果不理想。所以在HEVC中,不存在IWP模式,只採用EWP模式,下面分析EWP模式。 
Explicit WP: 
EWP模式的加權預測參數是由編碼端決定的,解碼端需要從碼流中讀取得到相應的加權預測參數。採用EWP模式,編碼端可以根據具體情況,靈活採用不同的參數。 
加權預測共有三個預測參數,分別是:weight(權重), offset(偏移量),LDW(log weight denominator)。具體計算方式如下式: 

    (1) 

    (2) 
上面兩個式子,(1)式用於單向預測,(2)用於B幀的雙向預測。P爲像素預測值,W爲權重,Q爲偏移量,LX爲參考鏈表L0或者L1。在編碼端,爲了避免浮點運算,需要將權重放大,放大的倍數爲2的LWD次方,LWD的取值範圍爲[0,7]。解碼端則縮小相應的倍數,是爲了四捨五入。Clip()是控制像素值在有效值之內,如8比特的有效值爲[0,255]。 
大致流程如下圖所示(轉): 

優化:

採用EWP模式,不管視頻有沒有光照衰弱效應,也就是Weight Prediction 有無壓縮效果的情況下,編碼端都需要將加權預測參數傳輸到解碼端。此時: 
W = (1<<LWD), Offset = 0。代入(1)式跟(2)式,可以發現,此時相當於沒采用加權預測的效果。所以我們需要對加權預測參數進行優化,減少傳輸。

(1)因爲L0和L1的每個參考圖像都有自己的加權預測參數,可以發掘他們之間的關係,減少傳輸次數。 
(2)每張圖像的YUV三個分量都需要傳輸自己的加權預測參數,色度分量可以以亮度分量爲預測值,減少傳輸大小。

代碼


//!< 加權預測參數結構體
struct WPScalingParam
{
// Explicit weighted prediction parameters parsed in slice header,
// or Implicit weighted prediction parameters (8 bits depth values).
Bool bPresentFlag; // 圖像的亮度或色度是否存在加權因子
UInt uiLog2WeightDenom; // 爲避免浮點運算,加權因子整數化時放大的倍數
Int iWeight; // 加權因子
Int iOffset; // 偏移量

// Weighted prediction scaling values built from above parameters (bitdepth scaled):
Int w; // 權重
Int o;
Int offset; // 偏移量
Int shift; // 加權因子整數化是放大的倍數log2
Int round; // 根據放大倍數,用於四捨五入
};

/** parse explicit wp tables
*讀取加權預測參數
*解碼端的哥倫布解碼
*/

Void TDecCavlc::xParsePredWeightTable( TComSlice* pcSlice )
{
WPScalingParam *wp;
TComSPS *sps = pcSlice->getSPS();
const ChromaFormat chFmt = sps->getChromaFormatIdc();
const Int numValidComp = Int(getNumberValidComponents(chFmt));
const Bool bChroma = (chFmt!=CHROMA_400);
const SliceType eSliceType = pcSlice->getSliceType();
const Int iNbRef = (eSliceType == B_SLICE ) ? (2) : (1);
UInt uiLog2WeightDenomLuma=0, uiLog2WeightDenomChroma=0; //加權預測過程中,因子放大的倍數
UInt uiTotalSignalledWeightFlags = 0;

Int iDeltaDenom;
// decode delta_luma_log2_weight_denom :
READ_UVLC( uiLog2WeightDenomLuma, "luma_log2_weight_denom" ); // ue(v): luma_log2_weight_denom
assert( uiLog2WeightDenomLuma <= 7 );
if( bChroma )
{
READ_SVLC( iDeltaDenom, "delta_chroma_log2_weight_denom" ); // se(v): delta_chroma_log2_weight_denom
assert((iDeltaDenom + (Int)uiLog2WeightDenomLuma)>=0);
assert((iDeltaDenom + (Int)uiLog2WeightDenomLuma)<=7);
uiLog2WeightDenomChroma = (UInt)(iDeltaDenom + uiLog2WeightDenomLuma);
}

for ( Int iNumRef=0 ; iNumRef<iNbRef ; iNumRef++ )
{
RefPicList eRefPicList = ( iNumRef ? REF_PIC_LIST_1 : REF_PIC_LIST_0 );
for ( Int iRefIdx=0 ; iRefIdx<pcSlice->getNumRefIdx(eRefPicList) ; iRefIdx++ )
{
pcSlice->getWpScaling(eRefPicList, iRefIdx, wp);

wp[COMPONENT_Y].uiLog2WeightDenom = uiLog2WeightDenomLuma;
for(Int j=1; j<numValidComp; j++)
{
wp[j].uiLog2WeightDenom = uiLog2WeightDenomChroma;
}

UInt uiCode;
READ_FLAG( uiCode, "luma_weight_lX_flag" ); // u(1): luma_weight_l0_flag
wp[COMPONENT_Y].bPresentFlag = ( uiCode == 1 ); //!< L0或L1 隊列中的第iRefidx幅圖像是否存在亮度加權因子
uiTotalSignalledWeightFlags += wp[COMPONENT_Y].bPresentFlag;
}
if ( bChroma )
{
UInt uiCode;
for ( Int iRefIdx=0 ; iRefIdx<pcSlice->getNumRefIdx(eRefPicList) ; iRefIdx++ )
{
pcSlice->getWpScaling(eRefPicList, iRefIdx, wp);
READ_FLAG( uiCode, "chroma_weight_lX_flag" ); // u(1): chroma_weight_l0_flag
for(Int j=1; j<numValidComp; j++)
{
wp[j].bPresentFlag = ( uiCode == 1 ); //!< L0或L1隊列中第iRefIdx幅圖像是否存在色度加權因子
}
uiTotalSignalledWeightFlags += 2*wp[COMPONENT_Cb].bPresentFlag;
}
}
for ( Int iRefIdx=0 ; iRefIdx<pcSlice->getNumRefIdx(eRefPicList) ; iRefIdx++ )
{
pcSlice->getWpScaling(eRefPicList, iRefIdx, wp);
if ( wp[COMPONENT_Y].bPresentFlag ) //!< 當前參考圖像是否存在亮度加權因子
{
Int iDeltaWeight;
//!< 因爲加權因子爲小數,傳輸的是放大後的加權因子與放大倍數的差值,可提高傳輸效率
//!< iDeltaWeight = iWeight - 1<<log2WeightDenom,
READ_SVLC( iDeltaWeight, "delta_luma_weight_lX" ); // se(v): delta_luma_weight_l0[i],計算亮度加權因子(差值)
assert( iDeltaWeight >= -128 );
assert( iDeltaWeight <= 127 );
wp[COMPONENT_Y].iWeight = (iDeltaWeight + (1<<wp[COMPONENT_Y].uiLog2WeightDenom));
READ_SVLC( wp[COMPONENT_Y].iOffset, "luma_offset_lX" ); // se(v): luma_offset_l0[i],計算亮度偏移量(差值)
Int range=sps->getUseHighPrecisionPredictionWeighting() ? (1<<g_bitDepth[CHANNEL_TYPE_LUMA])/2 : 128;
assert( wp[0].iOffset >= -range );
assert( wp[0].iOffset < range );
}
else
{
wp[COMPONENT_Y].iWeight = (1 << wp[COMPONENT_Y].uiLog2WeightDenom);
wp[COMPONENT_Y].iOffset = 0;
}
if ( bChroma )
{
if ( wp[COMPONENT_Cb].bPresentFlag ) //!, 當前參考圖像是否存在色度加權因子
{
Int range=sps->getUseHighPrecisionPredictionWeighting() ? (1<<g_bitDepth[CHANNEL_TYPE_CHROMA])/2 : 128;
for ( Int j=1 ; j<numValidComp ; j++ )
{
Int iDeltaWeight;
READ_SVLC( iDeltaWeight, "delta_chroma_weight_lX" ); // se(v): chroma_weight_l0[i][j],計算色度加權因子(差值)
assert( iDeltaWeight >= -128 );
assert( iDeltaWeight <= 127 );
wp[j].iWeight = (iDeltaWeight + (1<<wp[j].uiLog2WeightDenom));

Int iDeltaChroma;
READ_SVLC( iDeltaChroma, "delta_chroma_offset_lX" ); // se(v): delta_chroma_offset_l0[i][j]
assert( iDeltaChroma >= -4*range);
assert( iDeltaChroma < 4*range);
Int pred = ( range - ( ( range*wp[j].iWeight)>>(wp[j].uiLog2WeightDenom) ) ); //!< 計算此值也是爲了減少傳輸耗費
wp[j].iOffset = Clip3(-range, range-1, (iDeltaChroma + pred) );
}
}
else
{
for ( Int j=1 ; j<numValidComp ; j++ )
{
wp[j].iWeight = (1 << wp[j].uiLog2WeightDenom);
wp[j].iOffset = 0;
}
}
}
}

for ( Int iRefIdx=pcSlice->getNumRefIdx(eRefPicList) ; iRefIdx<MAX_NUM_REF ; iRefIdx++ )
{
pcSlice->getWpScaling(eRefPicList, iRefIdx, wp);

wp[0].bPresentFlag = false;
wp[1].bPresentFlag = false;
wp[2].bPresentFlag = false;
}
}
assert(uiTotalSignalledWeightFlags<=24);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章