xCheckRDCostInter函数主要是用来检查inter模式的,包括常规AMVP模式和Affine AMVP模式。该函数主要是通过调用predInterSearch实现对常规AMVP模式和Affine AMVP模式检查的,其调用关系如下所示:
在xCheckRDCostInter函数,遍历所有的BCW权重,对于每个BCW权重,调用predInterSearch找到最优的运动信息,并通过xEncodeInterResidual对预测残差进行变换量化。最后,通过xCalDebCost函数计算去块滤波后最终的Cost。
代码及注释如下:(基于VTM10.0)
void EncCu::xCheckRDCostInter( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
{
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();
// 判断你是否使用BCW,如果是B帧且开启BCW,则BCW待测数目为5,否则为1
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; //BCW用于宽度*高度大于等于256的CU
}
double curBestCost = bestCS->cost; //当前最佳的Cost
double equBcwCost = MAX_DOUBLE;
m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false;
// 遍历BCW所有权重
for( int bcwLoopIdx = 0; bcwLoopIdx < bcwLoopNum; bcwLoopIdx++ )
{
if( m_pcEncCfg->getUseBcwFast() ) //BCW快速算法
{
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;
}
}
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.affine
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);
m_pcInterSearch->predInterSearch( cu, partitioner ); //搜索最佳的帧间预测
bcwIdx = CU::getValidBcwIdx(cu);
if( testBcw && bcwIdx == BCW_DEFAULT ) // Enabled Bcw but the search results is uni. 已启用Bcw,但搜索结果为uni。
{
tempCS->initStructData(encTestMode.qp);
continue;
}
CHECK(!(testBcw || (!testBcw && bcwIdx == BCW_DEFAULT)), " !( bTestBcw || (!bTestBcw && bcwIdx == BCW_DEFAULT ) )");
bool isEqualUni = false;
if( m_pcEncCfg->getUseBcwFast() )
{
if( cu.firstPU->interDir != 3 && testBcw == 0 )
{
isEqualUni = true;
}
}
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;
}
} // for( UChar bcwLoopIdx = 0; bcwLoopIdx < bcwLoopNum; bcwLoopIdx++ )
if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE )
{
xCalDebCost( *bestCS, partitioner );
}
}
xCheckRDCostInterIMV和xCheckRDCostInter函数类似,不同的是,xCheckRDCostInterIMV函数会提前设置cu.imv(具体细节没看懂),cu.imv的作用如下:
- 对于常规AMVP模式,imv的值为0 1 2 3分别代表1/4像素精度、整像素精度、4像素精度和1/2像素精度
- 对于Affine AMVP模式,imv的值为0 1 2分别代表1/4像素精度、1/16像素精度和整像素精度
const MvPrecision Mv::m_amvrPrecision[4] = { MV_PRECISION_QUARTER, MV_PRECISION_INT, MV_PRECISION_4PEL, MV_PRECISION_HALF }; // for cu.imv=0, 1, 2 and 3
const MvPrecision Mv::m_amvrPrecAffine[3] = { MV_PRECISION_QUARTER, MV_PRECISION_SIXTEENTH, MV_PRECISION_INT }; // for cu.imv=0, 1 and 2
bool EncCu::xCheckRDCostInterIMV(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode, double &bestIntPelCost)
{
int iIMV = int( ( encTestMode.opts & ETO_IMV ) >> ETO_IMV_SHIFT ); //设置IMV
m_pcInterSearch->setAffineModeSelected(false);
// Only Half-Pel, int-Pel, 4-Pel and fast 4-Pel allowed
CHECK(iIMV < 1 || iIMV > 4, "Unsupported IMV Mode");
const bool testAltHpelFilter = iIMV == 4;
// Fast 4-Pel Mode
m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false;
EncTestMode encTestModeBase = encTestMode; // copy for clearing non-IMV options
encTestModeBase.opts = EncTestModeOpts( encTestModeBase.opts & ETO_IMV ); // clear non-IMV options (is that intended?)
tempCS->initStructData( encTestMode.qp );
m_pcInterSearch->resetBufferedUniMotions();
int bcwLoopNum = (tempCS->slice->isInterB() ? BCW_NUM : 1);
bcwLoopNum = (tempCS->slice->getSPS()->getUseBcw() ? bcwLoopNum : 1);
if( tempCS->area.lwidth() * tempCS->area.lheight() < BCW_SIZE_CONSTRAINT )
{
bcwLoopNum = 1;
}
bool validMode = false;
double curBestCost = bestCS->cost;
double equBcwCost = MAX_DOUBLE;
for( int bcwLoopIdx = 0; bcwLoopIdx < bcwLoopNum; bcwLoopIdx++ )
{
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( 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;
}
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.affine
cu.predMode = MODE_INTER;
cu.chromaQpAdj = m_cuChromaQpOffsetIdxPlus1;
cu.qp = encTestMode.qp;
CU::addPUs( cu );
if (testAltHpelFilter)
{
cu.imv = IMV_HPEL;
}
else
{
cu.imv = iIMV == 1 ? IMV_FPEL : IMV_4PEL;
}
// const MvPrecision Mv::m_amvrPrecision[4] = { MV_PRECISION_QUARTER, MV_PRECISION_INT, MV_PRECISION_4PEL, MV_PRECISION_HALF }; // for cu.imv=0, 1, 2 and 3
// const MvPrecision Mv::m_amvrPrecAffine[3] = { MV_PRECISION_QUARTER, MV_PRECISION_SIXTEENTH, MV_PRECISION_INT }; // for cu.imv=0, 1 and 2
bool testBcw;
uint8_t bcwIdx;
bool affineAmvrEanbledFlag = !testAltHpelFilter && cu.slice->getSPS()->getAffineAmvrEnabledFlag();
cu.BcwIdx = g_BcwSearchOrder[bcwLoopIdx];
bcwIdx = cu.BcwIdx;
testBcw = (bcwIdx != BCW_DEFAULT);
cu.firstPU->interDir = 10;
m_pcInterSearch->predInterSearch( cu, partitioner );
if ( cu.firstPU->interDir <= 3 )
{
bcwIdx = CU::getValidBcwIdx(cu);
}
else
{
return false;
}
if( m_pcEncCfg->getMCTSEncConstraint() && ( ( cu.firstPU->refIdx[L0] < 0 && cu.firstPU->refIdx[L1] < 0 ) || ( !( MCTSHelper::checkMvBufferForMCTSConstraint( *cu.firstPU ) ) ) ) )
{
// Do not use this mode
tempCS->initStructData( encTestMode.qp );
continue;
}
if( testBcw && bcwIdx == BCW_DEFAULT ) // Enabled Bcw but the search results is uni.
{
tempCS->initStructData(encTestMode.qp);
continue;
}
CHECK(!(testBcw || (!testBcw && bcwIdx == BCW_DEFAULT)), " !( bTestBcw || (!bTestBcw && bcwIdx == BCW_DEFAULT ) )");
bool isEqualUni = false;
if( m_pcEncCfg->getUseBcwFast() )
{
if( cu.firstPU->interDir != 3 && testBcw == 0 )
{
isEqualUni = true;
}
}
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;
}
}
xEncodeInterResidual( tempCS, bestCS, partitioner, encTestModeBase, 0
, 0
, &equBcwCost
);
if( cu.imv == IMV_FPEL && tempCS->cost < bestIntPelCost )
{
bestIntPelCost = tempCS->cost;
}
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;
}
validMode = true;
} // for( UChar bcwLoopIdx = 0; bcwLoopIdx < bcwLoopNum; bcwLoopIdx++ )
if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE )
{
xCalDebCost( *bestCS, partitioner );
}
return tempCS->slice->getSPS()->getAffineAmvrEnabledFlag() ? validMode : true;
}