之前在 H.266代码学习:transformNxN函数 中提到,帧内KLT的入口函数是xIntraCodingTUBlockTM
,今天就来对他进行详细学习。
这里本人存在一点疑问,先写出来:
从代码中看,帧内预测中使用KLT变换,必须使用TM匹配进行预测,为什么KLT只能针对TM匹配预测进行变换?TM匹配替代了原帧内预测,原HEVC中应该是没有的,为什么JEM中没有提到新增使用TM匹配的帧内预测?
希望大家看的时候能注意一下,如果可以,麻烦给解答一下,谢谢。我也将在后续对这两个问题进行学习,得出答案时会进行补充说明。
xIntraCodingTUBlockTM
xIntraCodingTUBlockTM
的功能是使用TM匹配的帧内预测,和xIntraCodingTUBlock
是同一级的函数,两者是竞争关系。xRecurIntraCodingLumaQT
会调用xIntraCodingTUBlock
进行使用AMT多核变换和原HEVC变换的帧内预测,如果开启usbKLT,则在完成xIntraCodingTUBlock
之后,会进行xIntraCodingTUBlockTM
进行使用SDT(详见 H.266变换编码:信号决定变换SDT)的帧内预测,两者选最优者。
JEM与HM主要区别有:
JEM新增的信号决定变换SDT,HM中是没有的。
流程如下:
xIntraCodingTUBlockTM
流程和xIntraCodingTUBlock
比较相似,只是将帧内预测换成了TM匹配预测。在之前的 H.266代码学习:transformNxN函数 中已经简单总结了流程,这里来详细分析一下:
一、初始化参数。
二、训练KLT:
1.使用getTargetTemplate
获取模板。
2.使用candidateSearchIntra
进行帧内搜索找最优匹配块。
3.使用generateTMPrediction
生成TM预测块。
4.使用calcKLTIntra
来训练KLT,并返回KLT是否可用。
三、计算残差。
四、变换量化。
1.设置lambda。
2.调用smoothResidual进行残差平滑滤波。
3.调用transformNxN进行KLT变换量化。
五、调用invTransformNxN进行KLT反变换量化。
六、用预测信号+残差生成重构信号,然后进行双边滤波。
七、更新失真信息。
代码分析:
#if VCEG_AZ08_INTRA_KLT
//帧内KLT
Bool TEncSearch::xIntraCodingTUBlockTM(TComYuv* pcOrgYuv,
TComYuv* pcPredYuv,
TComYuv* pcResiYuv,
Distortion& ruiDist,
const ComponentID compID,
TComTU& rTu
DEBUG_STRING_FN_DECLARE(sDebug)
#if COM16_C806_EMT
, UInt* puiSigNum
#endif
, Int tmpred0_tmpredklt1_ori2
)
{
/******************* 一、初始化,删除了此部分代码,不详细说了*******************/
//===== init availability pattern =====
DEBUG_STRING_NEW(sTemp)
/******************* 二、训练KLT*******************/
#if VCEG_AZ08_INTRA_KLT
Bool useKLT = false; //KLT初始为false
if (tmpred0_tmpredklt1_ori2 != 2 && bIsLuma) //默认TMPRED0_TMPREDKLT1_ORI2=1; 0: Template matching prediction; 1: TM prediction + KLT; 2: Original method
{
UInt uiBlkSize = uiWidth; //宽度
UInt uiTarDepth = g_aucConvertToBit[uiBlkSize];
UInt uiTempSize = g_uiDepth2IntraTempSize[uiTarDepth];
m_pcTrQuant->getTargetTemplate(pcCU, uiAbsPartIdx, uiBlkSize, uiTempSize); //获取模板
m_pcTrQuant->candidateSearchIntra(pcCU, uiAbsPartIdx, uiBlkSize, uiTempSize); //进行帧内搜索找最优匹配块
Int foundCandiNum;
Bool bSuccessful = m_pcTrQuant->generateTMPrediction(piPred, uiStride, uiBlkSize, uiTempSize, foundCandiNum); //生成TM预测块
if (bSuccessful == false || foundCandiNum < 1)
{
return false;
}
if (1 == tmpred0_tmpredklt1_ori2 && bSuccessful)
{
useKLT = m_pcTrQuant->calcKLTIntra(piPred, uiStride, uiBlkSize); //训练KLT,并返回KLT是否可用
}
}
#endif
/******************* 三、计算残差 *******************/
//===== get residual signal =====
{
// get residual
Pel* pOrg = piOrg;
Pel* pPred = piPred;
Pel* pResi = piResi;
for (UInt uiY = 0; uiY < uiHeight; uiY++)
{
for (UInt uiX = 0; uiX < uiWidth; uiX++)
{
pResi[uiX] = pOrg[uiX] - pPred[uiX];
}
pOrg += uiStride;
pResi += uiStride;
pPred += uiStride;
}
}
//===== transform and quantization =====
//--- init rate estim