VVC帧间预测(四)仿射运动补偿预测
HEVC中在进行运动补偿时只考虑了平移运动,而在真实世界里存在各种运动,例如缩放、旋转、头上运动和其他不规则运动。在VTM5中提出了基于块的仿射变换运动补偿预测。如下图所示,一个块的仿射运动向量由两个控制点(4个参数)或三个控制点(6个参数)生成。
基于块的仿射运动补偿方式如下:
1.首先将块划分为4x4的亮度子块。
2.对每个亮度子块按下式由仿射向量计算其中心像素的运动向量,然后四舍五入到1/16精度。
对于4参数仿射运动模型,中心像素为(x,y)的子块的运动向量计算如下:
对于6参数仿射运动模型,中心像素为(x,y)的子块的运动向量计算如下:
其中(mv0x,mv0y),(mv1x,mv1y),(mv2x,mv2y)分别是左上角、右上角和左下角的控制点的运动向量。
3.每个子块计算出运动向量后(如下图),根据运动向量进行运动补偿插值滤波得到每个子块的预测值。
4.对于色度分量同样是划分为4x4的子块,其运动向量等于与其相关的4个4x4的亮度子块运动向量的平均值。
和传统的帧间运动向量预测方式一样,仿射运动向量也有两种预测方式:仿射merge模式、仿射AMVP模式
仿射merge预测(affine merge prediction)
对于宽和高都大于等于8的CU可以使用AF_MERGE模式。在这种模式下当前CU的控制点运动向量(CPMV,control point motion vector)由其空域相邻的CU的运动信息生成。至多生成5个CPMV的预测候选项,且需要传输一个索引表示最终使用了哪个候选项。由下面3种CPMV候选项生成affine merge list:
1.继承其邻居CU的CPMV候选项。
2.由邻居CU的平移运动的MV构建CPMV。
3.0向量。
在VTM5中最多有两个类型1的候选项,一个继承自左边邻居CU,另一个继承自上边邻居CU。如下图所示,对于左侧CU扫描顺序是A0->A1,对于上方CU扫描顺序是B0->B1->B2。对于左侧和上方分别只继承扫描顺序中第一个有效的CU。继承的两个候选项间不进行剪枝操作。
当邻近CU被选定,该邻近CU的CPMV就用来生成当前CU的affine merge list里的候选项。如下图所示,如果左下角的A块被选中,当A是4参数仿射运动模型时,当前CU的两个CPMV根据v2,v3计算得到,当A是6参数仿射运动模型时,当前CU的三个CPMV根据v2,v3,v4计算得到。
对于类型2的候选项,其每个控制点是由特定的空域邻居和时域邻居生成,如下图所示。CPMVk(k=1,2,3,4)表示第k个控制点。对于CPMV1,由B2->B3->A2中第一个有效的块的MV生成。对于CPMV2,由B1->B0中第一个有效的块的MV生成。对于CPMV3,由A1->A0中第一个有效的块的MV生成。如果存在CPVM4的话,由TMVP生成。
当4个控制点的MV得到后,affine merge的候选项基于这些运动信息构建。下面控制点MV的组合用于构建候选项:
{CPMV1, CPMV2, CPMV3}, {CPMV1, CPMV2, CPMV4}, {CPMV1, CPMV3, CPMV4}, {CPMV2, CPMV3, CPMV4}, { CPMV1, CPMV2}, { CPMV1, CPMV3}
其中3个CPMV的组合构建6参数affine merge候选项,2个CPMV的组合用于构建4参数affine merge候选项。为了避免进行缩放计算,如果控制点的参考图像不同则相关组合被丢弃。
如果类型1和类型2的候选项没有填满affine merge list,则用0向量填充。
仿射AMVP预测(affine AMVP prediction)
对于宽和高都大于等于16的CU可以使用仿射AMVP模式。在merge模式中直接使用预测CPMV,而在AMVP中需要传输的是当前CU的最优CPMV和预测CPMV的残差。affine AVMP candidate list有2个候选项,由下面4类CPMV候选项生成:
1.继承其邻居CU的CPMV候选项。
2.由邻居CU的平移运动的MV构建CPMV。
3.来自邻居CU的平移运动MV。
4.0向量。
类型1的affine AMVP候选项构建和affine merge一样。唯一不同是邻居CU的参考图像和当前CU必须一样。
类型2的affine AMVP候选项构建和affine merge一样。除此之外,邻居块的参考图像索引也要检查,要选择扫描顺序中第一个帧间编码且和当前CU有相同参考图像的块。当当前CU是4参数仿射模型且mv0和mv1都有效时,将它们加入affine AMVP list。当当前CU是6参数仿射模型且3个CPMV都有效时,将它们加入affine AMVP list。否则类型2的候选项无效。
如果类型1和类型2加入之后affine AMVP list中的候选项还是少于2,则按序加入mv0,mv1,mv2用平移运动MV预测当前CU所有控制点的MV。最后如果list还没满则用0向量填充。
在VTM5的定义里affine AMVP有3个候选项如下:
struct AffineAMVPInfo
{Mv mvCandLT[ AMVP_MAX_NUM_CANDS_MEM ]; ///< array of affine motion vector predictor candidates for left-top cornerMv mvCandRT[ AMVP_MAX_NUM_CANDS_MEM ]; ///< array of affine motion vector predictor candidates for right-top cornerMv mvCandLB[ AMVP_MAX_NUM_CANDS_MEM ]; ///< array of affine motion vector predictor candidates for left-bottom cornerunsigned numCand; ///< number of motion vector predictor candidates
};
以下是affine AMVP list构建的代码:
void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, AffineAMVPInfo &affiAMVPInfo)
{affiAMVPInfo.numCand = 0;if (refIdx < 0){return;}//!<继承其邻居CU的CPMV候选项// insert inherited affine candidatesMv outputAffineMv[3];Position posLT = pu.Y().topLeft();Position posRT = pu.Y().topRight();Position posLB = pu.Y().bottomLeft();// check left neighborif ( !addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, affiAMVPInfo ) ){addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_LEFT, affiAMVPInfo );}// check above neighborif ( !addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE_RIGHT, affiAMVPInfo ) ){if ( !addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE, affiAMVPInfo ) ){addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, affiAMVPInfo );}}if ( affiAMVPInfo.numCand >= AMVP_MAX_NUM_CANDS ){for (int i = 0; i < affiAMVPInfo.numCand; i++){affiAMVPInfo.mvCandLT[i].roundAffinePrecInternal2Amvr(pu.cu->imv);affiAMVPInfo.mvCandRT[i].roundAffinePrecInternal2Amvr(pu.cu->imv);affiAMVPInfo.mvCandLB[i].roundAffinePrecInternal2Amvr(pu.cu->imv);}return;}//!<由邻居CU的平移运动的MV构建CPMV// insert constructed affine candidatesint cornerMVPattern = 0;//------------------- V0 (START) -------------------//AMVPInfo amvpInfo0;amvpInfo0.numCand = 0;// A->C: Above Left, Above, LeftaddMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, amvpInfo0 );if ( amvpInfo0.numCand < 1 ){addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE, amvpInfo0 );}if ( amvpInfo0.numCand < 1 ){addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_LEFT, amvpInfo0 );}cornerMVPattern = cornerMVPattern | amvpInfo0.numCand;//------------------- V1 (START) -------------------//AMVPInfo amvpInfo1;amvpInfo1.numCand = 0;// D->E: Above, Above RightaddMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE, amvpInfo1 );if ( amvpInfo1.numCand < 1 ){addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE_RIGHT, amvpInfo1 );}cornerMVPattern = cornerMVPattern | (amvpInfo1.numCand << 1);//------------------- V2 (START) -------------------//AMVPInfo amvpInfo2;amvpInfo2.numCand = 0;// F->G: Left, Below LeftaddMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_LEFT, amvpInfo2 );if ( amvpInfo2.numCand < 1 ){addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, amvpInfo2 );}cornerMVPattern = cornerMVPattern | (amvpInfo2.numCand << 2);outputAffineMv[0] = amvpInfo0.mvCand[0];outputAffineMv[1] = amvpInfo1.mvCand[0];outputAffineMv[2] = amvpInfo2.mvCand[0];outputAffineMv[0].roundAffinePrecInternal2Amvr(pu.cu->imv);outputAffineMv[1].roundAffinePrecInternal2Amvr(pu.cu->imv);outputAffineMv[2].roundAffinePrecInternal2Amvr(pu.cu->imv);if ( cornerMVPattern == 7 || (cornerMVPattern == 3 && pu.cu->affineType == AFFINEMODEL_4PARAM) ){affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[0];affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[1];affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[2];affiAMVPInfo.numCand++;}if ( affiAMVPInfo.numCand < 2 ){// check corner MVsfor ( int i = 2; i >= 0 && affiAMVPInfo.numCand < AMVP_MAX_NUM_CANDS; i-- ){if ( cornerMVPattern & (1 << i) ) // MV i exist{affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[i];affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[i];affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[i];affiAMVPInfo.numCand++;}}// Get Temporal Motion Predictorif ( affiAMVPInfo.numCand < 2 && pu.cs->slice->getEnableTMVPFlag() ){const int refIdxCol = refIdx;Position posRB = pu.Y().bottomRight().offset( -3, -3 );const PreCalcValues& pcv = *pu.cs->pcv;Position posC0;bool C0Avail = false;Position posC1 = pu.Y().center();
#if !JVET_N0266_SMALL_BLOCKSbool C1Avail = ( posC1.x < pcv.lumaWidth ) && ( posC1.y < pcv.lumaHeight ) ;
#endifMv cColMv;if ( ((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight) ){Position posInCtu( posRB.x & pcv.maxCUWidthMask, posRB.y & pcv.maxCUHeightMask );if ( (posInCtu.x + 4 < pcv.maxCUWidth) && // is not at the last column of CTU(posInCtu.y + 4 < pcv.maxCUHeight) ) // is not at the last row of CTU{posC0 = posRB.offset( 4, 4 );C0Avail = true;}else if ( posInCtu.x + 4 < pcv.maxCUWidth ) // is not at the last column of CTU But is last row of CTU{// in the reference the CTU address is not set - thus probably resulting in no using this C0 possibilityposC0 = posRB.offset( 4, 4 );}else if ( posInCtu.y + 4 < pcv.maxCUHeight ) // is not at the last row of CTU But is last column of CTU{posC0 = posRB.offset( 4, 4 );C0Avail = true;}else //is the right bottom corner of CTU{// same as for last column but not last rowposC0 = posRB.offset( 4, 4 );}}
#if JVET_N0266_SMALL_BLOCKSif ( ( C0Avail && getColocatedMVP( pu, eRefPicList, posC0, cColMv, refIdxCol ) ) || getColocatedMVP( pu, eRefPicList, posC1, cColMv, refIdxCol ) )
#elseif ( (C0Avail && getColocatedMVP( pu, eRefPicList, posC0, cColMv, refIdxCol )) || (C1Avail && getColocatedMVP( pu, eRefPicList, posC1, cColMv, refIdxCol ) ) )
#endif{cColMv.roundAffinePrecInternal2Amvr(pu.cu->imv);affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = cColMv;affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = cColMv;affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = cColMv;affiAMVPInfo.numCand++;}}//!<0向量if ( affiAMVPInfo.numCand < 2 ){// add zero MVfor ( int i = affiAMVPInfo.numCand; i < AMVP_MAX_NUM_CANDS; i++ ){affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand].setZero();affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand].setZero();affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand].setZero();affiAMVPInfo.numCand++;}}}for (int i = 0; i < affiAMVPInfo.numCand; i++){affiAMVPInfo.mvCandLT[i].roundAffinePrecInternal2Amvr(pu.cu->imv);affiAMVPInfo.mvCandRT[i].roundAffinePrecInternal2Amvr(pu.cu->imv);affiAMVPInfo.mvCandLB[i].roundAffinePrecInternal2Amvr(pu.cu->imv);}
}
以下是affine merge list构建的代码:
void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx, const int mrgCandIdx )
{const CodingStructure &cs = *pu.cs;const Slice &slice = *pu.cs->slice;const uint32_t maxNumAffineMergeCand = slice.getMaxNumAffineMergeCand();for ( int i = 0; i < maxNumAffineMergeCand; i++ ){for ( int mvNum = 0; mvNum < 3; mvNum++ ){affMrgCtx.mvFieldNeighbours[(i << 1) + 0][mvNum].setMvField( Mv(), -1 );affMrgCtx.mvFieldNeighbours[(i << 1) + 1][mvNum].setMvField( Mv(), -1 );}affMrgCtx.interDirNeighbours[i] = 0;affMrgCtx.affineType[i] = AFFINEMODEL_4PARAM;affMrgCtx.mergeType[i] = MRG_TYPE_DEFAULT_N;affMrgCtx.GBiIdx[i] = GBI_DEFAULT;}affMrgCtx.numValidMergeCand = 0;affMrgCtx.maxNumMergeCand = maxNumAffineMergeCand;bool enableSubPuMvp = slice.getSPS()->getSBTMVPEnabledFlag() && !(slice.getPOC() == slice.getRefPic(REF_PIC_LIST_0, 0)->getPOC() && slice.isIRAP());bool isAvailableSubPu = false;if ( enableSubPuMvp && slice.getEnableTMVPFlag() ){MergeCtx mrgCtx = *affMrgCtx.mrgCtx;bool tmpLICFlag = false;CHECK( mrgCtx.subPuMvpMiBuf.area() == 0 || !mrgCtx.subPuMvpMiBuf.buf, "Buffer not initialized" );mrgCtx.subPuMvpMiBuf.fill( MotionInfo() );int pos = 0;// Get spatial MVconst Position posCurLB = pu.Y().bottomLeft();MotionInfo miLeft;//leftconst PredictionUnit* puLeft = cs.getPURestricted( posCurLB.offset( -1, 0 ), pu, pu.chType );const bool isAvailableA1 = puLeft && isDiffMER( pu, *puLeft ) && pu.cu != puLeft->cu && CU::isInter( *puLeft->cu );if ( isAvailableA1 ){miLeft = puLeft->getMotionInfo( posCurLB.offset( -1, 0 ) );// get Inter DirmrgCtx.interDirNeighbours[pos] = miLeft.interDir;// get Mv from LeftmrgCtx.mvFieldNeighbours[pos << 1].setMvField( miLeft.mv[0], miLeft.refIdx[0] );if ( slice.isInterB() ){mrgCtx.mvFieldNeighbours[(pos << 1) + 1].setMvField( miLeft.mv[1], miLeft.refIdx[1] );}pos++;}mrgCtx.numValidMergeCand = pos;isAvailableSubPu = getInterMergeSubPuMvpCand( pu, mrgCtx, tmpLICFlag, pos, 0);if ( isAvailableSubPu ){for ( int mvNum = 0; mvNum < 3; mvNum++ ){affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 0][mvNum].setMvField( mrgCtx.mvFieldNeighbours[(pos << 1) + 0].mv, mrgCtx.mvFieldNeighbours[(pos << 1) + 0].refIdx );affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 1][mvNum].setMvField( mrgCtx.mvFieldNeighbours[(pos << 1) + 1].mv, mrgCtx.mvFieldNeighbours[(pos << 1) + 1].refIdx );}affMrgCtx.interDirNeighbours[affMrgCtx.numValidMergeCand] = mrgCtx.interDirNeighbours[pos];affMrgCtx.affineType[affMrgCtx.numValidMergeCand] = AFFINE_MODEL_NUM;affMrgCtx.mergeType[affMrgCtx.numValidMergeCand] = MRG_TYPE_SUBPU_ATMVP;if ( affMrgCtx.numValidMergeCand == mrgCandIdx ){return;}affMrgCtx.numValidMergeCand++;// early terminationif ( affMrgCtx.numValidMergeCand == maxNumAffineMergeCand ){return;}}}if ( slice.getSPS()->getUseAffine() ){///> Start: inherited affine candidatesconst PredictionUnit* npu[5];int numAffNeighLeft = getAvailableAffineNeighboursForLeftPredictor( pu, npu );int numAffNeigh = getAvailableAffineNeighboursForAbovePredictor( pu, npu, numAffNeighLeft );for ( int idx = 0; idx < numAffNeigh; idx++ ){// derive Mv from Neigh affine PUMv cMv[2][3];const PredictionUnit* puNeigh = npu[idx];pu.cu->affineType = puNeigh->cu->affineType;if ( puNeigh->interDir != 2 ){xInheritedAffineMv( pu, puNeigh, REF_PIC_LIST_0, cMv[0] );}if ( slice.isInterB() ){if ( puNeigh->interDir != 1 ){xInheritedAffineMv( pu, puNeigh, REF_PIC_LIST_1, cMv[1] );}}for ( int mvNum = 0; mvNum < 3; mvNum++ ){affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 0][mvNum].setMvField( cMv[0][mvNum], puNeigh->refIdx[0] );affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 1][mvNum].setMvField( cMv[1][mvNum], puNeigh->refIdx[1] );}affMrgCtx.interDirNeighbours[affMrgCtx.numValidMergeCand] = puNeigh->interDir;affMrgCtx.affineType[affMrgCtx.numValidMergeCand] = (EAffineModel)(puNeigh->cu->affineType);affMrgCtx.GBiIdx[affMrgCtx.numValidMergeCand] = puNeigh->cu->GBiIdx;if ( affMrgCtx.numValidMergeCand == mrgCandIdx ){return;}// early terminationaffMrgCtx.numValidMergeCand++;if ( affMrgCtx.numValidMergeCand == maxNumAffineMergeCand ){return;}}///> End: inherited affine candidates///> Start: Constructed affine candidates{MotionInfo mi[4];bool isAvailable[4] = { false };
#if JVET_N0481_BCW_CONSTRUCTED_AFFINE int8_t neighGbi[4] = { GBI_DEFAULT };
#endif// control point: LT B2->B3->A2const Position posLT[3] = { pu.Y().topLeft().offset( -1, -1 ), pu.Y().topLeft().offset( 0, -1 ), pu.Y().topLeft().offset( -1, 0 ) };for ( int i = 0; i < 3; i++ ){const Position pos = posLT[i];const PredictionUnit* puNeigh = cs.getPURestricted( pos, pu, pu.chType );if ( puNeigh && CU::isInter( *puNeigh->cu )){isAvailable[0] = true;mi[0] = puNeigh->getMotionInfo( pos );
#if JVET_N0481_BCW_CONSTRUCTED_AFFINE neighGbi[0] = puNeigh->cu->GBiIdx;
#endifbreak;}}// control point: RT B1->B0const Position posRT[2] = { pu.Y().topRight().offset( 0, -1 ), pu.Y().topRight().offset( 1, -1 ) };for ( int i = 0; i < 2; i++ ){const Position pos = posRT[i];const PredictionUnit* puNeigh = cs.getPURestricted( pos, pu, pu.chType );if ( puNeigh && CU::isInter( *puNeigh->cu )){isAvailable[1] = true;mi[1] = puNeigh->getMotionInfo( pos );
#if JVET_N0481_BCW_CONSTRUCTED_AFFINE neighGbi[1] = puNeigh->cu->GBiIdx;
#endifbreak;}}// control point: LB A1->A0const Position posLB[2] = { pu.Y().bottomLeft().offset( -1, 0 ), pu.Y().bottomLeft().offset( -1, 1 ) };for ( int i = 0; i < 2; i++ ){const Position pos = posLB[i];const PredictionUnit* puNeigh = cs.getPURestricted( pos, pu, pu.chType );if ( puNeigh && CU::isInter( *puNeigh->cu )){isAvailable[2] = true;mi[2] = puNeigh->getMotionInfo( pos );
#if JVET_N0481_BCW_CONSTRUCTED_AFFINE neighGbi[2] = puNeigh->cu->GBiIdx;
#endifbreak;}}// control point: RBif ( slice.getEnableTMVPFlag() ){//>> MTK colocated-RightBottom// offset the pos to be sure to "point" to the same position the uiAbsPartIdx would've pointed toPosition posRB = pu.Y().bottomRight().offset( -3, -3 );const PreCalcValues& pcv = *cs.pcv;Position posC0;bool C0Avail = false;if ( ((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight) ){Position posInCtu( posRB.x & pcv.maxCUWidthMask, posRB.y & pcv.maxCUHeightMask );if ( (posInCtu.x + 4 < pcv.maxCUWidth) && // is not at the last column of CTU(posInCtu.y + 4 < pcv.maxCUHeight) ) // is not at the last row of CTU{posC0 = posRB.offset( 4, 4 );C0Avail = true;}else if ( posInCtu.x + 4 < pcv.maxCUWidth ) // is not at the last column of CTU But is last row of CTU{posC0 = posRB.offset( 4, 4 );// in the reference the CTU address is not set - thus probably resulting in no using this C0 possibility}else if ( posInCtu.y + 4 < pcv.maxCUHeight ) // is not at the last row of CTU But is last column of CTU{posC0 = posRB.offset( 4, 4 );C0Avail = true;}else //is the right bottom corner of CTU{posC0 = posRB.offset( 4, 4 );// same as for last column but not last row}}Mv cColMv;int refIdx = 0;bool bExistMV = C0Avail && getColocatedMVP( pu, REF_PIC_LIST_0, posC0, cColMv, refIdx );if ( bExistMV ){mi[3].mv[0] = cColMv;mi[3].refIdx[0] = refIdx;mi[3].interDir = 1;isAvailable[3] = true;}if ( slice.isInterB() ){bExistMV = C0Avail && getColocatedMVP( pu, REF_PIC_LIST_1, posC0, cColMv, refIdx );if ( bExistMV ){mi[3].mv[1] = cColMv;mi[3].refIdx[1] = refIdx;mi[3].interDir |= 2;isAvailable[3] = true;}}}//------------------- insert model -------------------//int order[6] = { 0, 1, 2, 3, 4, 5 };int modelNum = 6;int model[6][4] = {{ 0, 1, 2 }, // 0: LT, RT, LB{ 0, 1, 3 }, // 1: LT, RT, RB{ 0, 2, 3 }, // 2: LT, LB, RB{ 1, 2, 3 }, // 3: RT, LB, RB{ 0, 1 }, // 4: LT, RT{ 0, 2 }, // 5: LT, LB};int verNum[6] = { 3, 3, 3, 3, 2, 2 };int startIdx = pu.cs->sps->getUseAffineType() ? 0 : 4;for ( int idx = startIdx; idx < modelNum; idx++ ){int modelIdx = order[idx];
#if JVET_N0481_BCW_CONSTRUCTED_AFFINEgetAffineControlPointCand(pu, mi, neighGbi, isAvailable, model[modelIdx], modelIdx, verNum[modelIdx], affMrgCtx);
#elsegetAffineControlPointCand( pu, mi, isAvailable, model[modelIdx], modelIdx, verNum[modelIdx], affMrgCtx );
#endifif ( affMrgCtx.numValidMergeCand != 0 && affMrgCtx.numValidMergeCand - 1 == mrgCandIdx ){return;}// early terminationif ( affMrgCtx.numValidMergeCand == maxNumAffineMergeCand ){return;}}}///> End: Constructed affine candidates}///> zero paddingint cnt = affMrgCtx.numValidMergeCand;while ( cnt < maxNumAffineMergeCand ){for ( int mvNum = 0; mvNum < 3; mvNum++ ){affMrgCtx.mvFieldNeighbours[(cnt << 1) + 0][mvNum].setMvField( Mv( 0, 0 ), 0 );}affMrgCtx.interDirNeighbours[cnt] = 1;if ( slice.isInterB() ){for ( int mvNum = 0; mvNum < 3; mvNum++ ){affMrgCtx.mvFieldNeighbours[(cnt << 1) + 1][mvNum].setMvField( Mv( 0, 0 ), 0 );}affMrgCtx.interDirNeighbours[cnt] = 3;}affMrgCtx.affineType[cnt] = AFFINEMODEL_4PARAM;if ( cnt == mrgCandIdx ){return;}cnt++;affMrgCtx.numValidMergeCand++;}
}
感兴趣的请关注微信公众号Video Coding
VVC帧间预测(四)仿射运动补偿预测相关推荐
- 【九】 H.266/VVC中帧间仿射运动补偿预测
一.前言 HEVC中在进行运动补偿时只考虑了平移运动,而在真实的世界存在各种运动,例如缩放.旋转等非平移运动.在H.266/VVC中提出了基于块的仿射变换运动补偿预测.如下图所示,一个块的仿射运动向量 ...
- H.266/VVC帧间预测总结
一.帧间预测基本原理 帧间预测是利用视频帧与帧之间的相关性,去除视频帧间的时间冗余信息.统计表明,帧间差绝对值超过3的像素平均不到一帧像素的4%,因此,采用高效的帧间编码方式,可以很大程度上提高视频压 ...
- DCC2020:VVC帧间预测中的几何划分
本文来自DCC2020论文<Advanced Geometric-based Inter Prediction for Versatile Video Coding> 几何划分相较于三角划 ...
- VVC帧间预测(八)DMVR
解码端运动向量修正(Decoder side motion vector refinement ,DMVR)是为了提高merge模式下双向预测MV的准确性而提出的技术.双向预测是在list0和list ...
- H.266/VVC帧间预测技术学习:带有运动矢量差的Merge技术(Merge mode with MVD)
在VVC的扩展Merge模式当中,当前CU生成的Merge list中选择一个率失真代价值最小的候选项直接作为自己的运动信息.除了常规Merge模式,VVC还引入了带运动矢量差(Merge mode ...
- H.266/VVC帧间预测技术学习:解码端运动矢量细化(Decoder side motion vector refinement, DMVR)
解码端运动矢量细化(Decoder side motion vector refinement, DMVR) 为了提高Merge模式的MV的准确性,在VVC中使用了基于双边匹配(BM)的解码端运动矢量 ...
- H.266/VVC帧间预测技术学习:CU级双向加权预测(Bi-prediction with CU-level weight)
CU级双向加权预测(Bi-prediction with CU-level weight ,BCW) 在HEVC中,通过对从两个不同参考图片获得的两个预测信号求平均和/或使用两个不同运动矢量来生成双向 ...
- 【十三】 H.266/VVC | 帧间预测技术 | 解码端运动向量修正技术(DMVR)
目的:为了提高merge模式下双向预测MV的准确性 基本思路:双向预测是在list0和list1中分别寻找一个运动向量,然后将MV0和MV1所指向的预测块进行加权得到最终预测块,而DMVR技术不是直接 ...
- VVC帧间预测(十)帧间帧内联合预测CIIP
帧间帧内联合预测(Combined inter and intra prediction ,CIIP),在HEVC中一个CU在预测时要么使用帧内预测要么使用帧间预测,二者只能取其一.而VVC中提出的C ...
最新文章
- UIImageView图片视图的基本概念和使用方法
- ASP.NET MVC- Upload File的例子
- 查询sql一个字段重复的数据mysql_sql查询按两个字段查询重复记录
- Freemarker模板引擎
- java中如何调用属性_java – 如何从属性文件导入值并在注释中使用它?
- 关于RabbitMQ以及RabbitMQ和Spring的整合
- 你会采取什么方法改进你的测试用例_自闭症孩子在公共场所哭闹、撒泼打滚,你会采取什么措施?...
- EDR BYPASS
- win7共享xp打印机_别麻烦了!局域网一键共享工具
- prompt弹出输入框中文php,Prompt输入框
- 为你的Intel(R) HD Graphics 显卡安装适合Premiere的驱动并解决“无法为此计算机验证正在安装的驱动程序“问题
- iconfont矢量图标在小程序中的使用
- max3232ese_【MAX3232ESE+ PDF数据手册】_中文资料_引脚图及功能_(美信 Maxim Integrated)-采芯网...
- 渗透测试PTES标准流程(超详细)
- word删除空白页删不了怎么办?Word怎么删除空白页?
- 动手学深度学习 - 11.1. 数学符号 (notation)
- 99%的人看了它都会说这是一篇很全的tomcat服务❤️❤️[⭐建议收藏⭐]
- Winform MDI窗体子窗体显示区域大小
- 由sp单位引发的惨案
- 一步一步oa办公系统java,OA项目 一个OA办公系统的java源码 联合开发网 - pudn.com
热门文章
- 位运算的奇技淫巧:Bit Twiddling Hacks
- 服务器文件防止被扒,防止别人扒自己的网页方法
- PTC creo 3.0 安装教程
- 8Manage:大宗商品采购,专注构建企业采购信息化!
- voxsrc20_std_00-How many kinds of topology used in speaker recognition?
- 图解对称加密与非对称加密
- HTML5简明教程系列之HTML5基础(一)
- 人类最常见的25个认知偏误(一)
- 读《学会提问》有感(一)
- D-Link DIR645 1.03绕过认证查看配置文件漏洞复现与分析