运动补偿的基本原理是,当编码器对图像序列中地第N帧进行处理时,利用运动补偿中的核心技术-运动估值ME(Motion Estimation),得到第N帧得预测帧N´。在实际编码传输时,并不总时传输第N帧,而是第N帧和其预测帧N´得差值△。如果运动估计十分有效,△中得概率基本上分布在零附近,从而导致△比原始图像第N帧得能量小得多,编码传输△所需得比特数也就少得多。
/*
=============Analysed by: yangxin
=============Date: 2018.10
=============Function: motionCompensation() 运动补偿
*/
void Predict::motionCompensation(const CUData& cu, const PredictionUnit& pu, Yuv& predYuv, bool bLuma, bool bChroma)
{
int refIdx0 = cu.m_refIdx[0][pu.puAbsPartIdx];
int refIdx1 = cu.m_refIdx[1][pu.puAbsPartIdx];
if (cu.m_slice->isInterP())
{
/* P Slice */
WeightValues wv0[3];
X265_CHECK(refIdx0 >= 0, "invalid P refidx\n");
X265_CHECK(refIdx0 < cu.m_slice->m_numRefIdx[0], "P refidx out of range\n");
const WeightParam *wp0 = cu.m_slice->m_weightPredTable[0][refIdx0];
MV mv0 = cu.m_mv[0][pu.puAbsPartIdx];
cu.clipMv(mv0);//--填充边界mv
if (cu.m_slice->m_pps->bUseWeightPred && wp0->bPresentFlag) //--使用加权预测
{
for (int plane = 0; plane < (bChroma ? 3 : 1); plane++)
{
wv0[plane].w = wp0[plane].inputWeight;
wv0[plane].offset = wp0[plane].inputOffset * (1 << (X265_DEPTH - 8));
wv0[plane].shift = wp0[plane].log2WeightDenom;
wv0[plane].round = wp0[plane].log2WeightDenom >= 1 ? 1 << (wp0[plane].log2WeightDenom - 1) : 0;
}
ShortYuv& shortYuv = m_predShortYuv[0];
if (bLuma)
predInterLumaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
if (bChroma)
predInterChromaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
addWeightUni(pu, predYuv, shortYuv, wv0, bLuma, bChroma);
}
else//--不加权直接补偿
{
if (bLuma)
predInterLumaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);//--帧间亮度像素预测补偿
if (bChroma)
predInterChromaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);//--帧间色度像素预测补偿
}
}
else
{
/* B Slice */
WeightValues wv0[3], wv1[3];
const WeightParam *pwp0, *pwp1;
X265_CHECK(refIdx0 < cu.m_slice->m_numRefIdx[0], "bidir refidx0 out of range\n");
X265_CHECK(refIdx1 < cu.m_slice->m_numRefIdx[1], "bidir refidx1 out of range\n");
if (cu.m_slice->m_pps->bUseWeightedBiPred)
{
pwp0 = refIdx0 >= 0 ? cu.m_slice->m_weightPredTable[0][refIdx0] : NULL;
pwp1 = refIdx1 >= 0 ? cu.m_slice->m_weightPredTable[1][refIdx1] : NULL;
if (pwp0 && pwp1 && (pwp0->bPresentFlag || pwp1->bPresentFlag))
{
/* biprediction weighting */
for (int plane = 0; plane < (bChroma ? 3 : 1); plane++)
{
wv0[plane].w = pwp0[plane].inputWeight;
wv0[plane].o = pwp0[plane].inputOffset * (1 << (X265_DEPTH - 8));
wv0[plane].shift = pwp0[plane].log2WeightDenom;
wv0[plane].round = 1 << pwp0[plane].log2WeightDenom;
wv1[plane].w = pwp1[plane].inputWeight;
wv1[plane].o = pwp1[plane].inputOffset * (1 << (X265_DEPTH - 8));
wv1[plane].shift = wv0[plane].shift;
wv1[plane].round = wv0[plane].round;
}
}
else
{
/* uniprediction weighting, always outputs to wv0 */
const WeightParam* pwp = (refIdx0 >= 0) ? pwp0 : pwp1;
for (int plane = 0; plane < (bChroma ? 3 : 1); plane++)
{
wv0[plane].w = pwp[plane].inputWeight;
wv0[plane].offset = pwp[plane].inputOffset * (1 << (X265_DEPTH - 8));
wv0[plane].shift = pwp[plane].log2WeightDenom;
wv0[plane].round = pwp[plane].log2WeightDenom >= 1 ? 1 << (pwp[plane].log2WeightDenom - 1) : 0;
}
}
}
else
pwp0 = pwp1 = NULL;
if (refIdx0 >= 0 && refIdx1 >= 0)
{
MV mv0 = cu.m_mv[0][pu.puAbsPartIdx];
MV mv1 = cu.m_mv[1][pu.puAbsPartIdx];
cu.clipMv(mv0);
cu.clipMv(mv1);
if (bLuma)
{
predInterLumaShort(pu, m_predShortYuv[0], *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
predInterLumaShort(pu, m_predShortYuv[1], *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
}
if (bChroma)
{
predInterChromaShort(pu, m_predShortYuv[0], *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
predInterChromaShort(pu, m_predShortYuv[1], *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
}
if (pwp0 && pwp1 && (pwp0->bPresentFlag || pwp1->bPresentFlag))
addWeightBi(pu, predYuv, m_predShortYuv[0], m_predShortYuv[1], wv0, wv1, bLuma, bChroma);
else
predYuv.addAvg(m_predShortYuv[0], m_predShortYuv[1], pu.puAbsPartIdx, pu.width, pu.height, bLuma, bChroma);
}
else if (refIdx0 >= 0)
{
MV mv0 = cu.m_mv[0][pu.puAbsPartIdx];
cu.clipMv(mv0);
if (pwp0 && pwp0->bPresentFlag)
{
ShortYuv& shortYuv = m_predShortYuv[0];
if (bLuma)
predInterLumaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
if (bChroma)
predInterChromaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
addWeightUni(pu, predYuv, shortYuv, wv0, bLuma, bChroma);
}
else
{
if (bLuma)
predInterLumaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
if (bChroma)
predInterChromaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
}
}
else
{
MV mv1 = cu.m_mv[1][pu.puAbsPartIdx];
cu.clipMv(mv1);
/* uniprediction to L1 */
X265_CHECK(refIdx1 >= 0, "refidx1 was not positive\n");
if (pwp1 && pwp1->bPresentFlag)
{
ShortYuv& shortYuv = m_predShortYuv[0];
if (bLuma)
predInterLumaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
if (bChroma)
predInterChromaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
addWeightUni(pu, predYuv, shortYuv, wv0, bLuma, bChroma);
}
else
{
if (bLuma)
predInterLumaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
if (bChroma)
predInterChromaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
}
}
}
}