VTM10.0程式碼學習8:xReconInter()

柴門風雪夜發表於2020-12-07

此係列是為了記錄自己學習VTM10.0的過程和鍛鍊表達能力,主要是從解碼端進行入手。由於本人水平有限,出現的錯誤懇請大家指正,歡迎與大家一起交流進步。


上一篇博文(VTM10.0程式碼學習7)的第1大節提到xReconInter(),這個函式是用來重構幀間模式或IBC模式下的CU塊,本篇博文就來細說它。


1. xReconInter()

if( cu.geoFlag )//如果當前CU開啟GPM
{
    m_pcInterPred->motionCompensationGeo( cu, m_geoMrgCtx );//在開啟GPM下進行運動補償
    PU::spanGeoMotionInfo( *cu.firstPU, m_geoMrgCtx, cu.firstPU->geoSplitDir, cu.firstPU->geoMergeIdx0, cu.firstPU->geoMergeIdx1 );//向cs中加入當前PU對應的MotionInfo
}
else
{
    m_pcIntraPred->geneIntrainterPred(cu);//進行CIIP中的幀內預測部分

    // inter prediction
    const bool luma = cu.Y().valid();//為1表示當前CU的亮度分量存在
    const bool chroma = isChromaEnabled(cu.chromaFormat) && cu.Cb().valid();//為1表示當前CU的色度分量存在
    //進行運動補償,獲取幀間預測值
    if (luma && (chroma || !isChromaEnabled(cu.chromaFormat)))
    {
        m_pcInterPred->motionCompensation(cu);
    }
    else//在IBC的情況下會進入else
    {
        m_pcInterPred->motionCompensation(cu, REF_PIC_LIST_0, luma, chroma);
    }
}

這段是用來進行運動補償,獲取當前CU的幀間預測值。

第一個if分支:如果判斷條件為真,表示在開啟GPM的情況下進行運動補償,這裡不再展開;判斷條件為假進入非GPM情況下的運動補償。

geneIntrainterPred():用來進行CIIP中的幀內預測部分

luma:為1表示當前CU的亮度分量存在

chroma:為1表示當前CU的色度分量存在

第二個if分支:如果當前CU亮度不存在,或在視訊源不是單通道的情況下色度不存在,表示當前CU是IBC預測。此時指明是list 0的單向預測,並將luma和chroma變數傳入函式motionCompensation()。如果判斷條件為真(IBC預測或幀間預測),就直接呼叫函式motionCompensation()進行運動補償,此時luma和chroma預設為1。有關函式motionCompensation()的講解,參考第2大節


if (cu.Y().valid())
{
    bool isIbcSmallBlk = CU::isIBC(cu) && (cu.lwidth() * cu.lheight() <= 16);
    CU::saveMotionInHMVP( cu, isIbcSmallBlk );//更新HMVP候選列表
}

如果當前CU不是亮度畫素點數小於等於16的開啟IBC預測的CU,就更新HMVP候選列表


if (cu.firstPU->ciipFlag)//當前CU開啟CIIP模式
{
    if (cu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())//如果開啟LMCS
    {
        cu.cs->getPredBuf(*cu.firstPU).Y().rspSignal(m_pcReshape->getFwdLUT());//進行LMCS中的luma mapping
    }
    m_pcIntraPred->geneWeightedPred(COMPONENT_Y, cu.cs->getPredBuf(*cu.firstPU).Y(), *cu.firstPU, m_pcIntraPred->getPredictorPtr2(COMPONENT_Y, 0));//進行亮度分量的CIIP的幀內幀間預測加權
    if (isChromaEnabled(cu.chromaFormat) && cu.chromaSize().width > 2)
    {
        m_pcIntraPred->geneWeightedPred(COMPONENT_Cb, cu.cs->getPredBuf(*cu.firstPU).Cb(), *cu.firstPU, m_pcIntraPred->getPredictorPtr2(COMPONENT_Cb, 0));
        m_pcIntraPred->geneWeightedPred(COMPONENT_Cr, cu.cs->getPredBuf(*cu.firstPU).Cr(), *cu.firstPU, m_pcIntraPred->getPredictorPtr2(COMPONENT_Cr, 0));
    }
}

