帧间预测的模式决策过程中,常用的率失真决策一个流程是:设置模式参数->按此模式进行帧间预测(运动补偿)->编码模式信息与残差->计算码率r->计算重建误差d->计算率rd cost并判断是否使用此模式。VTM中将编码模式信息与残差及其后续部分封装到函数xEncodeInterResidual()中。xEncodeInterResidual()中会配置sbt参数并判断是否使用sbt。各模式配置好参数后,主要通过调用InterSearch::encodeResAndCalcRdInterCU()完成率失真计算,并调用xEncodeDontSplit()等函数添加块划分、dQP、色度QP偏执占用的比特,最终调用xCheckBestMode()判断是否使用该模式。
void EncCu::xEncodeInterResidual( CodingStructure *&tempCS
, CodingStructure *&bestCS
, Partitioner &partitioner
, const EncTestMode& encTestMode
, int residualPass
, bool* bestHasNonResi
, double* equBcwCost
)
{
...
{
reloadCU = true; // enable cu reloading
cu->skip = false;
cu->sbtInfo = 0;
const bool skipResidual = residualPass == 1;
if( skipResidual || histBestSbt == MAX_UCHAR || !CU::isSbtMode( histBestSbt ) )
{
m_pcInterSearch->encodeResAndCalcRdInterCU( *tempCS, partitioner, skipResidual );
if (tempCS->slice->getSPS()->getUseColorTrans())
{
bestCS->tmpColorSpaceCost = tempCS->tmpColorSpaceCost;
bestCS->firstColorSpaceSelected = tempCS->firstColorSpaceSelected;
}
numRDOTried += mtsAllowed ? 2 : 1;
xEncodeDontSplit( *tempCS, partitioner );
xCheckDQP( *tempCS, partitioner );
#if JVET_Q0267_RESET_CHROMA_QP_OFFSET
xCheckChromaQPOffset( *tempCS, partitioner );
#endif
...
currBestCost = tempCS->cost;
sbtOffCost = tempCS->cost;
sbtOffDist = tempCS->dist;
sbtOffRootCbf = cu->rootCbf;
currBestSbt = CU::getSbtInfo(cu->firstTU->mtsIdx[COMPONENT_Y] > MTS_SKIP ? SBT_OFF_MTS : SBT_OFF_DCT, 0);
currBestTrs = cu->firstTU->mtsIdx[COMPONENT_Y];
#if WCG_EXT
DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda( true ) );
#else
DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda() );
#endif
xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
}
...
for( int sbtModeIdx = 0; sbtModeIdx < numSbtRdo; sbtModeIdx++ )
{
uint8_t sbtMode = m_pcInterSearch->getSbtRdoOrder( sbtModeIdx );
uint8_t sbtIdx = CU::getSbtIdxFromSbtMode( sbtMode );
uint8_t sbtPos = CU::getSbtPosFromSbtMode( sbtMode );
...
//we need to restart the distortion for the new tempCS, the bit count and the cost
tempCS->dist = 0;
tempCS->fracBits = 0;
tempCS->cost = MAX_DOUBLE;
cu->skip = false;
//set SBT info
cu->setSbtIdx( sbtIdx );
cu->setSbtPos( sbtPos );
//try residual coding
m_pcInterSearch->encodeResAndCalcRdInterCU( *tempCS, partitioner, skipResidual );
if (tempCS->slice->getSPS()->getUseColorTrans())
{
bestCS->tmpColorSpaceCost = tempCS->tmpColorSpaceCost;
bestCS->firstColorSpaceSelected = tempCS->firstColorSpaceSelected;
}
numRDOTried++;
xEncodeDontSplit( *tempCS, partitioner );
xCheckDQP( *tempCS, partitioner );
#if JVET_Q0267_RESET_CHROMA_QP_OFFSET
xCheckChromaQPOffset( *tempCS, partitioner );
#endif
if( NULL != bestHasNonResi && ( bestCostInternal > tempCS->cost ) )
{
bestCostInternal = tempCS->cost;
if( !( tempCS->getPU( partitioner.chType )->ciipFlag ) )
*bestHasNonResi = !cu->rootCbf;
}
if( tempCS->cost < currBestCost )
{
currBestSbt = cu->sbtInfo;
currBestTrs = tempCS->tus[cu->sbtInfo ? cu->getSbtPos() : 0]->mtsIdx[COMPONENT_Y];
assert( currBestTrs == 0 || currBestTrs == 1 );
currBestCost = tempCS->cost;
}
#if WCG_EXT
DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda( true ) );
#else
DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda() );
#endif
xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
}
if( bestCostBegin != bestCS->cost )
{
m_sbtCostSave[0] = sbtOffCost;
m_sbtCostSave[1] = currBestCost;
}
} //end emt loop
...
}
函数首先判断残差是否需要传输,如果不需要,直接计算失真与模式信息占用的比特,完成率失真计算。如果需要传输残差,则需要调用InterSearch::xEstimateInterResidualQT()完成量化反量化后计算重建失真,统计编码残差和模式信息所需的比特,完成率失真计算。
void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &partitioner, const bool &skipResidual
, const bool luma, const bool chroma
)
{
...
xEstimateInterResidualQT(cs, partitioner, &zeroDistortion, luma, chroma);
...
uint64_t finalFracBits = xGetSymbolFracBitsInter( cs, partitioner );
...
// update with clipped distortion and cost (previously unclipped reconstruction values were used)
Distortion finalDistortion = 0;
for (int comp = 0; comp < numValidComponents; comp++)
{
const ComponentID compID = ComponentID(comp);
if (compID == COMPONENT_Y && !luma)
continue;
if (compID != COMPONENT_Y && !chroma)
continue;
CPelBuf reco = cs.getRecoBuf (compID);
CPelBuf org = cs.getOrgBuf (compID);
#if WCG_EXT
if (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || (
m_pcEncCfg->getLmcs() && (cs.picHeader->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())))
{
const CPelBuf orgLuma = cs.getOrgBuf( cs.area.blocks[COMPONENT_Y] );
if (compID == COMPONENT_Y && !(m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled()) )
{
const CompArea &areaY = cu.Y();
CompArea tmpArea1(COMPONENT_Y, areaY.chromaFormat, Position(0, 0), areaY.size());
PelBuf tmpRecLuma = m_tmpStorageLCU.getBuf(tmpArea1);
tmpRecLuma.copyFrom(reco);
tmpRecLuma.rspSignal(m_pcReshape->getInvLUT());
finalDistortion += m_pcRdCost->getDistPart(org, tmpRecLuma, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
}
else
finalDistortion += m_pcRdCost->getDistPart(org, reco, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
}
else
#endif
{
finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE );
}
}
cs.dist = finalDistortion;
cs.fracBits = finalFracBits;
cs.cost = m_pcRdCost->calcRdCost(cs.fracBits, cs.dist);
...
}
函数首先不对此块进行进一步划分,尝试各种变换模式,调用transformNxN()和invTransformNxN()进行变换和反变换,得到重建值,并m_CABACEstimator->residual_coding()等函数计算占用比特,从而计算出不划分的rd cost。然后尝试不同的划分方式,递归调用此函数计算出各划分方式的rd cost并保留最佳模式。如果需要进一步划分,需要调用xEncodeInterResidualQT()完成cbf和残差编码。
void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &partitioner, Distortion *puiZeroDist /*= NULL*/
, const bool luma, const bool chroma
, PelUnitBuf* orgResi
)
{
...
if (bCheckFull)
{
TransformUnit &tu = csFull->addTU(CS::getArea(cs, currArea, partitioner.chType), partitioner.chType);
...
for( uint32_t c = 0; c < numTBlocks; c++ )
{
...
for( int transformMode = 0; transformMode < numTransformCandidates; transformMode++ )
{
for( int crossCPredictionModeId = 0; crossCPredictionModeId < crossCPredictionModesToTest; crossCPredictionModeId++ )
{
...
if( nNumTransformCands > 1 )
{
if( transformMode == 0 )
{
m_pcTrQuant->transformNxN( tu, compID, cQP, &trModes, m_pcEncCfg->getMTSInterMaxCand() );
tu.mtsIdx[compID] = trModes[0].first;
}
#if JVET_AHG14_LOSSLESS
if( !( m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && tu.mtsIdx[compID] == 0 ) )
{
m_pcTrQuant->transformNxN( tu, compID, cQP, currAbsSum, m_CABACEstimator->getCtx(), true );
}
#else
m_pcTrQuant->transformNxN( tu, compID, cQP, currAbsSum, m_CABACEstimator->getCtx(), true );
#endif
}
else
{
m_pcTrQuant->transformNxN( tu, compID, cQP, currAbsSum, m_CABACEstimator->getCtx() );
}
...
if (currAbsSum > 0) //if non-zero coefficients are present, a residual needs to be derived for further prediction
{
...
currCompFracBits = m_CABACEstimator->getEstFracBits();
PelBuf resiBuf = csFull->getResiBuf(compArea);
CPelBuf orgResiBuf = csFull->getOrgResiBuf(compArea);
m_pcTrQuant->invTransformNxN(tu, compID, resiBuf, cQP);
if (slice.getPicHeader()->getLmcsEnabledFlag() && isChroma(compID) && slice.getPicHeader()->getLmcsChromaResidualScaleFlag() && tu.blocks[compID].width*tu.blocks[compID].height > 4)
{
resiBuf.scaleSignal(tu.getChromaAdj(), 0, tu.cu->cs->slice->clpRng(compID));
}
if (bUseCrossCPrediction)
{
crossComponentPrediction(tu, compID, lumaResi, resiBuf, resiBuf, true);
}
...
}
...
} // component loop
...
csFull->fracBits += m_CABACEstimator->getEstFracBits();
csFull->dist += uiSingleDist;
#if WCG_EXT
if( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() )
{
csFull->cost = m_pcRdCost->calcRdCost(csFull->fracBits, csFull->dist, false);
}
else
#endif
csFull->cost = m_pcRdCost->calcRdCost(csFull->fracBits, csFull->dist);
} // check full
// code sub-blocks
if( bCheckSplit )
{
...
do
{
xEstimateInterResidualQT(*csSplit, partitioner, bCheckFull ? nullptr : puiZeroDist
, luma, chroma
, orgResi
);
csSplit->cost = m_pcRdCost->calcRdCost( csSplit->fracBits, csSplit->dist );
} while( partitioner.nextPart( *csSplit ) );
partitioner.exitCurrSplit();
...
...
}
if( csSplit && csFull )
{
csSplit->releaseIntermediateData();
csFull ->releaseIntermediateData();
}
}
}