CU级双向加权预测(Bi-prediction with CU-level weight ,BCW)
在HEVC中,通过对从两个不同参考图片获得的两个预测信号求平均和/或使用两个不同运动矢量来生成双向预测信号。 在VVC中,双向预测模式不再是简单的平均,可以对两个预测信号进行加权平均。
其中w为权重,总共包含5个权重,w∈{-2,3,4,5,10}。
对于每个双向预测的CU,权重w通过以下两种方式之一确定:
- 对于非Merge CU,权重索引在MVD之后由编码端传给解码端
- 对于Merge CU,权重索引由基于Merge候选索引从相邻块推断得到,这适用于常规Merge模式和继承的仿射Merge模式。对于构造的仿射Merge模式,基于最多3个块的运动信息构造仿射运动信息。使用构造的仿射Merge模式的CU的BCW索引被简单地设置为等于第一控制点MV的BCW索引。
BCW仅适用于具有256个或更多亮度像素的CU(即CU宽度乘以CU高度大于或等于256)。 对于低延迟(low-delay)图片,将使用所有5种权重。 对于非低延迟(non-low-delay)图片,仅使用3个权重(w∈{3,4,5})。
在编码端,为了不增加编码器的复杂性,应用快速搜索算法来查找权重索引(参考VTM软件和文档JVET-L0646):
- 当BCW与AMVR结合使用时,如果当前图片为低延迟图片,则仅针对1像素和4像素MV精度有条件地检查不相等的权重。
- 当与仿射结合使用时,仅当仿射模式被选择为当前最佳模式时,仿射运动估计才会使用不相等的权重。
- 当两个双向预测的参考帧相同时,不相等的权重仅在某些情况下使用。
- 当满足某些条件时,将不搜索不相等的权重,具体取决于当前图片及其参考图片之间的POC距离,编码QP和时间级别temporal level。
BCW权重索引使用一个context coded bin和bypass coded bins进行编码。 第一个context coded bin指示是否使用了相等的权重; 如果使用了不相等的权重,则使用bypass coded bins来指示使用了哪个不相等的权重。
在VVC中,不能将CIIP(帧间帧内联合预测)和BCW用于同一个CU。 当CU以CIIP模式编码时,当前CU的BCW索引被设置为2(w=4),即相等的权重。
const int8_t g_BcwWeights[BCW_NUM] = { -2, 3, 4, 5, 10 };
int8_t getBcwWeight(uint8_t bcwIdx, uint8_t uhRefFrmList)
{
// Weghts for the model: P0 + w * (P1 - P0) = (1-w) * P0 + w * P1
// Retuning 1-w for P0 or w for P1
return (uhRefFrmList == REF_PIC_LIST_0 ? g_BcwWeightBase - g_BcwWeights[bcwIdx] : g_BcwWeights[bcwIdx]);
}
template<>
void AreaBuf<Pel>::addWeightedAvg(const AreaBuf<const Pel> &other1, const AreaBuf<const Pel> &other2, const ClpRng& clpRng, const int8_t bcwIdx)
{
const int8_t w0 = getBcwWeight(bcwIdx, REF_PIC_LIST_0); //P0的权重
const int8_t w1 = getBcwWeight(bcwIdx, REF_PIC_LIST_1); //P1的权重
const int8_t log2WeightBase = g_BcwLog2WeightBase;
const Pel* src0 = other1.buf;
const Pel* src2 = other2.buf;
Pel* dest = buf;
const unsigned src1Stride = other1.stride;
const unsigned src2Stride = other2.stride;
const unsigned destStride = stride;
const int clipbd = clpRng.bd;
#if JVET_R0351_HIGH_BIT_DEPTH_SUPPORT
const int shiftNum = IF_INTERNAL_FRAC_BITS(clipbd) + log2WeightBase;
#else
const int shiftNum = std::max<int>(2, (IF_INTERNAL_PREC - clipbd)) + log2WeightBase;
#endif
const int offset = (1 << (shiftNum - 1)) + (IF_INTERNAL_OFFS << log2WeightBase);
#define ADD_AVG_OP( ADDR ) dest[ADDR] = ClipPel( rightShift( ( src0[ADDR]*w0 + src2[ADDR]*w1 + offset ), shiftNum ), clpRng )
#define ADD_AVG_INC \
src0 += src1Stride; \
src2 += src2Stride; \
dest += destStride; \
SIZE_AWARE_PER_EL_OP(ADD_AVG_OP, ADD_AVG_INC);
#undef ADD_AVG_OP
#undef ADD_AVG_INC
}
加权预测(WP)
加权预测(WP)是H.264 / AVC和HEVC标准支持的编码工具,可以有效地提高渐变内容编码效率,VVC也支持WP技术。 WP允许在每个参考图片列表L0和L1中为每个参考图片发信号通知加权参数(权重和偏移)。然后,在运动补偿期间,使用相应参考帧的权重和偏移。
加权预测原理:加权预测用于修正P Slice或B Slice中的运动补偿预测像素,加权预测表示预测像素可以用一个(适用于P Slice情况)或者两个(适用于B Slice情况)参考图像中的像素通过与加权系数相乘得出,如下:
其中,和分别表示参考图像1和参考图像2的重建值,w1和w2分别表示二者的权重,由编码器决定,并传给解码端。加权预测适用于两图像之间像素值整体变化且有相同变化规律的情形,如淡入、淡出等效果。
加权预测(WP)用于帧级加权,双向加权预测(BCW)用于CU级。WP和BCW设计用于处理不同类型的视频内容。为了避免WP和BCW之间的交互,这会使VVC解码器的设计复杂化,如果CU使用WP,则不会用信号通知BCW权重索引,并且将w推断为4(即应用相等的权重)。
//! set wp tables
void InterSearch::setWpScalingDistParam( int iRefIdx, RefPicList eRefPicListCur, Slice *pcSlice )
{
if ( iRefIdx<0 )
{
m_cDistParam.applyWeight = false;
return;
}
WPScalingParam *wp0 , *wp1;
m_cDistParam.applyWeight = ( pcSlice->getSliceType()==P_SLICE && pcSlice->testWeightPred() ) || ( pcSlice->getSliceType()==B_SLICE && pcSlice->testWeightBiPred() ) ;
if ( !m_cDistParam.applyWeight )
{
return;
}
int iRefIdx0 = ( eRefPicListCur == REF_PIC_LIST_0 ) ? iRefIdx : (-1); //参考帧0
int iRefIdx1 = ( eRefPicListCur == REF_PIC_LIST_1 ) ? iRefIdx : (-1); //参考帧1
getWpScaling( pcSlice, iRefIdx0, iRefIdx1, wp0 , wp1 ); //获得权重参数
if ( iRefIdx0 < 0 )
{
wp0 = NULL;
}
if ( iRefIdx1 < 0 )
{
wp1 = NULL;
}
m_cDistParam.wpCur = NULL;
if ( eRefPicListCur == REF_PIC_LIST_0 )
{
m_cDistParam.wpCur = wp0;
}
else
{
m_cDistParam.wpCur = wp1;
}
}
其中getWpScaling函数用于获得WP的权重和偏移
void WeightPrediction::getWpScaling(Slice *pcSlice, const int &iRefIdx0, const int &iRefIdx1, WPScalingParam *&wp0,
WPScalingParam *&wp1, const ComponentID maxNumComp)
{
CHECK(iRefIdx0 < 0 && iRefIdx1 < 0, "Both picture reference list indizes smaller than '0'");
const bool wpBiPred = pcSlice->getPPS()->getWPBiPred();
const bool bBiPred = (iRefIdx0 >= 0 && iRefIdx1 >= 0);
const bool bUniPred = !bBiPred;
if (bUniPred || wpBiPred)
{
// explicit -------------------- 显性WP
wp0 = pcSlice->getWpScaling(REF_PIC_LIST_0, iRefIdx0);
wp1 = pcSlice->getWpScaling(REF_PIC_LIST_1, iRefIdx1);
}
else
{
THROW( "Unsupported WP configuration" );
}
if (iRefIdx0 < 0)
{
wp0 = nullptr;
}
if (iRefIdx1 < 0)
{
wp1 = nullptr;
}
const uint32_t numValidComponent = getNumberValidComponents(pcSlice->getSPS()->getChromaFormatIdc());
const bool bUseHighPrecisionPredictionWeighting = pcSlice->getSPS()->getSpsRangeExtension().getHighPrecisionOffsetsEnabledFlag();
if (bBiPred) //双向预测
{
// Bi-predictive case
for (int yuv = 0; yuv < numValidComponent && yuv <= maxNumComp; yuv++)
{
const int bitDepth = pcSlice->getSPS()->getBitDepth(toChannelType(ComponentID(yuv)));
const int offsetScalingFactor = bUseHighPrecisionPredictionWeighting ? 1 : (1 << (bitDepth - 8));
wp0[yuv].w = wp0[yuv].codedWeight;
wp1[yuv].w = wp1[yuv].codedWeight;
wp0[yuv].o = wp0[yuv].codedOffset * offsetScalingFactor;
wp1[yuv].o = wp1[yuv].codedOffset * offsetScalingFactor;
wp0[yuv].offset = wp0[yuv].o + wp1[yuv].o;
wp0[yuv].shift = wp0[yuv].log2WeightDenom + 1;
wp0[yuv].round = (1 << wp0[yuv].log2WeightDenom);
wp1[yuv].offset = wp0[yuv].offset;
wp1[yuv].shift = wp0[yuv].shift;
wp1[yuv].round = wp0[yuv].round;
}
}
else
{
// UniPred
WPScalingParam *const pwp = (iRefIdx0 >= 0) ? wp0 : wp1;
for (int yuv = 0; yuv < numValidComponent && yuv <= maxNumComp; yuv++)
{
const int bitDepth = pcSlice->getSPS()->getBitDepth(toChannelType(ComponentID(yuv)));
const int offsetScalingFactor = bUseHighPrecisionPredictionWeighting ? 1 : (1 << (bitDepth - 8));
pwp[yuv].w = pwp[yuv].codedWeight;
pwp[yuv].offset = pwp[yuv].codedOffset * offsetScalingFactor;
pwp[yuv].shift = pwp[yuv].log2WeightDenom;
pwp[yuv].round = (pwp[yuv].log2WeightDenom >= 1) ? (1 << (pwp[yuv].log2WeightDenom - 1)) : (0);
}
}
}