VVC在HEVC的基础上增加了很多新的帧间预测工具,在VTM5里新增的帧间预测工具如下:
-
Extended merge prediction
-
Merge mode with MVD (MMVD)
-
AMVP mode with symmetric MVD signalling
-
Affine motion compensated prediction
-
Subblock-based temporal motion vector prediction (SbTMVP)
-
Adaptive motion vector resolution (AMVR)
-
Motion field storage: 1/16th luma sample MV storage and 8x8 motion field compression
-
Bi-prediction with CU-level weight (BCW)
-
Bi-directional optical flow (BDOF)
-
Decoder side motion vector refinement (DMVR)
-
Triangle partition prediction
-
Combined inter and intra prediction (CIIP)
在接下来的系列文章中将分别介绍这些工具,本文将介绍第一个帧间预测工具扩展的merge模式(Extended merge prediction)
1、扩展的merge模式(Extended merge prediction)
merge是在HEVC中新提出的一种MV预测技术,利用时域或空域相邻块的MV对当前块MV进行预测。HEVC中merge模式会为当前PU建立一个MV候选列表,列表中有5个候选MV,通过遍历这5个MV,计算率失真代价最小的MV作为当前PU的MV(不存在MVD)。在码流中只需传输最优MV在列表中的索引即可。
HEVC中merge利用时域或空域相邻块构建MV列表,且列表中只有5个候选MV。VVC对此进行了扩展,候选列表merge list中最多可以有6个候选MV,候选MV有5种类型,顺序如下。
-
Spatial MVP from spatial neighbour CUs 相邻块的空域MVP
-
Temporal MVP from collocated CUs 同位块的时域MVP
-
History-based MVP from an FIFO table 基于历史信息构建的FIFO表的MVP
-
Pairwise average MVP 成对的平均MVP
-
Zero MVs. 0向量
1.1、空域候选列表构建
空域候选列表建立方式和HEVC一样。如下图A0,A1,B0,B1,B2是当前CU空域邻近块。VVC中空域最多提供4个候选MV。即最多使用这5个候选块中的4个候选块的运动信息,候选列表按照A1->B1->B0->A0->B2的顺序建立,其中B2是替补,只有前4个有一个或多个不存在时(例如不再同一个slice或tile或使用帧内编码)才将B2加入候选列表。当候选列表加入A1后,后面新加入候选项时要进行冗余性检查以免新加入的候选项的运动信息和已有项的相同。为了减少计算复杂度,冗余性检查时不会和已有的每一项进行比较,只比下图中较箭头连接的项。
1.2、时域候选列表构建
时域候选列表建立方式和HEVC一样。与空域情形不同,时域候选列表不能直接使用候选块的运动信息,需要根据位置关系进行相应比例伸缩。如下图所示,curr_CU是当前CU,col_CU是其同位CU,tb是当前图像与参考图像的距离(用POC度量),td是同位图像与其参考图像的距离。则候选MV计算如下:
curMV=(td/tb)*colMV
VVC中时域最多提供1个候选MV。如下图C0位置的候选MV由其同位CU的MV伸缩得到,如果C0的同位CU不能用,则用C1的同位CU代替。
1.3、History-based merge candidates derivation
history-based MVP (HMVP)的merge候选项在空域候选和时域候选之后加入merge list。HMVP候选项来自于一个FIFO的表,这个表是通过已编码块的运动信息构建的。每到新的一行CTU时这个表就要重置(清空)。每遇到一个帧间编码的CU(非子块)时,它相关的运动信息会加到这个表的最后一项成为一个新的HMVP候选项。
在VTM5中,HMVP表大小为6。每当插入一个新的候选项时,首先进行冗余性检查即检查待插入项的运动信息和表中已有项的运动信息是否相同,如果不相同则按FIFO规则插入,如果相同则将相同的HMVP从表中移除,其后所有项向前移动一位。
HMVP表构建好后就可以将其中的HMVP候选项插入merge list,对HMVP表中的候选项从后向前依次进行冗余性检查(即检查该项和merge list中的空域、时域候选项运动信息是否相同),检查通过即插入merge list。
为了减少冗余性检查的操作进行了以下简化操作:
-
用于构建merge list的HMVP候选项数量设为(N<=4)?M:(8-N),其中N表示merge list中已有项的数目,M表示HMVP表中候选项数目。
-
一旦merge list中候选项数目达到最大允许候选项数减1(即6-1=5),则停止从HMVP生成merge候选项过程。
1.4、Pair-wise average merge candidates derivation
逐对平均候选项是指对merge list里的已有候选项按照预定义的组队关系计算平均值,预定义的组队关系为{(0, 1), (0, 2), (1, 2), (0, 3), (1, 3), (2, 3)},数字代表候选项在merge list里的索引。L0和L1的平均向量分别单独计算,如果两个向量在某个列表里都有效则即使它们指向不同的参考图像也计算它们的平均,如果只有一个向量在某个列表里有效则直接使用它,如果某个列表没有有效运动向量则该列表无效。
如果计算完平均候选项后merge list还未填满,则用0向量填充。
2、总结
以上便是merge list的构建过程,merge list长度为6,首先寻找空域候选项(最多4个),然后时域候选项(最多1个),然后基于历史信息的候选项,然后是平均候选项,如果merge list还未填满则用0向量填充。在上面的过程中如果merge list填满则不进行后面的步骤。
VVC的merge list构建与HEVC最大的不同就是HEVC中的merge list只含空域候选项和时域候选项。
当前CU的merge list构建完毕后,遍历它的6个候选项进行率失真代价计算,选择率失真代价最小的候选项直接作为当前CU的MV。
merge list构建的代码如下,对应位置处有注释
void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
int mmvdList,
const int& mrgCandIdx )
{
const CodingStructure &cs = *pu.cs;
const Slice &slice = *pu.cs->slice;
const uint32_t maxNumMergeCand = slice.getMaxNumMergeCand();
const bool canFastExit = pu.cs->pps->getLog2ParallelMergeLevelMinus2() == 0;
#if !JVET_L0090_PAIR_AVG
// this variable is unused if remove HEVC combined candidates
bool isCandInter[MRG_MAX_NUM_CANDS];
#endif
for (uint32_t ui = 0; ui < maxNumMergeCand; ++ui)
{
#if !JVET_L0090_PAIR_AVG
isCandInter[ui] = false;
#endif
mrgCtx.GBiIdx[ui] = GBI_DEFAULT;
mrgCtx.interDirNeighbours[ui] = 0;
mrgCtx.mrgTypeNeighbours [ui] = MRG_TYPE_DEFAULT_N;
mrgCtx.mvFieldNeighbours[(ui << 1) ].refIdx = NOT_VALID;
mrgCtx.mvFieldNeighbours[(ui << 1) + 1].refIdx = NOT_VALID;
}
mrgCtx.numValidMergeCand = maxNumMergeCand;
// compute the location of the current PU
int cnt = 0;
#if JVET_N0266_SMALL_BLOCKS
const Position posLT = pu.Y().topLeft();
const Position posRT = pu.Y().topRight();
const Position posLB = pu.Y().bottomLeft();
#else
const Position posLT = pu.shareParentPos;
const Position posRT = pu.shareParentPos.offset(pu.shareParentSize.width - 1, 0);
const Position posLB = pu.shareParentPos.offset(0, pu.shareParentSize.height - 1);
#endif
MotionInfo miAbove, miLeft, miAboveLeft, miAboveRight, miBelowLeft;
//left
const PredictionUnit* puLeft = cs.getPURestricted( posLB.offset( -1, 0 ), pu, pu.chType );
const bool isAvailableA1 = puLeft && isDiffMER( pu, *puLeft ) && pu.cu != puLeft->cu && CU::isInter( *puLeft->cu );
//!<A1块,左侧的块
if( isAvailableA1 )
{
miLeft = puLeft->getMotionInfo( posLB.offset(-1, 0) );
#if !JVET_L0090_PAIR_AVG
isCandInter[cnt] = true;
#endif
// get Inter Dir
mrgCtx.interDirNeighbours[cnt] = miLeft.interDir;
mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puLeft->cu->GBiIdx : GBI_DEFAULT;
// get Mv from Left
mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miLeft.mv[0], miLeft.refIdx[0]);
if (slice.isInterB())
{
mrgCtx.mvFieldNeighbours[(cnt << 1) + 1].setMvField(miLeft.mv[1], miLeft.refIdx[1]);
}
if (mrgCandIdx == cnt && canFastExit)
{
return;
}
cnt++;
}
// early termination
if (cnt == maxNumMergeCand)
{
return;
}
// above
const PredictionUnit *puAbove = cs.getPURestricted( posRT.offset( 0, -1 ), pu, pu.chType );
bool isAvailableB1 = puAbove && isDiffMER( pu, *puAbove ) && pu.cu != puAbove->cu && CU::isInter( *puAbove->cu );
//!<B1块,右侧的块
if( isAvailableB1 )
{
miAbove = puAbove->getMotionInfo( posRT.offset( 0, -1 ) );
//!<冗余性检查,B1只和A1比较
if( !isAvailableA1 || ( miAbove != miLeft ) )
{
#if !JVET_L0090_PAIR_AVG
isCandInter[cnt] = true;
#endif
// get Inter Dir
mrgCtx.interDirNeighbours[cnt] = miAbove.interDir;
// get Mv from Above
mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAbove->cu->GBiIdx : GBI_DEFAULT;
mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miAbove.mv[0], miAbove.refIdx[0] );
if( slice.isInterB() )
{
mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miAbove.mv[1], miAbove.refIdx[1] );
}
if (mrgCandIdx == cnt && canFastExit)
{
return;
}
cnt++;
}
}
// early termination
if( cnt == maxNumMergeCand )
{
return;
}
int spatialCandPos = cnt;
// above right
const PredictionUnit *puAboveRight = cs.getPURestricted( posRT.offset( 1, -1 ), pu, pu.chType );
bool isAvailableB0 = puAboveRight && isDiffMER( pu, *puAboveRight ) && CU::isInter( *puAboveRight->cu );
//!<B0块,右上角的块
if( isAvailableB0 )
{
miAboveRight = puAboveRight->getMotionInfo( posRT.offset( 1, -1 ) );
#if HM_JEM_MERGE_CANDS
if( ( !isAvailableB1 || ( miAbove != miAboveRight ) ) && ( !isAvailableA1 || ( miLeft != miAboveRight ) ) )
#else//!<冗余性检查,B0只和B1比较
if( !isAvailableB1 || ( miAbove != miAboveRight ) )
#endif
{
#if !JVET_L0090_PAIR_AVG
isCandInter[cnt] = true;
#endif
// get Inter Dir
mrgCtx.interDirNeighbours[cnt] = miAboveRight.interDir;
// get Mv from Above-right
mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAboveRight->cu->GBiIdx : GBI_DEFAULT;
mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miAboveRight.mv[0], miAboveRight.refIdx[0] );
if( slice.isInterB() )
{
mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miAboveRight.mv[1], miAboveRight.refIdx[1] );
}
if (mrgCandIdx == cnt && canFastExit)
{
return;
}
cnt++;
}
}
// early termination
if( cnt == maxNumMergeCand )
{
return;
}
//left bottom
const PredictionUnit *puLeftBottom = cs.getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType );
bool isAvailableA0 = puLeftBottom && isDiffMER( pu, *puLeftBottom ) && CU::isInter( *puLeftBottom->cu );
//!<A0块,左下角块
if( isAvailableA0 )
{
miBelowLeft = puLeftBottom->getMotionInfo( posLB.offset( -1, 1 ) );
#if HM_JEM_MERGE_CANDS
if( ( !isAvailableA1 || ( miBelowLeft != miLeft ) ) && ( !isAvailableB1 || ( miBelowLeft != miAbove ) ) && ( !isAvailableB0 || ( miBelowLeft != miAboveRight ) ) )
#else//!<冗余性检查,A0只和A1比较
if( !isAvailableA1 || ( miBelowLeft != miLeft ) )
#endif
{
#if !JVET_L0090_PAIR_AVG
isCandInter[cnt] = true;
#endif
// get Inter Dir
mrgCtx.interDirNeighbours[cnt] = miBelowLeft.interDir;
mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puLeftBottom->cu->GBiIdx : GBI_DEFAULT;
// get Mv from Bottom-Left
mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miBelowLeft.mv[0], miBelowLeft.refIdx[0] );
if( slice.isInterB() )
{
mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miBelowLeft.mv[1], miBelowLeft.refIdx[1] );
}
if (mrgCandIdx == cnt && canFastExit)
{
return;
}
cnt++;
}
}
// early termination
if( cnt == maxNumMergeCand )
{
return;
}
//!<B2左上角块,只有空域候选项不够4时才考虑
// above left
if ( cnt < 4 )
{
const PredictionUnit *puAboveLeft = cs.getPURestricted( posLT.offset( -1, -1 ), pu, pu.chType );
bool isAvailableB2 = puAboveLeft && isDiffMER( pu, *puAboveLeft ) && CU::isInter( *puAboveLeft->cu );
if( isAvailableB2 )
{
miAboveLeft = puAboveLeft->getMotionInfo( posLT.offset( -1, -1 ) );
#if HM_JEM_MERGE_CANDS
if( ( !isAvailableA1 || ( miLeft != miAboveLeft ) ) && ( !isAvailableB1 || ( miAbove != miAboveLeft ) ) && ( !isAvailableA0 || ( miBelowLeft != miAboveLeft ) ) && ( !isAvailableB0 || ( miAboveRight != miAboveLeft ) ) )
#else//!<冗余性检查,B2和A1、B1比较
if( ( !isAvailableA1 || ( miLeft != miAboveLeft ) ) && ( !isAvailableB1 || ( miAbove != miAboveLeft ) ) )
#endif
{
#if !JVET_L0090_PAIR_AVG
isCandInter[cnt] = true;
#endif
// get Inter Dir
mrgCtx.interDirNeighbours[cnt] = miAboveLeft.interDir;
mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAboveLeft->cu->GBiIdx : GBI_DEFAULT;
// get Mv from Above-Left
mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miAboveLeft.mv[0], miAboveLeft.refIdx[0] );
if( slice.isInterB() )
{
mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miAboveLeft.mv[1], miAboveLeft.refIdx[1] );
}
if (mrgCandIdx == cnt && canFastExit)
{
return;
}
cnt++;
}
}
}
// early termination
if (cnt == maxNumMergeCand)
{
return;
}
#if JVET_N0213_TMVP_REMOVAL
if (slice.getEnableTMVPFlag() && (pu.lumaSize().width + pu.lumaSize().height > 12))
#else
if (slice.getEnableTMVPFlag())
#endif
{
//>> MTK colocated-RightBottom
// offset the pos to be sure to "point" to the same position the uiAbsPartIdx would've pointed to
#if JVET_N0266_SMALL_BLOCKS
Position posRB = pu.Y().bottomRight().offset( -3, -3 );
#else
Position posRB = pu.shareParentPos.offset(pu.shareParentSize.width-3, pu.shareParentSize.height - 3);
#endif
const PreCalcValues& pcv = *cs.pcv;
Position posC0;
#if JVET_N0266_SMALL_BLOCKS
Position posC1 = pu.Y().center();
#else
Position posC1 = pu.shareParentPos.offset((pu.shareParentSize.width/2), (pu.shareParentSize.height/2));
#endif
bool C0Avail = false;
#if !JVET_N0266_SMALL_BLOCKS
bool C1Avail = (posC1.x < pcv.lumaWidth) && (posC1.y < pcv.lumaHeight);
#endif
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候选项
Mv cColMv;
int iRefIdx = 0;
int dir = 0;
unsigned uiArrayAddr = cnt;
bool bExistMV = ( C0Avail && getColocatedMVP(pu, REF_PIC_LIST_0, posC0, cColMv, iRefIdx ) ) //!<获得伸缩后的同位MV
#if JVET_N0266_SMALL_BLOCKS
|| getColocatedMVP( pu, REF_PIC_LIST_0, posC1, cColMv, iRefIdx );
#else
|| ( C1Avail && getColocatedMVP(pu, REF_PIC_LIST_0, posC1, cColMv, iRefIdx ));
#endif
if (bExistMV)
{
dir |= 1;
mrgCtx.mvFieldNeighbours[2 * uiArrayAddr].setMvField(cColMv, iRefIdx);
}
if (slice.isInterB())
{
bExistMV = ( C0Avail && getColocatedMVP(pu, REF_PIC_LIST_1, posC0, cColMv, iRefIdx ) )
#if JVET_N0266_SMALL_BLOCKS
|| getColocatedMVP( pu, REF_PIC_LIST_1, posC1, cColMv, iRefIdx );
#else
|| (C1Avail && getColocatedMVP(pu, REF_PIC_LIST_1, posC1, cColMv, iRefIdx ) );
#endif
if (bExistMV)
{
dir |= 2;
mrgCtx.mvFieldNeighbours[2 * uiArrayAddr + 1].setMvField(cColMv, iRefIdx);
}
}
if( dir != 0 )
{
bool addTMvp = true;
#if HM_JEM_MERGE_CANDS
int iSpanCand = cnt;
for( int i = 0; i < iSpanCand; i++ )
{
if( mrgCtx.interDirNeighbours[ i ] == dir &&
mrgCtx.mvFieldNeighbours [ i << 1 ] == mrgCtx.mvFieldNeighbours[ uiArrayAddr << 1 ] &&
mrgCtx.mvFieldNeighbours [( i << 1 ) + 1] == mrgCtx.mvFieldNeighbours[( uiArrayAddr << 1 ) + 1] )
{
addTMvp = false;
}
}
#endif
if( addTMvp )
{
mrgCtx.interDirNeighbours[uiArrayAddr] = dir;
#if !JVET_L0090_PAIR_AVG
isCandInter [uiArrayAddr] = true;
#endif
mrgCtx.GBiIdx[uiArrayAddr] = GBI_DEFAULT;
if (mrgCandIdx == cnt && canFastExit)
{
return;
}
cnt++;
}
}
}
// early termination
if (cnt == maxNumMergeCand)
{
return;
}
//!<最大候选数量减1,HMVP
int maxNumMergeCandMin1 = maxNumMergeCand - 1;
if (cnt != maxNumMergeCandMin1)
{
bool isAvailableSubPu = false;
unsigned subPuMvpPos = 0;
#if JVET_L0090_PAIR_AVG
#if JVET_N0266_SMALL_BLOCKS
bool isShared = false;
#else
bool isShared = ((pu.Y().lumaSize().width != pu.shareParentSize.width) || (pu.Y().lumaSize().height != pu.shareParentSize.height));
#endif //!<添加HMVP
bool bFound = addMergeHMVPCand(cs, mrgCtx, canFastExit
, mrgCandIdx
, maxNumMergeCandMin1, cnt
, spatialCandPos
, isAvailableSubPu, subPuMvpPos
, CU::isIBC(*pu.cu)
, isShared
);
#else
bool bFound = addMergeHMVPCand(slice, mrgCtx, isCandInter, canFastExit
, (mmvdList != 0 && mrgCandIdx != -1) ? (const int)mrgCandIdxIBC : mrgCandIdx
, maxNumMergeCandMin1, cnt, cnt, isAvailableSubPu, subPuMvpPos
, mmvdList
);
#endif
if (bFound)
{
return;
}
}
#if JVET_L0090_PAIR_AVG
// pairwise-average candidates
{
//!<平均MV
if (cnt > 1 && cnt < maxNumMergeCand)
{
mrgCtx.mvFieldNeighbours[cnt * 2].setMvField( Mv( 0, 0 ), NOT_VALID );
mrgCtx.mvFieldNeighbours[cnt * 2 + 1].setMvField( Mv( 0, 0 ), NOT_VALID );
// calculate average MV for L0 and L1 seperately
unsigned char interDir = 0;
//!<两个列表分别计算平均向量
for( int refListId = 0; refListId < (slice.isInterB() ? 2 : 1); refListId++ )
{
const short refIdxI = mrgCtx.mvFieldNeighbours[0 * 2 + refListId].refIdx;
const short refIdxJ = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].refIdx;
//!<两个向量都无效的情况
// both MVs are invalid, skip
if( (refIdxI == NOT_VALID) && (refIdxJ == NOT_VALID) )
{
continue;
}
//!<两个向量都有效的情况
interDir += 1 << refListId;
// both MVs are valid, average these two MVs
if( (refIdxI != NOT_VALID) && (refIdxJ != NOT_VALID) )
{
const Mv& MvI = mrgCtx.mvFieldNeighbours[0 * 2 + refListId].mv;
const Mv& MvJ = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].mv;
//!<求平均,通过移位实现
// average two MVs
Mv avgMv = MvI;
avgMv += MvJ;
roundAffineMv(avgMv.hor, avgMv.ver, 1);
mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( avgMv, refIdxI );
}//!<只有一个向量有效的情况
// only one MV is valid, take the only one MV
else if( refIdxI != NOT_VALID )
{
Mv singleMv = mrgCtx.mvFieldNeighbours[0 * 2 + refListId].mv;
mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( singleMv, refIdxI );
}
else if( refIdxJ != NOT_VALID )
{
Mv singleMv = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].mv;
mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( singleMv, refIdxJ );
}
}
mrgCtx.interDirNeighbours[cnt] = interDir;
if( interDir > 0 )
{
cnt++;
}
}
// early termination
if( cnt == maxNumMergeCand )
{
return;
}
}
#endif
uint32_t uiArrayAddr = cnt;
#if !JVET_L0090_PAIR_AVG
uint32_t uiCutoff = std::min( uiArrayAddr, 3u );
if (slice.isInterB())
{
static const uint32_t NUM_PRIORITY_LIST = 12;
static const uint32_t uiPriorityList0[NUM_PRIORITY_LIST] = { 0 , 1, 0, 2, 1, 2, 0, 3, 1, 3, 2, 3 };
static const uint32_t uiPriorityList1[NUM_PRIORITY_LIST] = { 1 , 0, 2, 0, 2, 1, 3, 0, 3, 1, 3, 2 };
for (int idx = 0; idx < uiCutoff * (uiCutoff - 1) && uiArrayAddr != maxNumMergeCand; idx++)
{
CHECK( idx >= NUM_PRIORITY_LIST, "Invalid priority list number" );
int i = uiPriorityList0[idx];
int j = uiPriorityList1[idx];
if (isCandInter[i] && isCandInter[j] && (mrgCtx.interDirNeighbours[i] & 0x1) && (mrgCtx.interDirNeighbours[j] & 0x2))
{
isCandInter[uiArrayAddr] = true;
mrgCtx.interDirNeighbours[uiArrayAddr] = 3;
mrgCtx.GBiIdx[uiArrayAddr] = ((mrgCtx.interDirNeighbours[uiArrayAddr] == 3)) ? CU::deriveGbiIdx(mrgCtx.GBiIdx[i], mrgCtx.GBiIdx[j]) : GBI_DEFAULT;
// get Mv from cand[i] and cand[j]
mrgCtx.mvFieldNeighbours[ uiArrayAddr << 1 ].setMvField(mrgCtx.mvFieldNeighbours[ i << 1 ].mv, mrgCtx.mvFieldNeighbours[ i << 1 ].refIdx);
mrgCtx.mvFieldNeighbours[(uiArrayAddr << 1) + 1].setMvField(mrgCtx.mvFieldNeighbours[(j << 1) + 1].mv, mrgCtx.mvFieldNeighbours[(j << 1) + 1].refIdx);
int iRefPOCL0 = slice.getRefPOC(REF_PIC_LIST_0, mrgCtx.mvFieldNeighbours[(uiArrayAddr << 1) ].refIdx);
int iRefPOCL1 = slice.getRefPOC(REF_PIC_LIST_1, mrgCtx.mvFieldNeighbours[(uiArrayAddr << 1) + 1].refIdx);
if( iRefPOCL0 == iRefPOCL1 && mrgCtx.mvFieldNeighbours[( uiArrayAddr << 1 )].mv == mrgCtx.mvFieldNeighbours[( uiArrayAddr << 1 ) + 1].mv )
{
isCandInter[uiArrayAddr] = false;
}
else
{
uiArrayAddr++;
}
}
}
}
// early termination
if (uiArrayAddr == maxNumMergeCand)
{
return;
}
#endif
int iNumRefIdx = slice.isInterB() ? std::min(slice.getNumRefIdx(REF_PIC_LIST_0), slice.getNumRefIdx(REF_PIC_LIST_1)) : slice.getNumRefIdx(REF_PIC_LIST_0);
int r = 0;
int refcnt = 0;
while (uiArrayAddr < maxNumMergeCand)
{
#if !JVET_L0090_PAIR_AVG
isCandInter [uiArrayAddr ] = true;
#endif
mrgCtx.interDirNeighbours [uiArrayAddr ] = 1;
mrgCtx.GBiIdx [uiArrayAddr ] = GBI_DEFAULT;
mrgCtx.mvFieldNeighbours [uiArrayAddr << 1].setMvField(Mv(0, 0), r); //!<填充0向量
if (slice.isInterB())
{
mrgCtx.interDirNeighbours [ uiArrayAddr ] = 3;
mrgCtx.mvFieldNeighbours [(uiArrayAddr << 1) + 1].setMvField(Mv(0, 0), r);
}
if ( mrgCtx.interDirNeighbours[uiArrayAddr] == 1 && pu.cs->slice->getRefPic(REF_PIC_LIST_0, mrgCtx.mvFieldNeighbours[uiArrayAddr << 1].refIdx)->getPOC() == pu.cs->slice->getPOC())
{
mrgCtx.mrgTypeNeighbours[uiArrayAddr] = MRG_TYPE_IBC;
}
uiArrayAddr++;
if (refcnt == iNumRefIdx - 1)
{
r = 0;
}
else
{
++r;
++refcnt;
}
}
mrgCtx.numValidMergeCand = uiArrayAddr;
}
参考
JVET-N1002
JVET-L0266
JVET-L0090
感兴趣的请关注微信公众号Video Coding