目前实验室的研究方向全面转向了H.266,以后我也将由HM转向JEM,不再更新HEVC代码学习了。JEM相对于HM看起来繁杂很多,毕竟加了很多内容,但框架是一样的,建议看的时候和HM比对着看,我也会在这里记录JEM相对于HM的对比学习。这里使用的是JEM7.0。
如果有从看过我之前博客的同学们,会发现我近期更加注意版面了有木有?嘿嘿=.=,舒服了很多有没有。好吧,我承认,是我有强迫症。
今天就来看JEM中的xRecurIntraCodingLumaQT
函数。在之前的 HEVC代码学习37:帧内预测代码整体学习 中已经提到estIntraPredLumaQT
进行亮度帧内预测时,会调用xRecurIntraCodingLumaQT
,是正式预测的入口函数,其中会完成变换、量化、率失真计算的工作。整体流程和HM中的xRecurIntraCodingQT
相同,推荐阅读NB_vol_1大神的博客很详细,不过他所学习的HM版本较早,和现在的有区别。
JEM中与HM主要区别在于
1.在JEM中,统一了CU、PU和TU的概念,也就是说在完成CU划分之后不再进行PU和TU的划分。在HM中,xRecurIntraCodingQT
会进行自调用来完成TU的划分,而在JEM中是没有进一步的TU划分的。
2.增加了EMT多核变换和KLT的模板匹配部分的代码。
流程如下:
一、初始化参数,其中JEM中增加了checkTM
,表示是否使用模板匹配。
二、检测bCheckFull
是否为true
,如果是true
,执行1,否则执行2。
1.检测checkTransformSkip
是否为true
,如果是true
,将使用TS模式,跳过变换步骤。
(1)进行两次遍历,第一次将遍历numTrIdxCands
个模式,第二次只遍历一次(这里遍历两次的意义我还不清楚,之后学习清楚之后会补充,如果有大神知道请留言给讲一下,在这先谢过了)。其中每一次遍历都会设置子块的EMT和TS的Flag,然后调用xIntraCodingTUBlock
来进行亮度分量的预测、计算残差(没有变换步骤)、量化生成重构信号。
(2)选取两次遍历中最优模式。
2.检测checkTransformSkip
是否为false
,则会进行变换。
(1)遍历numTrIdxCands
个模式调用xIntraCodingTUBlock
来进行亮度分量的预测、计算残差并进行变换、量化生成重构信号。
(2)选取最优模式。
三、使用TM,则调用xIntraCodingTUBlockTM
进行模板匹配预测、计算残差进行变换(KLT可用)、量化。
代码太长进行了一些删减,分析如下:
Void
TEncSearch::xRecurIntraCodingLumaQT(TComYuv* pcOrgYuv,
TComYuv* pcPredYuv,
TComYuv* pcResiYuv,
#if COM16_C806_LARGE_CTU
Pel* resiLuma[NUMBER_OF_STORED_RESIDUAL_TYPES],
#else
Pel resiLuma[NUMBER_OF_STORED_RESIDUAL_TYPES][MAX_CU_SIZE * MAX_CU_SIZE],
#endif
Distortion& ruiDistY,
#if HHI_RQT_INTRA_SPEEDUP
Bool bCheckFirst,
#endif
Double& dRDCost,
TComTU& rTu
DEBUG_STRING_FN_DECLARE(sDebug))
{
TComDataCU *pcCU = rTu.getCU(); //当前CU
const UInt uiAbsPartIdx = rTu.GetAbsPartIdxTU(); //分块索引
const UInt uiFullDepth = rTu.GetTransformDepthTotal();
const UInt uiTrDepth = rTu.GetTransformDepthRel();
#if JVET_C0024_QTBT
Bool bCheckFull = true;
Bool bCheckSplit = false;
assert(uiAbsPartIdx ==0 && uiTrDepth==0); //uiAbsPartIdx =0,uiTrDepth=0
UInt uiWidth = pcCU->getWidth(uiAbsPartIdx); //当前块宽度
UInt uiHeight = pcCU->getHeight(uiAbsPartIdx); //当前块高度
UInt uiWIdx = g_aucConvertToBit[ uiWidth ] ; //bit表示的宽度
UInt uiHIdx = g_aucConvertToBit[ uiHeight ] ; //bit表示的高度
#else
// 删除默认不启用的代码
#endif
#if !COM16_C806_LARGE_CTU
// 删除默认不启用的代码
#endif
Bool bMaintainResidual[NUMBER_OF_STORED_RESIDUAL_TYPES];
for (UInt residualTypeIndex = 0; residualTypeIndex < NUMBER_OF_STORED_RESIDUAL_TYPES; residualTypeIndex++)
{
bMaintainResidual[residualTypeIndex] = true; //assume true unless specified otherwise
}
bMaintainResidual[RESIDUAL_ENCODER_SIDE] = !(m_pcEncCfg->getUseReconBasedCrossCPredictionEstimate());
#if COM16_C806_EMT
UInt uiSigNum;
#endif
#if HHI_RQT_INTRA_SPEEDUP
#if JVET_C0024_QTBT
Int isIntraSlice = (pcCU->getSlice()->getSliceType() == I_SLICE); //I Slice
#else
// 删除默认不启用的代码
#endif
#else
// 删除默认不启用的代码
#endif
#if COM16_C806_EMT && HHI_RQT_INTRA_SPEEDUP
// Re-use the selected transform indexes in the previous call of xRecurIntraCodingQT
#if JVET_C0024_QTBT
UInt uiInitTrDepth = 0; //初始变换深度0
#else
UInt uiInitTrDepth = pcCU->getPartitionSize(0) == SIZE_2Nx2N ? 0 : 1;
#endif
UChar ucSavedEmtTrIdx = 0;
Bool bCheckInitTrDepth = false;
static UInt uiInitAbsPartIdx;
if ( uiTrDepth==uiInitTrDepth ) //uiTrDepth一定等于uiInitTrDepth啊?
{
uiInitAbsPartIdx = uiAbsPartIdx;
}
if ( !bCheckFirst && uiTrDepth==uiInitTrDepth )
{
ucSavedEmtTrIdx = m_puhQTTempEmtTuIdx[uiAbsPartIdx-uiInitAbsPartIdx];
bCheckInitTrDepth = true;
}
#endif
#if JVET_C0024_QTBT
assert(bCheckFull && !bCheckSplit);
#endif
Double dSingleCost = MAX_DOUBLE; //初始代价取最大
Distortion uiSingleDistLuma = 0; //初始亮度失真为0
UInt uiSingleCbfLuma = 0;
Bool checkTransformSkip = pcCU->getSlice()->getPPS()->getUseTransformSkip(); //检测是否为TS模式
Int bestModeId[MAX_NUM_COMPONENT] = { 0, 0, 0}; //最优模式列表
#if COM16_C806_EMT
UChar bestTrIdx = 0; //最优变换索引
UChar nNumTrCands = pcCU->getEmtCuFlag(uiAbsPartIdx) ? 4 : 1; //EMT可用时,变换列表候选数为4,否则为1
Bool bAllIntra = (m_pcEncCfg->getIntraPeriod()==1);
#endif
#if JVET_C0024_QTBT
//检测是否使用TS模式
checkTransformSkip &= TUCompRectHasAssociatedTransformSkipFlag(pcCU->getSlice()->isIntra(), rTu.getRect(COMPONENT_Y), pcCU->getSlice()->getPPS()->getPpsRangeExtension().getLog2MaxTransformSkipBlockSize());
#else
checkTransformSkip &= TUCompRectHasAssociatedTransformSkipFlag(rTu.getRect(COMPONENT_Y), pcCU->getSlice()->getPPS()->getPpsRangeExtension().getLog2MaxTransformSkipBlock