這段用來進行CIIP的幀內幀間預測加權

如果開啟LMCS,還要對幀間預測值進行luma mapping。如果對這裡進行luma mapping有疑惑的話,推薦看一下JVET-S2002 3.7.3小節的開頭以及圖54。


xDecodeInterTexture(cu);//獲得當前CU的畫素殘差值

具體參考1.1小節


//下面進行獲取畫素重建值
if (cu.rootCbf)//如果碼流中存在當前CU有關殘差的語法元素
{
    if (cs.slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())
    {
        if (!cu.firstPU->ciipFlag && !CU::isIBC(cu))
            cs.getPredBuf(cu).get(COMPONENT_Y).rspSignal(m_pcReshape->getFwdLUT());//進行LMCS中的luma mapping
    }
    cs.getResiBuf( cu ).reconstruct( cs.getPredBuf( cu ), cs.getResiBuf( cu ), cs.slice->clpRngs() );//將畫素預測值與畫素殘差值相加存在畫素殘差值中
    cs.getRecoBuf( cu ).copyFrom   (                      cs.getResiBuf( cu ) );//將畫素殘差值拷貝給畫素重建值
}
else
{//如果碼流中不存在當前CU有關殘差的語法元素
    cs.getRecoBuf(cu).copyClip(cs.getPredBuf(cu), cs.slice->clpRngs());//將畫素預測值拷貝給畫素重建值
    if (cs.slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag() && !cu.firstPU->ciipFlag && !CU::isIBC(cu))
    {//如果需要,進行LMCS中的luma mapping
        cs.getRecoBuf(cu).get(COMPONENT_Y).rspSignal(m_pcReshape->getFwdLUT());
    }
}

if分支:如果判斷條件為真則存在殘差,將畫素預測值與畫素殘差值相加存在畫素殘差值中,再將畫素殘差值拷貝給畫素重建值;如果判斷條件為假,則直接將畫素預測值拷貝給畫素重建值。如果需要,還會對亮度分量進行luma mapping


cs.setDecomp(cu);//設定cs中的m_isDecomp

這段用來設定cs中的m_isDecomp,m_isDecomp表示當前幀每個畫素點是否已重構


1.1 xDecodeInterTexture()

這個函式用來獲得當前CU的畫素殘差值

const uint32_t uiNumVaildComp = getNumberValidComponents(cu.chromaFormat);
if (cu.colorTransform)//當開啟ACT的情況
{
}
else
{
    //分Y、Cb、Cr分量進行
    for (uint32_t ch = 0; ch < uiNumVaildComp; ch++)
    {
        const ComponentID compID = ComponentID(ch);
        //遍歷當前CU內所有TU
        for( auto& currTU : CU::traverseTUs( cu ) )
        {
            CodingStructure  &cs = *cu.cs;
            const Slice &slice = *cs.slice;
            if (slice.getLmcsEnabledFlag() && slice.getPicHeader()->getLmcsChromaResidualScaleFlag() && (compID == COMPONENT_Y) && (currTU.cbf[COMPONENT_Cb] || currTU.cbf[COMPONENT_Cr]))
            {//如果需要,設定LMCS中chroma scaling的縮放係數
                const CompArea &areaY = currTU.blocks[COMPONENT_Y];
                int adj = m_pcReshape->calculateChromaAdjVpduNei(currTU, areaY);
                currTU.setChromaAdj(adj);
            }
            xDecodeInterTU( currTU, compID );//獲取當前TU對應分量的畫素殘差值
        }
    }
}

if分支:如果判斷條件為真,表示在開啟ACT的情況下獲取畫素殘差值,這裡不再展開;判斷條件為假,在非ACT情況下獲取畫素殘差值

在else分支中,分三個分量遍歷當前CU內所有TU,呼叫xDecodeInterTU()函式獲取當前TU對應分量的畫素殘差值。這個函式中的內容與幀內預測情況下獲取畫素殘差值方式一致,不再贅述。如果需要,還要設定LMCS中chroma scaling的縮放係數,在xDecodeInterTU()裡會用到


2. motionCompensation()

