对于帧内预测来说,主要包括角度预测和划分。
在xCompressCU函数中体现为
xCheckRDCostIntra( tempCS, bestCS, partitioner, currTestMode );
以及
xCheckModeSplit( tempCS, bestCS, partitioner, currTestMode
, tempMotCandLUTs
, bestMotCandLUTs
, partitioner.currArea()
);
。
xCheckModeSplit函数中主要是根据划分模式进行划分,再递归回xCompressCU,与HEVC中递归差不多。
这里主要讲帧内亮度角度预测过程,VTM版本为4.0.1。
xCheckRDCostIntra函数进来之后,会进行相应参数的初始化,之后调用亮度角度预测
m_pcIntraSearch->estIntraPredLumaQT( cu, partitioner, bestCostSoFar );
和色度角度预测
m_pcIntraSearch->estIntraPredChromaQT( cu, ( !useIntraSubPartitions || ( CS::isDualITree( *cu.cs ) && !isLuma( CHANNEL_TYPE_CHROMA ) ) ) ? partitioner : subTuPartitioner, maxCostAllowedForChroma );
。
帧内亮度预测主要过程为:
1、参考像素的获取与滤波;具体可见---https://blog.youkuaiyun.com/pengyouyou/article/details/89027301
2、在原HEVC的35种模式中,挑选出numModesForFullRD种哈达玛代价最小的模式-------RMD第一步;
其中planner模式讲解见---https://blog.youkuaiyun.com/pengyouyou/article/details/89041932
3、在RMD第一步得到的角度模式中,对其相邻的新增角度模式计算哈达玛代价值,同样保留numModesForFullRD种代价最小的模式-------RMD第二步;
4、添加MPM模式;
5、对候选模式进行RDO过程,选出代价最小的模式。
虽然大致过程为这5个步骤,但是在MRL和ISP等技术的引入使得具体过程更加复杂。
具体程序注释如下:
#if JVET_M0102_INTRA_SUBPARTITIONS
void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, const double bestCostSoFar )//亮度角度预测
#else
void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner )
#endif
{
CodingStructure &cs = *cu.cs;
const SPS &sps = *cs.sps;
const uint32_t uiWidthBit = g_aucLog2[partitioner.currArea().lwidth() ];//
const uint32_t uiHeightBit = g_aucLog2[partitioner.currArea().lheight()];
// Lambda calculation at equivalent Qp of 4 is recommended because at that Qp, the quantization divisor is 1.
const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda(cu.transQuantBypass) / double(1 << SCALE_BITS);
//===== loop over partitions =====
const TempCtx ctxStart ( m_CtxCache, m_CABACEstimator->getCtx() );
const TempCtx ctxStartIntraMode(m_CtxCache, SubCtx(Ctx::IntraLumaMpmFlag, m_CABACEstimator->getCtx()));
const TempCtx ctxStartMHIntraMode ( m_CtxCache, SubCtx( Ctx::MHIntraPredMode, m_CABACEstimator->getCtx() ) );
const TempCtx ctxStartMrlIdx ( m_CtxCache, SubCtx( Ctx::MultiRefLineIdx, m_CABACEstimator->getCtx() ) );
CHECK( !cu.firstPU, "CU has no PUs" );
const bool keepResi = cs.pps->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() || KEEP_PRED_AND_RESI_SIGNALS;
uint32_t extraModes = 0; // add two extra modes, which would be used after uiMode <= DC_IDX is removed for cu.nsstIdx == 3
#if !JVET_M0464_UNI_MTS
const int width = partitioner.currArea().lwidth();
const int height = partitioner.currArea().lheight();
// Marking EMT usage for faster EMT
// 0: EMT is either not applicable for current CU (cuWidth > EMT_INTRA_MAX_CU or cuHeight > EMT_INTRA_MAX_CU), not active in the config file or the fast decision algorithm is not used in this case
// 1: EMT fast algorithm can be applied for the current CU, and the DCT2 is being checked
// 2: EMT is being checked for current CU. Stored results of DCT2 can be utilized for speedup
uint8_t emtUsageFlag = 0;
const int maxSizeEMT = EMT_INTRA_MAX_CU_WITH_QTBT;
if( width <= maxSizeEMT && height <= maxSizeEMT && sps.getUseIntraEMT() )
{
emtUsageFlag = cu.emtFlag == 1 ? 2 : 1;
}
bool isAllIntra = m_pcEncCfg->getIntraPeriod() == 1;
if( width * height < 64 && !isAllIntra )
{
emtUsageFlag = 0; //this forces the recalculation of the candidates list. Why is this necessary? (to be checked)
}
#endif
#if JVET_M0102_INTRA_SUBPARTITIONS
#if JVET_M0464_UNI_MTS
const int width = partitioner.currArea().lwidth();
const int height = partitioner.currArea().lheight();
int nOptionsForISP = NUM_INTRA_SUBPARTITIONS_MODES;//ISP划分模式:HOR_INTRA_SUBPARTITIONS = 1, VER_INTRA_SUBPARTITIONS = 2,
//默认的NUM_INTRA_SUBPARTITIONS_MODES=3
#else
int nOptionsForISP = cu.emtFlag == 0 ? NUM_INTRA_SUBPARTITIONS_MODES : 1;
#endif
double bestCurrentCost = bestCostSoFar;//当前最佳代价
int ispOptions[NUM_INTRA_SUBPARTITIONS_MODES] = { 0 };//ISP模式初始为non-ISP
if( nOptionsForISP > 1 ) {
auto splitsThatCanBeUsedForISP = CU::canUseISPSplit( width, height, cu.cs->sps->getMaxTrSize() );//得到可能进行的ISP模式
if( splitsThatCanBeUsedForISP == CAN_USE_VER_AND_HORL_SPLITS ) {//水平和垂直ISP均可以
const CodingUnit* cuLeft = cu.ispMode != NOT_INTRA_SUBPARTITIONS ? cs.getCU( cs.area.blocks[partitioner.chType].pos().offset( -1, 0 ), partitioner.chType ) : nullptr;
const CodingUnit* cuAbove = cu.ispMode != NOT_INTRA_SUBPARTITIONS ? cs.getCU( cs.area.blocks[partitioner.chType].pos().offset( 0, -1 ), partitioner.chType ) : nullptr;
bool ispHorIsFirstTest = CU::firstTestISPHorSplit( width, height, COMPONENT_Y, cuLeft, cuAbove );
if( ispHorIsFirstTest )//先水平ISP,默认nOptionsForISP=3
{
ispOptions[1] = HOR_INTRA_SUBPARTITIONS;
ispOptions[2] = VER_INTRA_SUBPARTITIONS;
}
else//先垂直ISP,默认nOptionsForISP=3
{
ispOptions[1] = VER_INTRA_SUBPARTITIONS;
ispOptions[2] = HOR_INTRA_SUBPARTITIONS;
}
}
else if( splitsThatCanBeUsedForISP == HOR_INTRA_SUBPARTITIONS )
{
nOptionsForISP = 2;
ispOptions[1] = HOR_INTRA_SUBPARTITIONS;//水平ISP
}
else if( splitsThatCanBeUsedForISP == VER_INTRA_SUBPARTITIONS )
{
nOptionsForISP = 2;
ispOptions[1] = VER_INTRA_SUBPARTITIONS;//垂直ISP
}
else
{
nOptionsForISP = 1;//non-ISP
}
}
if( nOptionsForISP > 1 )//ISP模式清空列表
{
//variables for the full RD list without MRL modes
m_rdModeListWithoutMrl .clear();
m_rdModeListWithoutMrlHor .clear();
m_rdModeListWithoutMrlVer .clear();
//variables with data from regular intra used to skip ISP splits
m_intraModeDiagRatio .clear();
m_intraModeHorVerRatio .clear();
m_intraModeTestedNormalIntra.clear();
}
#endif
static_vector<uint32_t, FAST_UDI_MAX_RDMODE_NUM> uiHadModeList;//哈达玛模式列表
static_vector<double, FAST