此系列是为了记录自己学习VTM10.0的过程,目前正在看编码端。主要的参考文档有JVET-S2001-vH和JVET-S2002-v1。由于本人水平有限,出现的错误恳请大家指正。如果觉得我的文章对您有帮助的话,不妨点赞收藏加关注,谢谢!!
上次更新博客已是两个月前了,这两个月被internship反复折磨,但收获也是不少。现在终于结束了!!想到的第一件事就是赶快更博客,大家千万别取关/(ㄒoㄒ)/~。从这篇博客就开始讲有关帧间的内容了,不知道大家还记不记得之前的博客(VTM10.0代码学习11),末尾就是对当前CU进行各种模式的测试。看标题就知道这次要说的是xCheckRDCostInter()和CheckRDCostInterIMV()。这是有关AMVP的函数,大家也都知道帧间主要内容可以分成merge和AMVP。这两个函数还同时包括affine的AMVP,唯一的区别就是开不开启AMVR。帧内剩余的三个函数是:xCheckRDCostMerge2Nx2N(非affine的merge)、xCheckRDCostAffineMerge2Nx2N(affine的merge)、xCheckRDCostMergeGeo2Nx2N(GPM,是特殊的merge模式,比较复杂就用独立函数处理)。目前的打算是下一篇就开始讲xCheckRDCostMerge2Nx2N,同时也是本系列的终点了!!废话不多说,这就开始本篇内容
1. xCheckRDCostInter
由于无论是RA和LD的CTC都是不开启WP,所以帧间中有关WP的代码之后都会自动略去。
tempCS->initStructData( encTestMode.qp );
m_pcInterSearch->setAffineModeSelected(false);
if( tempCS->slice->getCheckLDC() )
{
m_bestBcwCost[0] = m_bestBcwCost[1] = std::numeric_limits<double>::max();
m_bestBcwIdx[0] = m_bestBcwIdx[1] = -1;
}
m_pcInterSearch->resetBufferedUniMotions();
int bcwLoopNum = (tempCS->slice->isInterB() ? BCW_NUM : 1);
bcwLoopNum = (tempCS->sps->getUseBcw() ? bcwLoopNum : 1);
if( tempCS->area.lwidth() * tempCS->area.lheight() < BCW_SIZE_CONSTRAINT )
{
bcwLoopNum = 1;
}
double curBestCost = bestCS->cost;//当前CU目前的最佳RDcost
double equBcwCost = MAX_DOUBLE;//不开启BCW时的RDcost
m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false;
initStructData():重置tempCS
setAffineModeSelected():重置m_affineModeSelected,为true表示affine被选为最佳模式
第一个if分支:当前帧为low-delay时会用到m_bestBcwCost和m_bestBcwIdx,所以需要先重置
bcwLoopNum:表示需要测试的BCW权重的次数
curBestCost:当前CU之前的最佳RDcost
equBcwCost:不开启BCW时的RDcost
for( int bcwLoopIdx = 0; bcwLoopIdx < bcwLoopNum; bcwLoopIdx++ )
{
}
for循环:遍历BCW的5种权重,具体参考1.1小节
1.1 循环遍历BCW
if( m_pcEncCfg->getUseBcwFast() )
{
auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >(m_modeCtrl);
if( blkCache )
{
bool isBestInter = blkCache->getInter(bestCS->area);
uint8_t bestBcwIdx = blkCache->getBcwIdx(bestCS->area);
if( isBestInter && g_BcwSearchOrder[bcwLoopIdx] != BCW_DEFAULT && g_BcwSearchOrder[bcwLoopIdx] != bestBcwIdx )
{
continue;
}
}
}
if( !tempCS->slice->getCheckLDC() )
{
if( bcwLoopIdx != 0 && bcwLoopIdx != 3 && bcwLoopIdx != 4 )
{
continue;
}
}
第一个if分支:CTC一般默认开启BCW的快速算法,这里表示当CU目前的最佳模式为帧间时,不测试均等权重和最佳BCW权重之外的权重
第二个if分支:如果当前帧不为low-delay时,只测试BCW的三种全权重,跳过其余两种
CodingUnit &cu = tempCS->addCU( tempCS->area, partitioner.chType );
partitioner.setCUData( cu );
cu.slice = tempCS->slice;
cu.tileIdx = tempCS->pps->getTileIdx( tempCS->area.lumaPos() );
cu.skip = false;
cu.mmvdSkip = false;
cu.predMode = MODE_INTER;
cu.chromaQpAdj = m_cuChromaQpOffsetIdxPlus1;
cu.qp = encTestMode.qp;
CU::addPUs( cu );
cu.BcwIdx = g_BcwSearchOrder[bcwLoopIdx];
uint8_t bcwIdx = cu.BcwIdx;
bool testBcw = (bcwIdx != BCW_DEFAULT);//为true表示开启BCW
这段主要用来设置CU
bcwIdx:当前测试的BCW权重
testBcw:为true表示开启BCW
m_pcInterSearch->predInterSearch( cu, partitioner );//进行AMVP的帧间预测
bcwIdx = CU::getValidBcwIdx(cu);
if( testBcw && bcwIdx == BCW_DEFAULT ) // Enabled Bcw but the search results is uni.
{
tempCS->initStructData(encTestMode.qp);
continue;
}
bool isEqualUni = false;
if( m_pcEncCfg->getUseBcwFast() )
{
if( cu.firstPU->interDir != 3 && testBcw == 0 )
{
isEqualUni = true;
}
}
predInterSearch():进行AMVP的帧间预测,具体参考第3大节,里面的RDcost都是化简的
isEqualUni:为true表示不开启BCW时最佳模式为单向预测
xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, 0
, 0
, &equBcwCost
);
if( g_BcwSearchOrder[bcwLoopIdx] == BCW_DEFAULT )
m_pcInterSearch->setAffineModeSelected((bestCS->cus.front()->affine && !(bestCS->cus.front()->firstPU->mergeFlag)));
tempCS->initStructData(encTestMode.qp);
double skipTH = MAX_DOUBLE;
skipTH = (m_pcEncCfg->getUseBcwFast() ? 1.05 : MAX_DOUBLE);
if( equBcwCost > curBestCost * skipTH )
{
break;
}
if( m_pcEncCfg->getUseBcwFast() )
{
if( isEqualUni == true && m_pcEncCfg->getIntraPeriod() == -1 )
{
break;
}
}
if( g_BcwSearchOrder[bcwLoopIdx] == BCW_DEFAULT && xIsBcwSkip(cu) && m_pcEncCfg->getUseBcwFast() )
{
break;
}
xEncodeInterResidual():进行RDcost计算,抉择出最佳帧间模式,还包含对SBT的RD决策过程,实际RDcost计算是通过调用encodeResAndCalcRdInterCU。encodeResAndCalcRdInterCU包含对MTS的RD决策过程
第一个if分支:设置m_affineModeSelected
第二个if分支:不开启BCW时的RDcost如果超过之前的最佳RDcost的1.05倍,就跳过之后BCW的权重测试
第三个if分支:当序列中只有第一帧是I帧时,如果不开启BCW时最佳模式为单向预测,就跳过之后BCW的权重测试
第四个if分支:满足一定条件,就跳过之后BCW的权重测试
2. xCheckRDCostInterIMV
与xCheckRDCostInter差别不大,就只讲不同点
int iIMV = int( ( encTestMode.opts & ETO_IMV ) >> ETO_IMV_SHIFT );
const bool testAltHpelFilter = iIMV == 4;//是否在测试半像素精度
testAltHpelFilter:是否在测试半像素精度
2.1 循环遍历BCW
if( m_pcEncCfg->getUseBcwFast() && tempCS->slice->getCheckLDC() && g_BcwSearchOrder[bcwLoopIdx] != BCW_DEFAULT
&& (m_bestBcwIdx[0] >= 0 && g_BcwSearchOrder[bcwLoopIdx] != m_bestBcwIdx[0])
&& (m_bestBcwIdx[1] >= 0 && g_BcwSearchOrder[bcwLoopIdx] != m_bestBcwIdx[1]))
{
continue;
}
如果当前帧是low-delay,只能测试不开启AMVR时最好的两次RDcost下的BCW权重还有默认权重
if (testAltHpelFilter)
{
cu.imv = IMV_HPEL;
}
else
{
cu.imv = iIMV == 1 ? IMV_FPEL : IMV_4PEL;
}
非affine下imv可以为0,1,2,3,分别为1/4,1,4,1/2像素精度;affine下imv可以为0,1,2,分别为1/4,1/16,1像素精度
bool affineAmvrEanbledFlag = !testAltHpelFilter && cu.slice->getSPS()->getAffineAmvrEnabledFlag();
if ( !CU::hasSubCUNonZeroMVd( cu ) && !CU::hasSubCUNonZeroAffineMVd( cu ) )
{
if (m_modeCtrl->useModeResult(encTestModeBase, tempCS, partitioner))
{
std::swap(tempCS, bestCS);
// store temp best CI for next CU coding
m_CurrCtx->best = m_CABACEstimator->getCtx();
}
if ( affineAmvrEanbledFlag )
{
tempCS->initStructData( encTestMode.qp );
continue;
}
else
{
return false;
}
}
这段有点不明白就先不说了
3. predInterSearch
3.1 预备变量
CodingStructure& cs = *cu.cs;
//AMVP列表的暂时变量
AMVPInfo amvp[2];
Mv cMvSrchRngLT;
Mv cMvSrchRngRB;
Mv cMvZero;
// RPL0/1各自单向预测时的最佳情况下的MV
Mv cMv[2];
// 双向预测时的最佳情况下的MV
Mv cMvBi[2];
Mv cMvTemp[