for( auto &pu : CU::traversePUs( cu ) )
{
    PelUnitBuf predBuf = cu.cs->getPredBuf( pu );//獲取在cs中儲存的當前PU的畫素預測值地址
    pu.mvRefine = true;
    motionCompensation(pu, predBuf, eRefPicList, luma, chroma);//進行運動補償,獲取當前PU的幀間預測值
    pu.mvRefine = false;
}

這段用來遍歷當前CU內所有PU,進行運動補償(雖然CU內只有一個PU)

predBuf:當前PU的畫素預測值地址

mvRefine:為1表示當前PU允許開啟DMVR

motionCompensation():進行運動補償,獲取當前PU的幀間預測值。此函式是過載的,雖然函式名相同實則不同。稱此函式為PU級motionCompensation(),具體參考2.1小節


2.1 PU級motionCompensation()

由於這個函式是在編碼端和解碼端都會呼叫的函式,為了方便理解,下面省略只在編碼端以及只在IBC預測下才會遇到的情況

int refIdx0 = pu.refIdx[REF_PIC_LIST_0];//參考幀在reference list 0的Index
int refIdx1 = pu.refIdx[REF_PIC_LIST_1];//參考幀在reference list 1的Index

//有關WP的引數
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;//為1表示開啟BDOF
//下面判斷是否開啟BDOF
if (pu.cs->sps->getBDOFEnabledFlag() && (!pu.cs->picHeader->getDisBdofFlag()))
{
}
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;//為1表示開啟DMVR
dmvrApplied = (pu.mvRefine) && PU::checkDMVRCondition(pu);

if ((pu.lumaSize().width > MAX_BDOF_APPLICATION_REGION || pu.lumaSize().height > MAX_BDOF_APPLICATION_REGION) && pu.mergeType != MRG_TYPE_SUBPU_ATMVP && (bioApplied && !dmvrApplied))
{//如果開啟BDOF後PU過大,需要劃分成SubPU進行運動補償
    xSubPuBio(pu, predBuf, eRefPicList, predBufWOBIO);
}
else
{
    if (pu.mergeType != MRG_TYPE_DEFAULT_N && pu.mergeType != MRG_TYPE_IBC)
    {//如果當前PU的MVP由ATMVP得到,則劃分成SubPU進行運動補償
        xSubPuMC(pu, predBuf, eRefPicList, luma, chroma);
    }
    else if (xCheckIdenticalMotion(pu))
    {//如果list0和list1的預測一樣,採用單向預測
        xPredInterUni(pu, REF_PIC_LIST_0, predBuf, false, false, luma, chroma);
    }
    else
    {
        xPredInterBi(pu, predBuf, luma, chroma, predBufWOBIO);//進行雙向預測,如果list0或list1的預測不可用則變成單向預測
    }
}

refIdx0:參考幀在reference list 0的Index

refIdx1:參考幀在reference list 1的Index

wp0和wp1:都是有關WP的引數

bioApplied:為1表示開啟BDOF,後面的if分支用來判斷是否開啟BDOF,由於過長就不貼出來了

refIsScaled:為1表示參考幀進行過縮放,則不能開啟BDOF

dmvrApplied:為1表示開啟DMVR,後面會進行判斷

第二個if分支:如果判斷條件為真,表示開啟BDOF後PU過大,需要劃分成SubPU進行運動補償,呼叫函式xSubPuBio();判斷條件為假,進入其餘情況

第三個if分支:如果判斷條件為真,表示當前PU的MVP由ATMVP得到,則劃分成SubPU進行運動補償,呼叫函式xSubPuMC();如果elseif的判斷條件為真,表示list0和list1的預測一樣,採用單向預測呼叫函式xPredInterUni(),這個稍後會講;進入else,呼叫函式xPredInterBi(),具體參考第3大節


3. xPredInterBi()

這個函式進行雙向預測,如果list0或list1的預測不可用則變成單向預測。前面的設定和2.1小節類似,這裡不再贅述

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);
    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)
    {//如果兩個方向都可以進行預測
        if (dmvrApplied)
        {//如果開啟DMVR,跳過這裡的預測,後面會呼叫xProcessDMVR
            continue;
        }
        xPredInterUni(pu, eRefPicList, pcMbBuf, true, bioApplied, luma, chroma);
    }
    else
    {
        if( ( (pps.getUseWP() && slice.getSliceType() == P_SLICE) || (pps.getWPBiPred() && slice.getSliceType() == B_SLICE) ) )
        {//如果使用WP
            xPredInterUni(pu, eRefPicList, pcMbBuf, true, bioApplied, luma, chroma);
        }
        else
        {
            xPredInterUni(pu, eRefPicList, pcMbBuf, pu.cu->geoFlag, bioApplied, luma, chroma);
        }
    }
}

