motionCompensation函数是根据之前得到的MV,进行运动补偿,得到当前块的预测值。
在运动估计中,得到的MV是亚像素精度的,而参考图像是整像素的,亚像素位置上是没有值的。如果通过MV找到的参考块是非整像素位置的,那么在参考图像上是没有这个参考块的。首先需要对参考图像进行插值,构造亚像素参考块。得到的这个参考块作为最终的预测块。
motionCompensation函数调用的函数如下图所示,所调用的各函数的作用如下:
- xPredInterUni 单向预测
- xPredInterBlk:非Affine模式产生运动补偿块(进行插值得到预测块)
- xPredAffineBlk:Affine模式产生运动补偿块
- xSubPuBio:当应用BDOF且不满足DMVR条件时且PU宽度或者高度大于16时,调用该函数,然后将PU分为多个子PU进行运动补偿,执行BDOF修正
- xSubPuMC:当PU是ATMVP模式时,调用该函数,将PU分为多个子PU进行运动补偿
- xPredInterBi:双向预测,即分别进行两次单向预测,然后将其进行加权。在函数内部会判断是否进行DMVR、BDOF、BCW、WP
- xWeightedPredictionBi:对于B帧,双向加权预测
- xWeightedPredictionUni:P帧,单向加权预测
- xProcessDMVR:进行DMVR细化操作
- xWeightedAverage:
- addWeightedAvg:CU级双向加权预测 BCW
- applyBiOptFlow:应用BDOF技术
void InterPrediction::motionCompensation( PredictionUnit &pu, PelUnitBuf &predBuf, const RefPicList &eRefPicList
, const bool luma, const bool chroma
, PelUnitBuf* predBufWOBIO /*= NULL*/
)
{
// Note: there appears to be an interaction with weighted prediction that
// makes the code follow different paths if chroma is on or off (in the encoder).
// Therefore for 4:0:0, "chroma" is not changed to false.
CHECK(predBufWOBIO && pu.ciipFlag, "the case should not happen!");
if (!pu.cs->pcv->isEncoder)
{
if (CU::isIBC(*pu.cu))
{
CHECK(!luma, "IBC only for Chroma is not allowed.");
xIntraBlockCopy(pu, predBuf, COMPONENT_Y);
if (chroma && isChromaEnabled(pu.chromaFormat))
{
xIntraBlockCopy(pu, predBuf, COMPONENT_Cb);
xIntraBlockCopy(pu, predBuf, COMPONENT_Cr);
}
return;
}
}
// dual tree handling for IBC as the only ref
if ((!luma || !chroma) && eRefPicList == REF_PIC_LIST_0)
{
xPredInterUni(pu, eRefPicList, predBuf, false, false, luma, chroma);
return;
}
// else, go with regular MC below
CodingStructure &cs = *pu.cs;
const PPS &pps = *cs.pps;
const SliceType sliceType = cs.slice->getSliceType();
if( eRefPicList != REF_PIC_LIST_X ) //指明了pu的参考列表,单向预测
{
CHECK(predBufWOBIO != NULL, "the case should not happen!");
if ((CU::isIBC(*pu.cu) == false) && ((sliceType == P_SLICE && pps.getUseWP()) || (sliceType == B_SLICE && pps.getWPBiPred())))
{
//使用加权预测
xPredInterUni(pu, eRefPicList, predBuf, true, false, luma, chroma);
xWeightedPredictionUni(pu, predBuf, eRefPicList, predBuf, -1, m_maxCompIDToPred, (luma && !chroma),
(!luma && chroma));//单向加权预测
}
else
{
xPredInterUni(pu, eRefPicList, predBuf, false, false, luma, chroma);//单向预测
}
}
else //双向预测
{
CHECK( !pu.cu->affine && pu.refIdx[0] >= 0 && pu.refIdx[1] >= 0 && ( pu.lwidth() + pu.lheight() == 12 ), "invalid 4x8/8x4 bi-predicted blocks" );
int refIdx0 = pu.refIdx[REF_PIC_LIST_0];
int refIdx1 = pu.refIdx[REF_PIC_LIST_1];
const WPScalingParam *wp0 = pu.cs->slice->getWpScaling(REF_PIC_LIST_0, refIdx0);
const WPScalingParam *wp1 = pu.cs->slice->getWpScaling(REF_PIC_LIST_1, refIdx1);
bool bioApplied = false;
const Slice &slice = *pu.cs->slice;
if (pu.cs->sps->getBDOFEnabledFlag() && (!pu.cs->picHeader->getDisBdofFlag()))
{
if (pu.cu->affine || m_subPuMC)
{
bioApplied = false;
}
else
{
const bool biocheck0 =
!((WPScalingParam::isWeighted(wp0) || WPScalingParam::isWeighted(wp1)) && slice.getSliceType() == B_SLICE);
const bool biocheck1 = !(pps.getUseWP() && slice.getSliceType() == P_SLICE);
if (biocheck0
&& biocheck1
&& PU::isBiPredFromDifferentDirEqDistPoc(pu)
&& (pu.Y().height >= 8)
&& (pu.Y().width >= 8)
&& ((pu.Y().height * pu.Y().width) >= 128)
)
{
bioApplied = true;
}
}
if (bioApplied && pu.ciipFlag)
{
bioApplied = false;
}
if (bioApplied && pu.cu->smvdMode)
{
bioApplied = false;
}
if (pu.cu->cs->sps->getUseBcw() && bioApplied && pu.cu->BcwIdx != BCW_DEFAULT)
{
bioApplied = false;
}
if (pu.mmvdEncOptMode == 2 && pu.mmvdMergeFlag)
{
bioApplied = false;
}
}
bool refIsScaled = ( refIdx0 < 0 ? false : pu.cu->slice->getRefPic( REF_PIC_LIST_0, refIdx0 )->isRefScaled( pu.cs->pps ) ) ||
( refIdx1 < 0 ? false : pu.cu->slice->getRefPic( REF_PIC_LIST_1, refIdx1 )->isRefScaled( pu.cs->pps ) );
bioApplied = refIsScaled ? false : bioApplied;
bool dmvrApplied = false;
dmvrApplied = (pu.mvRefine) && PU::checkDMVRCondition(pu);
// 应用BDOF且不使用DMVR且PU宽度或者高度大于16且不是ATMVP模式
if ((pu.lumaSize().width > MAX_BDOF_APPLICATION_REGION || pu.lumaSize().height > MAX_BDOF_APPLICATION_REGION) && pu.mergeType != MRG_TYPE_SUBPU_ATMVP && (bioApplied && !dmvrApplied))
{
xSubPuBio(pu, predBuf, eRefPicList, predBufWOBIO);
}
else
{
if (pu.mergeType != MRG_TYPE_DEFAULT_N && pu.mergeType != MRG_TYPE_IBC)
{
// MRG_TYPE_SUBPU_ATMVP
CHECK(predBufWOBIO != NULL, "the case should not happen!");
xSubPuMC(pu, predBuf, eRefPicList, luma, chroma);
}
else if (xCheckIdenticalMotion(pu)) //如果前后两个方向的参考poc相同,mv相同,则归为单向处理
{
xPredInterUni(pu, REF_PIC_LIST_0, predBuf, false, false, luma, chroma);
if (predBufWOBIO)
predBufWOBIO->copyFrom(predBuf, (luma && !chroma), (chroma && !luma));
}
else
{
xPredInterBi(pu, predBuf, luma, chroma, predBufWOBIO);//双向预测,其中会判断是否进行DMVR、BDOF、BCW、WP
}
}
}
return;
}
//单向预测的运动补偿算法
//xPredInterUni调用xPredInterBlk函数进行pu的每个通道的预测像素的获取
void InterPrediction::xPredInterUni(const PredictionUnit &pu, const RefPicList &eRefPicList, PelUnitBuf &pcYuvPred,
const bool &bi, const bool &bioApplied, const bool luma, const bool chroma)
{
const SPS &sps = *pu.cs->sps;
int iRefIdx = pu.refIdx[eRefPicList];
Mv mv[3];
bool isIBC = false;
CHECK( !CU::isIBC( *pu.cu ) && pu.lwidth() == 4 && pu.lheight() == 4, "invalid 4x4 inter blocks" );
if (CU::isIBC(*pu.cu))
{
isIBC = true;
}
if( pu.cu->affine ) //Affine模式
{
CHECK( iRefIdx < 0, "iRefIdx incorrect." );
mv[0] = pu.mvAffi[eRefPicList][0]; //Affine模式时运动补偿用到mv为控制点mv
mv[1] = pu.mvAffi[eRefPicList][1];
mv[2] = pu.mvAffi[eRefPicList][2];
}
else
{
mv[0] = pu.mv[eRefPicList];//非Affine模式时,运动补偿所用mv为之前merge、inter_ME等获取的
}
if( !pu.cu->affine )
{
if( !isIBC && pu.cu->slice->getRefPic( eRefPicList, iRefIdx )->isRefScaled( pu.cs->pps ) == false )
{
if( !pu.cs->pps->getWrapAroundEnabledFlag() )
{
clipMv( mv[0], pu.cu->lumaPos(), pu.cu->lumaSize(), sps, *pu.cs->pps );
}
}
}
for( uint32_t comp = COMPONENT_Y; comp < pcYuvPred.bufs.size() && comp <= m_maxCompIDToPred; comp++ )
{
const ComponentID compID = ComponentID( comp );
if (compID == COMPONENT_Y && !luma)
{
continue;
}
if (compID != COMPONENT_Y && !chroma)
{
continue;
}
if ( pu.cu->affine ) //Affine模式
{
CHECK( bioApplied, "BIO is not allowed with affine" );
m_iRefListIdx = eRefPicList;
bool genChromaMv = (!luma && chroma && compID == COMPONENT_Cb);
//Affine模式pu块的运动补偿,获取pu块的预测像素
xPredAffineBlk( compID, pu, pu.cu->slice->getRefPic( eRefPicList, iRefIdx )->unscaledPic, mv, pcYuvPred, bi, pu.cu->slice->clpRng( compID ), genChromaMv, pu.cu->slice->getScalingRatio( eRefPicList, iRefIdx ));
}
else
{
if (isIBC)
{
xPredInterBlk(compID, pu, pu.cu->slice->getPic(), mv[0], pcYuvPred, bi, pu.cu->slice->clpRng(compID),
bioApplied, isIBC);
}
else
{
//非Affine模式pu块的每个通道,进行运动补偿,获取pu预测像素
xPredInterBlk( compID, pu, pu.cu->slice->getRefPic( eRefPicList, iRefIdx )->unscaledPic, mv[0], pcYuvPred, bi, pu.cu->slice->clpRng( compID ), bioApplied, isIBC, pu.cu->slice->getScalingRatio( eRefPicList, iRefIdx ) );
}
}
}
}
//双向预测的运动补偿算法
//双向运动补偿,可以看做是分别进行前向运动补偿和后向运动补偿,分别获得前向的预测像素和后向的预测像素
//之后分情况进行加权预测
void InterPrediction::xPredInterBi(PredictionUnit &pu, PelUnitBuf &pcYuvPred, const bool luma, const bool chroma, PelUnitBuf *yuvPredTmp /*= NULL*/)
{
const PPS &pps = *pu.cs->pps;
const Slice &slice = *pu.cs->slice;
CHECK( !pu.cu->affine && pu.refIdx[0] >= 0 && pu.refIdx[1] >= 0 && ( pu.lwidth() + pu.lheight() == 12 ), "invalid 4x8/8x4 bi-predicted blocks" );
int refIdx0 = pu.refIdx[REF_PIC_LIST_0];
int refIdx1 = pu.refIdx[REF_PIC_LIST_1];
const WPScalingParam *wp0 = pu.cs->slice->getWpScaling(REF_PIC_LIST_0, refIdx0);
const WPScalingParam *wp1 = pu.cs->slice->getWpScaling(REF_PIC_LIST_1, refIdx1);
bool bioApplied = false; //BDOF应用标志
if (pu.cs->sps->getBDOFEnabledFlag() && (!pu.cs->picHeader->getDisBdofFlag()))
{
if (pu.cu->affine || m_subPuMC)
{
bioApplied = false;
}
else
{
const bool biocheck0 =
!((WPScalingParam::isWeighted(wp0) || WPScalingParam::isWeighted(wp1)) && slice.getSliceType() == B_SLICE);
const bool biocheck1 = !(pps.getUseWP() && slice.getSliceType() == P_SLICE);
if (biocheck0
&& biocheck1
&& PU::isBiPredFromDifferentDirEqDistPoc(pu)
&& (pu.Y().height >= 8)
&& (pu.Y().width >= 8)
&& ((pu.Y().height * pu.Y().width) >= 128)
)
{
bioApplied = true;
}
}
if (bioApplied && pu.ciipFlag)
{
bioApplied = false;
}
if (bioApplied && pu.cu->smvdMode)
{
bioApplied = false;
}
if (pu.cu->cs->sps->getUseBcw() && bioApplied && pu.cu->BcwIdx != BCW_DEFAULT)
{
bioApplied = false;
}
} //判断是否使用BDOF
if (pu.mmvdEncOptMode == 2 && pu.mmvdMergeFlag)
{
bioApplied = false;
}
bool dmvrApplied = false;
dmvrApplied = (pu.mvRefine) && PU::checkDMVRCondition(pu);
bool refIsScaled = ( refIdx0 < 0 ? false : pu.cu->slice->getRefPic( REF_PIC_LIST_0, refIdx0 )->isRefScaled( pu.cs->pps ) ) ||
( refIdx1 < 0 ? false : pu.cu->slice->getRefPic( REF_PIC_LIST_1, refIdx1 )->isRefScaled( pu.cs->pps ) );
dmvrApplied = dmvrApplied && !refIsScaled;
bioApplied = bioApplied && !refIsScaled;
//前后向两次单向补偿,合起来就是双向预测运动补偿
for (uint32_t refList = 0; refList < NUM_REF_PIC_LIST_01; refList++)
{
if( pu.refIdx[refList] < 0)
{
continue;
}
RefPicList eRefPicList = (refList ? REF_PIC_LIST_1 : REF_PIC_LIST_0);
CHECK(CU::isIBC(*pu.cu) && eRefPicList != REF_PIC_LIST_0, "Invalid interdir for ibc mode");
CHECK(CU::isIBC(*pu.cu) && pu.refIdx[refList] != MAX_NUM_REF, "Invalid reference index for ibc mode");
CHECK((CU::isInter(*pu.cu) && pu.refIdx[refList] >= slice.getNumRefIdx(eRefPicList)), "Invalid reference index");
m_iRefListIdx = refList;
// 单向运动补偿获得的前向和后向预测像素,存储于m_acYuvPred
PelUnitBuf pcMbBuf = ( pu.chromaFormat == CHROMA_400 ?
PelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[refList][0], pcYuvPred.Y())) :
PelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[refList][0], pcYuvPred.Y()), PelBuf(m_acYuvPred[refList][1], pcYuvPred.Cb()), PelBuf(m_acYuvPred[refList][2], pcYuvPred.Cr())) );
if (pu.refIdx[0] >= 0 && pu.refIdx[1] >= 0) //pu的前后向预测皆可用
{
if (dmvrApplied)
{
if (yuvPredTmp)
{
xPredInterUni(pu, eRefPicList, pcMbBuf, true, false, luma, chroma);//单向运动补偿
}
continue;
}
xPredInterUni(pu, eRefPicList, pcMbBuf, true, bioApplied, luma, chroma);
}
else //单向预测
{
if( ( (pps.getUseWP() && slice.getSliceType() == P_SLICE) || (pps.getWPBiPred() && slice.getSliceType() == B_SLICE) ) )
{
xPredInterUni(pu, eRefPicList, pcMbBuf, true, bioApplied, luma, chroma);
}
else
{
xPredInterUni(pu, eRefPicList, pcMbBuf, pu.cu->geoFlag, bioApplied, luma, chroma);
}
}
}
//m_acYuvPred中存储的,就是前面通过单向运动补偿获得的前向和后向预测像素
//srcPred0为前向运动补偿的预测像素,srcPred1为后向运动补偿的预测像素
CPelUnitBuf srcPred0 = ( pu.chromaFormat == CHROMA_400 ?
CPelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[0][0], pcYuvPred.Y())) :
CPelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[0][0], pcYuvPred.Y()), PelBuf(m_acYuvPred[0][1], pcYuvPred.Cb()), PelBuf(m_acYuvPred[0][2], pcYuvPred.Cr())) );
CPelUnitBuf srcPred1 = ( pu.chromaFormat == CHROMA_400 ?
CPelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[1][0], pcYuvPred.Y())) :
CPelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[1][0], pcYuvPred.Y()), PelBuf(m_acYuvPred[1][1], pcYuvPred.Cb()), PelBuf(m_acYuvPred[1][2], pcYuvPred.Cr())) );
const bool lumaOnly = luma && !chroma;
const bool chromaOnly = !luma && chroma;
// 加权预测
if( !pu.cu->geoFlag && (!dmvrApplied) && (!bioApplied) && pps.getWPBiPred() && slice.getSliceType() == B_SLICE && pu.cu->BcwIdx == BCW_DEFAULT)
{
//B帧且采用加权预测时,双向加权预测
xWeightedPredictionBi( pu, srcPred0, srcPred1, pcYuvPred, m_maxCompIDToPred, lumaOnly, chromaOnly );
if (yuvPredTmp)
{
yuvPredTmp->copyFrom(pcYuvPred);
}
}
else if( !pu.cu->geoFlag && pps.getUseWP() && slice.getSliceType() == P_SLICE )
{
//P帧且采用加权预测时,单向加权预测
xWeightedPredictionUni( pu, srcPred0, REF_PIC_LIST_0, pcYuvPred, -1, m_maxCompIDToPred, lumaOnly, chromaOnly );
if (yuvPredTmp)
{
yuvPredTmp->copyFrom(pcYuvPred);
}
}
else
{
if (dmvrApplied)
{
if (yuvPredTmp)
{
yuvPredTmp->addAvg(srcPred0, srcPred1, slice.clpRngs(), false);
}
xProcessDMVR(pu, pcYuvPred, slice.clpRngs(), bioApplied);
}
else
{
// BCW CU级双向加权预测 BDOF
xWeightedAverage( pu, srcPred0, srcPred1, pcYuvPred, slice.getSPS()->getBitDepths(), slice.clpRngs(), bioApplied, lumaOnly, chromaOnly, yuvPredTmp );
}
}
}