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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章