for迴圈:分別對list0和list1進行單向預測

第一個if分支:如果refIdx[refList]小於0,表示當前list的預測不可用,跳過當前list的單向預測

eRefPicList:表示當前list是list0還是list1

pcMbBuf:存放當前list的單向預測值

第二個if分支:分不同情況呼叫函式xPredInterUni()進行單向預測,只是傳入引數的不同。在開啟DMVR的情況下,跳過這裡的預測,後面會呼叫函式xProcessDMVR()。有關xPredInterUni()的講解,參考3.1小節


//list 0的單向預測值
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())) );
//list 1的單向預測值
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;//為1表示只進行了亮度分量的預測
const bool chromaOnly = !luma && chroma;//為1表示只進行了色度分量的預測

srcPred0:list 0的單向預測值

srcPred1:list 1的單向預測值

lumaOnly:為1表示只進行了亮度分量的預測

chromaOnly:為1表示只進行了色度分量的預測


if( !pu.cu->geoFlag && (!dmvrApplied) && (!bioApplied) && pps.getWPBiPred() && slice.getSliceType() == B_SLICE && pu.cu->BcwIdx == BCW_DEFAULT)
{
    xWeightedPredictionBi( pu, srcPred0, srcPred1, pcYuvPred, m_maxCompIDToPred, lumaOnly, chromaOnly );//進行雙向WP,當list0或list1的預測無效時會變成單向WP
}
else if( !pu.cu->geoFlag && pps.getUseWP() && slice.getSliceType() == P_SLICE )
{
    xWeightedPredictionUni( pu, srcPred0, REF_PIC_LIST_0, pcYuvPred, -1, m_maxCompIDToPred, lumaOnly, chromaOnly );//進行單向WP
}
else
{//如果不進行WP
    if (dmvrApplied)
    {//如果開啟DMVR
        xProcessDMVR(pu, pcYuvPred, slice.clpRngs(), bioApplied);//進行DMVR
    }
    else
    {//一般情況下進行list0和list1的預測值平均,當list0或list1的預測值無效時就變為直接拷貝。裡面包括BDOF和BCW的實現
        xWeightedAverage( pu, srcPred0, srcPred1, pcYuvPred, slice.getSPS()->getBitDepths(), slice.clpRngs(), bioApplied, lumaOnly, chromaOnly, yuvPredTmp );
    }
}

第一個if分支:如果判斷條件為真,表示進行雙向WP,呼叫函式xWeightedPredictionBi();如果elseif的判斷條件為真,表示進行單向WP,呼叫函式xWeightedPredictionUni();如果進入else,表示不進行WP

第二個if分支:如果判斷條件為真,表示開啟DMVR,呼叫函式xProcessDMVR();判斷條件為假,呼叫函式xWeightedAverage()

xWeightedAverage():一般情況下進行list0和list1的預測值平均,當list0或list1的預測值無效時就變為直接拷貝。裡面包括BDOF和BCW的實現。具體參考3.2小節


3.1 xPredInterUni()

先來看一下函式的兩個形參:

  • bioApplied:為1表示開啟BDOF

  • bi:這個暫且不清楚具體意義,只能知道以下情況時為1

    • 開啟WP
    • 開啟GPM
    • list0和list1的預測值皆有效
int iRefIdx = pu.refIdx[eRefPicList];//參考幀在參考幀列表中的Index
bool isIBC = false;
if (CU::isIBC(*pu.cu))
{
    isIBC = true;
}

isIBC:為1表示進行IBC預測

Mv mv[3];
//獲取MV
if( pu.cu->affine )
{
    mv[0] = pu.mvAffi[eRefPicList][0];
    mv[1] = pu.mvAffi[eRefPicList][1];
    mv[2] = pu.mvAffi[eRefPicList][2];
}
else
{
    mv[0] = pu.mv[eRefPicList];
}
//進行MV裁剪
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 );
        }
    }
}

