代码所在网址:
https://jvet.hhi.fraunhofer.de/svn/svn_VVCSoftware_BMS/
这些只是自己初步的学习,若有问题烦请更正,共同进步~
帧内预测的相关内容在函数xCheckRDCostIntra()中
该函数下调用了亮度预测函数及色度预测函数:estIntraPredLumaQT()及estIntraPredLumaQT()
两个函数及其调用函数分析见:
https://blog.csdn.net/yolo_life/article/details/81673741
void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
{
#if JEM_TOOLS
double bestInterCost = m_modeCtrl->getBestInterCost();
#endif
#if JEM_TOOLS
#if HEVC_USE_PART_SIZE
bool isAllIntra = m_pcEncCfg->getIntraPeriod() == 1;
#endif
#endif
#if JEM_TOOLS
//AMT第一个通道
double costSize2Nx2NemtFirstPass = m_modeCtrl->getEmtSize2Nx2NFirstPassCost();
double costSizeNxNemtFirstPass = MAX_DOUBLE;
bool skipSecondEmtPass = m_modeCtrl->getSkipSecondEMTPass();
#endif
#if JEM_TOOLS
auto slsCtrl = dynamic_cast<SaveLoadEncInfoCtrl*>( m_modeCtrl );
const SPS &sps = *tempCS->sps;
#endif
const PPS &pps = *tempCS->pps;
const CodingUnit *bestCU = bestCS->getCU( partitioner.chType );
#if JEM_TOOLS
//NSST索引
const int nsstIdx = ( encTestMode.opts & ETO_NSST ) >> ETO_NSST_SHIFT;
#endif
#if JEM_TOOLS
const bool usePDPC = ( encTestMode.opts & ETO_PDPC ) != 0;
#endif
#if JEM_TOOLS
const int maxSizeEMT = pps.pcv->noRQT ? EMT_INTRA_MAX_CU_WITH_QTBT : EMT_INTRA_MAX_CU;
#endif
#if JEM_TOOLS
#if HM_EMT_NSST_AS_IN_JEM
//判断是帧内编码的宽高均<=maxSizeEMT的亮度块,是则使用AMT的第二通道,值为1,表示使用AMT;不是则为0,不使用AMT
UChar considerEmtSecondPass = ( sps.getSpsNext().getUseIntraEMT() && isLuma( partitioner.chType ) && partitioner.currArea().lwidth() <= maxSizeEMT && partitioner.currArea().lheight() <= maxSizeEMT ) ? 1 : 0;
#else
UChar considerEmtSecondPass = ( sps.getSpsNext().getUseIntraEMT() && isLuma( partitioner.chType ) && partitioner.currArea().lwidth() <= maxSizeEMT && partitioner.currArea().lheight() <= maxSizeEMT && nsstIdx == 0 ) ? 1 : 0;
#endif
CHECK( usePDPC && sps.getSpsNext().isPlanarPDPC(), "PDPC cannot be on with Planar-PDPC" );
#endif
Distortion interHad = m_modeCtrl->getInterHad();
#if JEM_TOOLS//AMT相关循环
for( UChar emtCuFlag = 0; emtCuFlag <= considerEmtSecondPass; emtCuFlag++ )
#else
for( UChar numPasses = 0; numPasses < 1; numPasses++ )
#endif
{
#if JEM_TOOLS
//Possible early EMT tests interruptions //AMT早期中断判断
//1) saveLoadTag code for EMT
if( sps.getSpsNext().getUseQTBT() && slsCtrl && m_pcEncCfg->getUseSaveLoadEncInfo() )
{
if( m_pcEncCfg->getIntraEMT() && LOAD_ENC_INFO == slsCtrl->getSaveLoadTag( tempCS->area ) && ( emtCuFlag > 0 ) != slsCtrl->getSaveLoadEmtCuFlag( tempCS->area ) )
{
continue;
}
}
//2) Second EMT pass. This "if clause" is necessary because of the NSST and PDPC "for loops".
if( emtCuFlag && skipSecondEmtPass )
{
continue;
}
#endif
//3) if interHad is 0, only try further modes if some intra mode was already better than inter
if( m_pcEncCfg->getUsePbIntraFast() && !tempCS->slice->isIntra() && bestCU && CU::isInter( *bestCS->getCU( partitioner.chType ) ) && interHad == 0 )
{
continue;
}
//************************************************ 初始化
tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
CodingUnit &cu = tempCS->addCU( CS::getArea( *tempCS, tempCS->area, partitioner.chType ), partitioner.chType );
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 = encTestMode.partSize;
cu.predMode = MODE_INTRA;
cu.transQuantBypass = encTestMode.lossless;
#if JEM_TOOLS
cu.pdpc = usePDPC;
#endif
cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
cu.qp = encTestMode.qp;
//cu.ipcm = false;
#if JEM_TOOLS
cu.nsstIdx = nsstIdx;
cu.emtFlag = emtCuFlag;
#endif
CU::addPUs( cu );
tempCS->interHad = interHad;
//************************************************ 亮度帧内预测
if( isLuma( partitioner.chType ) )
{
m_pcIntraSearch->estIntraPredLumaQT( cu, partitioner );//入口函数
if( m_pcEncCfg->getUsePbIntraFast() && tempCS->dist == MAX_UINT && tempCS->interHad == 0 )
{
interHad = 0;
// JEM assumes only perfect reconstructions can from now on beat the inter mode
m_modeCtrl->enforceInterHad( 0 );
continue;
}
if( !CS::isDualITree( *tempCS ) )//isDualITree:I帧下的亮度色度分离树判断,为真表示分离树
{
cu.cs->picture->getRecoBuf( cu.Y() ).copyFrom( cu.cs->getRecoBuf( COMPONENT_Y ) );
}
}
//************************************************ 色度帧内预测
if( tempCS->area.chromaFormat != CHROMA_400 && ( partitioner.chType == CHANNEL_TYPE_CHROMA || !CS::isDualITree( *tempCS ) ) )
{
m_pcIntraSearch->estIntraPredChromaQT( cu, partitioner );
}
cu.rootCbf = false;
for( UInt t = 0; t < getNumberValidTBlocks( *cu.cs->pcv ); t++ )
{
cu.rootCbf |= cu.firstTU->cbf[t] != 0;
}
//************************************************ Get total bits for current mode: encode CU
m_CABACEstimator->resetBits();
if( pps.getTransquantBypassEnabledFlag() )
{ //编码变换量化旁路情况下的标志(旁路意味着残差直接赋值给变换系数)
m_CABACEstimator->cu_transquant_bypass_flag( cu );
}
if( !cu.cs->slice->isIntra() )
{ //编码非帧内下的skip标志
m_CABACEstimator->cu_skip_flag ( cu );
}
//编码预测模式
m_CABACEstimator->pred_mode ( cu );
#if JEM_TOOLS
//编码PDPC标志
m_CABACEstimator->pdpc_flag ( cu );
#endif
#if HEVC_USE_PART_SIZE //默认flase
m_CABACEstimator->part_mode ( cu );
#endif
//编码CU预测数据(包括:帧内:亮度预测模式+色度预测模式;PU;IMV模式;OBMC标志;LIC标志)
m_CABACEstimator->cu_pred_data ( cu );
//编码PCM数据
m_CABACEstimator->pcm_data ( cu );
//************************************************ Encode Coefficients
CUCtx cuCtx;
cuCtx.isDQPCoded = true;
cuCtx.isChromaQpAdjCoded = true;
m_CABACEstimator->cu_residual( cu, partitioner, cuCtx );//变换系数编码
tempCS->fracBits = m_CABACEstimator->getEstFracBits();
tempCS->cost = m_pcRdCost->calcRdCost(tempCS->fracBits, tempCS->dist);
#if !HM_POSTPONE_SPLIT_BITS //宏为真,默认不进入下一个函数
xEncodeDontSplit( *tempCS, partitioner );
#endif
//************************************************ deltaQP检测
xCheckDQP( *tempCS, partitioner );
#if JEM_TOOLS
//************************************************ Check if secondary transform (NSST) is too expensive
const int nonZeroCoeffThr = CS::isDualITree( *tempCS ) ? ( isLuma( partitioner.chType ) ? NSST_SIG_NZ_LUMA : NSST_SIG_NZ_CHROMA ) : NSST_SIG_NZ_LUMA + NSST_SIG_NZ_CHROMA;
if( nsstIdx && tempCS->pcv->noRQT && cuCtx.numNonZeroCoeffNonTs <= nonZeroCoeffThr )
{
Bool isMDIS = false;
if( sps.getSpsNext().isPlanarPDPC() )
{
CHECK( CU::getNumPUs( cu ) > 1, "PLanarPDPC: encoder MDIS condition not defined for multi PU" );
const PredictionUnit* pu = cu.firstPU;
isMDIS = IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, *pu, true, *pu );
#if HM_MDIS_AS_IN_JEM
if( pu->intraDir[0] == PLANAR_IDX ) { isMDIS |= IntraPrediction::getPlanarMDISCondition( *pu ); }
#endif
}
if( cuCtx.numNonZeroCoeffNonTs > 0 || isMDIS )
{
tempCS->cost = MAX_DOUBLE;
}
}
if( nsstIdx && !tempCS->pcv->noRQT && cu.rootCbf == 0 )
{
tempCS->cost = MAX_DOUBLE;
}
#endif
#if JEM_TOOLS
//************************************************保存了第一个AMT通道模式的代价
if( !emtCuFlag ) static_cast< double& >( cu.partSize == SIZE_2Nx2N ? costSize2Nx2NemtFirstPass : costSizeNxNemtFirstPass ) = tempCS->cost;
#endif
#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 JEM_TOOLS
//************************************************ now we check whether the second pass of SIZE_2Nx2N and the whole Intra SIZE_NxN should be skipped or not
if( !emtCuFlag && !tempCS->slice->isIntra() && bestCU && bestCU->predMode != MODE_INTRA && cu.partSize == SIZE_2Nx2N && m_pcEncCfg->getFastInterEMT() && ( m_pcEncCfg->getUseSaveLoadEncInfo() ? ( bestInterCost < MAX_DOUBLE ) : true ) )
{
const double thEmtInterFastSkipIntra = 1.4; // Skip checking Intra if "2Nx2N using DCT2" is worse than best Inter mode
if( costSize2Nx2NemtFirstPass > thEmtInterFastSkipIntra * bestInterCost )
//对应AMT中跳过第二个通道的相关判断操作
{
skipSecondEmtPass = true;
m_modeCtrl->setSkipSecondEMTPass( true );
break;
}
}
#if HEVC_USE_PART_SIZE //默认flase
//now we check whether the second pass of EMT with SIZE_NxN should be skipped or not
if( !emtCuFlag && isAllIntra && cu.partSize == SIZE_NxN && m_pcEncCfg->getFastIntraEMT() )
{
costSize2Nx2NemtFirstPass = m_modeCtrl->getEmtSize2Nx2NFirstPassCost();
const double thEmtIntraFastSkipNxN = 1.2; // Skip checking "NxN using EMT" if "NxN using DCT2" is worse than "2Nx2N using DCT2"
if( costSizeNxNemtFirstPass > thEmtIntraFastSkipNxN * costSize2Nx2NemtFirstPass )
{
break;
}
}
#endif
#endif
} //for emtCuFlag
}