xCheckRDCostMerge2Nx2N在xCompressCU函数中调用,用来进行Merge模式的计算。skip模式相较于Merge模式,不向解码端发送残差系数。
首先由getInterMergeCandidates获取merge候选模式信息,存储于mergeCtx;
如果不进行fastMerge,则跳过SATD直接进行RDO选最优;
fastMerge时,对7个merge候选模式进行SATD,分别进行MC获得pred像素,选取cost最小的4个merge候选;
对RdModeList中的merge候选进行RDO选择最优的merge模式。
void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
{
const Slice &slice = *tempCS->slice;
CHECK( slice.getSliceType() == I_SLICE, "Merge modes not available for I-slices" );
tempCS->initStructData( encTestMode.qp, encTestMode.lossless ); //tempCS清空初始化
MergeCtx mergeCtx; //MergeCtx类中存放merge列表的所有信息
const SPS &sps = *tempCS->sps;
{
// first get merge candidates
CodingUnit cu( tempCS->area ); //创建cu、pu用来获得merge列表
cu.cs = tempCS;
cu.partSize = SIZE_2Nx2N;
cu.predMode = MODE_INTER;
cu.slice = tempCS->slice;
#if HEVC_TILES_WPP
cu.tileIdx = tempCS->picture->tileMap->getTileIdxMap(tempCS->area.lumaPos());
#endif
PredictionUnit pu( tempCS->area );
pu.cu = &cu;
pu.cs = tempCS;
PU::getInterMergeCandidates(pu, mergeCtx); //获取merge列表,其中包含7个merge候选信息,存储于mergeCtx
}
bool candHasNoResidual[MRG_MAX_NUM_CANDS]; //若merge模式没有resi,则为skip模式
for (UInt ui = 0; ui < mergeCtx.numValidMergeCand; ui++)
{
candHasNoResidual[ui] = false;
}
bool bestIsSkip = false;
unsigned uiNumMrgSATDCand = mergeCtx.numValidMergeCand; //SATD粗选择后的merge候选数目
PelUnitBuf acMergeBuffer [ MRG_MAX_NUM_CANDS ];
//存储SATD时,7个merge候选经过MC得到的pred像素信息
static_vector<unsigned, MRG_MAX_NUM_CANDS> RdModeList;
bool mrgTempBufSet = false;
for( unsigned i = 0; i < MRG_MAX_NUM_CANDS; i++ )
{
RdModeList.push_back( i ); //如果不进行SATD,那么RdModeList中存放7个merge候选,直接进行最后的RDO
}
if( m_pcEncCfg->getUseFastMerge() )
{
uiNumMrgSATDCand = NUM_MRG_SATD_CAND; //SATD粗选择后的merge候选数目,4
bestIsSkip = false;
if( auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >( m_modeCtrl ) )
{
bestIsSkip = blkCache->isSkip( tempCS->area );
}
static_vector<double, MRG_MAX_NUM_CANDS> candCostList; //存放SATD时得到的7个候选的cost
// 1. Pass: get SATD-cost for selected candidates and reduce their count
if( !bestIsSkip ) //不是skip模式,对7个merge候选模式进行SATD粗选择
{
RdModeList.clear(); //RdModeList前面有压栈操作,这里清空
mrgTempBufSet = true;
const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda( encTestMode.lossless );
CodingUnit &cu = tempCS->addCU( tempCS->area, partitioner.chType ); //tempCS添加cu和pu
partitioner.setCUData( cu );
cu.slice = tempCS->slice;
#if HEVC_TILES_WPP
cu.tileIdx = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
#endif
cu.skip = false;
cu.partSize = SIZE_2Nx2N;
//cu.affine
cu.predMode = MODE_INTER;
//cu.LICFlag
cu.transQuantBypass = encTestMode.lossless;
cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
cu.qp = encTestMode.qp;
//cu.emtFlag is set below
PredictionUnit &pu = tempCS->addPU( cu, partitioner.chType );
DistParam distParam;
const Bool bUseHadamard= !encTestMode.lossless; //设置计算失真的类:distParam 的各项参数
m_pcRdCost->setDistParam (distParam, tempCS->getOrgBuf().Y(), m_acMergeBuffer[0].Y(), sps.getBitDepth (CHANNEL_TYPE_LUMA), COMPONENT_Y, bUseHadamard);
const UnitArea localUnitArea( tempCS->area.chromaFormat, Area( 0, 0, tempCS->area.Y().width, tempCS->area.Y().height) );
for( UInt uiMergeCand = 0; uiMergeCand < mergeCtx.numValidMergeCand; uiMergeCand++ ) //7个merge候选进行SATD
{
acMergeBuffer[uiMergeCand] = m_acMergeBuffer[uiMergeCand].getBuf( localUnitArea );
mergeCtx.setMergeInfo( pu, uiMergeCand );
PU::spanMotionInfo( pu, mergeCtx );
distParam.cur = acMergeBuffer[uiMergeCand].Y(); //计算失真时的pred像素存储于acMergeBuffer[uiMergeCand]
m_pcInterSearch->motionCompensation( pu, acMergeBuffer[uiMergeCand] ); //MC,得到的pred像素存储于acMergeBuffer[uiMergeCand]
if( mergeCtx.interDirNeighbours[uiMergeCand] == 3 && mergeCtx.mrgTypeNeighbours[uiMergeCand] == MRG_TYPE_DEFAULT_N )
{
mergeCtx.mvFieldNeighbours[2*uiMergeCand].mv = pu.mv[0]; //双向预测时
mergeCtx.mvFieldNeighbours[2*uiMergeCand+1].mv = pu.mv[1];
}
UInt uiSad = distParam.distFunc( distParam ); //对比pred和orig计算失真
UInt uiBitsCand = uiMergeCand + 1;
if( uiMergeCand == tempCS->slice->getMaxNumMergeCand() - 1 )
{
uiBitsCand--;
}
Double cost = (Double)uiSad + (Double)uiBitsCand * sqrtLambdaForFirstPass; //cost
updateCandList( uiMergeCand, cost, RdModeList, candCostList, uiNumMrgSATDCand ); //按照cost大小将merge候选模式在RdModeList中排序
CHECK( std::min( uiMergeCand + 1, uiNumMrgSATDCand ) != RdModeList.size(), "" );
}
// Try to limit number of candidates using SATD-costs
for( UInt i = 1; i < uiNumMrgSATDCand; i++ ) //按照1.25*min cost mode,限制SATD后选择候选的个数
{
if( candCostList[i] > MRG_FAST_RATIO * candCostList[0] )
{
uiNumMrgSATDCand = i;
break;
}
}
tempCS->initStructData( encTestMode.qp, encTestMode.lossless ); //tempCS清空
}
}
const UInt iteration = encTestMode.lossless ? 1 : 2;
// 2. Pass: check candidates using full RD test
for( UInt uiNoResidualPass = 0; uiNoResidualPass < iteration; uiNoResidualPass++ )
{
for( UInt uiMrgHADIdx = 0; uiMrgHADIdx < uiNumMrgSATDCand; uiMrgHADIdx++ ) //RDO选择RdModeList中的最优merge模式
{
UInt uiMergeCand = RdModeList[uiMrgHADIdx];
if( ( (uiNoResidualPass != 0) && candHasNoResidual[uiMergeCand] )
|| ( (uiNoResidualPass == 0) && bestIsSkip ) )
{
continue;
}
// first get merge candidates
CodingUnit &cu = tempCS->addCU( tempCS->area, partitioner.chType ); //tempCS添加cu和pu
partitioner.setCUData( cu );
cu.slice = tempCS->slice;
#if HEVC_TILES_WPP
cu.tileIdx = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
#endif
cu.skip = false;
cu.partSize = SIZE_2Nx2N;
//cu.affine
cu.predMode = MODE_INTER;
//cu.LICFlag
cu.transQuantBypass = encTestMode.lossless;
cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
cu.qp = encTestMode.qp;
PredictionUnit &pu = tempCS->addPU( cu, partitioner.chType );
mergeCtx.setMergeInfo( pu, uiMergeCand ); //将merge候选信息赋给pu以及整个tempCS
PU::spanMotionInfo( pu, mergeCtx );
if( mrgTempBufSet ) //如果进行了SATD,mrgTempBufSet为true,直接获取到SATD时的pred像素
{
tempCS->getPredBuf().copyFrom( acMergeBuffer[ uiMergeCand ]);
}
else
{
m_pcInterSearch->motionCompensation( pu ); //之前没有SATD,通过MC获取pred像素信息
}
//由orig和pred像素信息,获得resi,可得到reco像素
//分别处理无残差的skip模式和merge模式,得到cost,最终xCheckBestMode将最优merge信息存储于bestCS
xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, uiNoResidualPass, ( ( uiNoResidualPass == 0 ) ? &candHasNoResidual[uiMergeCand] : NULL ) );
if( m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip )
{
bestIsSkip = bestCS->getCU( partitioner.chType )->rootCbf == 0;
}
tempCS->initStructData( encTestMode.qp, encTestMode.lossless ); //tempCS清空初始化,进行下一个merge候选
}// end loop uiMrgHADIdx
if( uiNoResidualPass == 0 && m_pcEncCfg->getUseEarlySkipDetection() ) //判断skip模式
{
const CodingUnit &bestCU = *bestCS->getCU( partitioner.chType );
const PredictionUnit &bestPU = *bestCS->getPU( partitioner.chType );
if( bestCU.rootCbf == 0 )
{
if( bestPU.mergeFlag )
{
m_modeCtrl->setEarlySkipDetected(); //skip模式
}
else if( m_pcEncCfg->getMotionEstimationSearchMethod() != MESEARCH_SELECTIVE )
{
Int absolute_MV = 0;
for( UInt uiRefListIdx = 0; uiRefListIdx < 2; uiRefListIdx++ )
{
if( slice.getNumRefIdx( RefPicList( uiRefListIdx ) ) > 0 )
{
absolute_MV += bestPU.mvd[uiRefListIdx].getAbsHor() + bestPU.mvd[uiRefListIdx].getAbsVer();
}
}
if( absolute_MV == 0 )
{
m_modeCtrl->setEarlySkipDetected();
}
}
}
}
}
}