mv:儲存著當前PU的MV,如果需要,還要進行裁剪


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模式
    {
        m_iRefListIdx = eRefPicList;
        bool genChromaMv = (!luma && chroma && compID == COMPONENT_Cb);//為1表示要生成色度的子塊MV
        //在開啟affine的情況下,獲取相應分量的單向預測值(包括PROF的實現)
        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)//如果開啟IBC預測
        {
            xPredInterBlk(compID, pu, pu.cu->slice->getPic(), mv[0], pcYuvPred, bi, pu.cu->slice->clpRng(compID),
                          bioApplied, isIBC);
        }
        else
        {
            //獲取相應分量的單向預測值
            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 ) );
        }
    }
}

for迴圈:分Y、Cb、Cr獲取單向預測值

第一個if分支和第二個if分支:如果對應分量不需要獲取預測值,則跳過當前迴圈

第三個if分支:如果判斷條件為真,表示開啟affine,呼叫函式xPredAffineBlk()獲取相應分量的單向預測值。函式裡面包括PROF的實現,這裡不再展開。判斷條件為假,表示進入非affine的情況,呼叫函式xPredInterBlk()獲取相應分量的單向預測值。開啟IBC與不開啟IBC時傳入函式的實參有區別,函式具體參考第4大節


3.2 xWeightedAverage()

const int iRefIdx0 = pu.refIdx[0];
const int iRefIdx1 = pu.refIdx[1];

if( iRefIdx0 >= 0 && iRefIdx1 >= 0 )
{
}
else if( iRefIdx0 >= 0 && iRefIdx1 < 0 )
{
    if( pu.cu->geoFlag )
    {
        pcYuvDst.copyFrom( pcYuvSrc0 );
    }
    else
    {
        pcYuvDst.copyClip( pcYuvSrc0, clpRngs, lumaOnly, chromaOnly );
    }
}
else if( iRefIdx0 < 0 && iRefIdx1 >= 0 )
{
    if( pu.cu->geoFlag )
    {
        pcYuvDst.copyFrom( pcYuvSrc1 );
    }
    else
    {
        pcYuvDst.copyClip( pcYuvSrc1, clpRngs, lumaOnly, chromaOnly );
    }
}

iRefIdx0和iRefIdx1:參考幀在參考幀列表中的Index,如果值為-1表示不存在相應方向的幀間預測

if分支:如果判斷條件為真,表示list0和list1的預測值都有效,具體參考3.2.1小節;判斷條件為假,表示list0和list1只有其中一個的預測值有效,直接將list0或list1的單向預測值拷貝給當前PU對應的畫素預測值。如果不開啟GPM的話,單向預測值需要經過裁剪


3.2.1 list0和list1的預測值都有效

if( pu.cu->BcwIdx != BCW_DEFAULT && !pu.ciipFlag )
{//如果開啟BCW
    pcYuvDst.addWeightedAvg(pcYuvSrc0, pcYuvSrc1, clpRngs, pu.cu->BcwIdx, chromaOnly, lumaOnly);
    return;
}
if (bioApplied)//如果開啟BDOF
{
}
if (!bioApplied && (lumaOnly || chromaOnly))
{
    pcYuvDst.addAvg(pcYuvSrc0, pcYuvSrc1, clpRngs, chromaOnly, lumaOnly);
}
else
{
    pcYuvDst.addAvg(pcYuvSrc0, pcYuvSrc1, clpRngs, bioApplied);//如果開啟BDOF只用對色度進行list0和list1的預測值平均
}

第一個if分支:如果判斷條件為真,表示開啟BCW,對list0和list1的單向預測值加權並跳過之後的語句

第二個if分支:如果判斷條件為真,表示開啟BDOF,裡面呼叫函式applyBiOptFlow()進行BDOF,這裡不再展開

第三個if分支:這裡大致的意思是,如果開啟BDOF只用對色度進行list0和list1的預測值平均,亮度已經在第二個if分支中處理過了


4. xPredInterBlk()

為了方便講解和理解,這裡省略參考幀縮放、DMVR、BDOF的情況

const ChromaFormat  chFmt = pu.chromaFormat;
const bool          rndRes = !bi;
bool useAltHpelIf = pu.cu->imv == IMV_HPEL;//為1表示MVD的傳輸精度是半畫素

int shiftHor = MV_FRACTIONAL_BITS_INTERNAL + ::getComponentScaleX(compID, chFmt);
int shiftVer = MV_FRACTIONAL_BITS_INTERNAL + ::getComponentScaleY(compID, chFmt);

shiftHor:對MV的水平分量要縮小(2的shiftHor次)倍。一方面是因為儲存的是1/16畫素精度,獲取預測畫素值用的是整畫素精度;另一方面是因為儲存的是亮度MV,獲取色度MV還要根據色度取樣格式進行縮小。

shiftVer:與shiftHor類似,處理的是MV的垂直分量


bool  wrapRef = false;//為1表示是horizontal wrap-around運動補償
Mv    mv(_mv);
if( !isIBC && refPic->isWrapAroundEnabled( pu.cs->pps ) )
{
    wrapRef = wrapClipMv( mv, pu.blocks[0].pos(), pu.blocks[0].size(), pu.cs->sps, pu.cs->pps );
}

wrapRef:為1表示是horizontal wrap-around運動補償


int xFrac = mv.hor & ((1 << shiftHor) - 1);//MV的水平方向的小數部分
int yFrac = mv.ver & ((1 << shiftVer) - 1);//MV的垂直方向的小數部分
if (isIBC)
{
    xFrac = yFrac = 0;
}

PelBuf & dstBuf = dstPic.bufs[compID];
unsigned width  = dstBuf.width;
unsigned height = dstBuf.height;

xFrac:MV的水平方向的小數部分

yFrac:MV的垂直方向的小數部分


CPelBuf refBuf;//儲存著參考幀中相應的參考塊
{
    Position offset = pu.blocks[compID].pos().offset(mv.getHor() >> shiftHor, mv.getVer() >> shiftVer);//參考塊在參考幀中的位置
    refBuf = refPic->getRecoBuf(CompArea(compID, chFmt, offset, pu.blocks[compID].size()), wrapRef);//獲取參考幀中相應的參考塊
}

refBuf:儲存著參考幀中相應的參考塊

getRecoBuf():獲取參考幀中相應的參考塊


if (yFrac == 0)
{
    m_if.filterHor(compID, (Pel *) refBuf.buf, refBuf.stride, dstBuf.buf, dstBuf.stride, backupWidth, backupHeight,
                   xFrac, rndRes, chFmt, clpRng, bilinearMC, bilinearMC, useAltHpelIf);
}
else if (xFrac == 0)
{
    m_if.filterVer(compID, (Pel *) refBuf.buf, refBuf.stride, dstBuf.buf, dstBuf.stride, backupWidth, backupHeight,
                   yFrac, true, rndRes, chFmt, clpRng, bilinearMC, bilinearMC, useAltHpelIf);
}
else
{
    PelBuf tmpBuf = PelBuf(m_filteredBlockTmp[0][compID], pu.blocks[compID]);

    int vFilterSize = isLuma(compID) ? NTAPS_LUMA : NTAPS_CHROMA;
    if (bilinearMC)
    {
        vFilterSize = NTAPS_BILINEAR;
    }
    m_if.filterHor(compID, (Pel *) refBuf.buf - ((vFilterSize >> 1) - 1) * refBuf.stride, refBuf.stride, tmpBuf.buf,
                   tmpBuf.stride, backupWidth, backupHeight + vFilterSize - 1, xFrac, false, chFmt, clpRng,
                   bilinearMC, bilinearMC, useAltHpelIf);
    m_if.filterVer(compID, (Pel *) tmpBuf.buf + ((vFilterSize >> 1) - 1) * tmpBuf.stride, tmpBuf.stride, dstBuf.buf,
                   dstBuf.stride, backupWidth, backupHeight, yFrac, false, rndRes, chFmt, clpRng, bilinearMC,
                   bilinearMC, useAltHpelIf);
}

這段可以理解成,根據MV的小數部分對參考塊進行濾波,獲取單向預測值。

相關文章