在之前的 HEVC代码学习37:帧内预测代码整体学习 中已经提到,estIntraPredLumaQT
是亮度帧内预测的入口函数,下面将对该函数进行详细学习。
estIntraPredLumaQT
中完成了亮度分量的帧内预测,其主要流程如下:
一、初始化各种参数。
二、为了减少率失真优化次数,HEVC中默认使用帧内快速搜索算法,将分粗选和细选两个阶段进行。如果不使用快速搜索,将对所有帧内预测模式进行率失真优化。
1.粗选阶段:遍历35种帧内预测模式使用predIntraAng
计算预测值,计算比较哈达玛失真,调用xUpdateCandList
构建全率失真优化候选列表。全率失真优化候选列表长度numModesForFullRD
由块宽度决定。注意在粗选阶段全部使用哈达玛变换计算失真,提高速度。
2.细选阶段:调用getIntraDirPredictor
构建MPM列表,加入全率失真优化候选列表中。遍历全率失真优化候选列表,调用xRecurIntraCodingLumaQT
进行变换量化重构,计算率失真代价,注意在这里该函数倒数第二个参数是bCheckFirst=true
,表示会按照四叉树的方式继续向下划分。比较率失真代价,找到最优模式。
三、对获得的最优模式再次调用xRecurIntraCodingLumaQT
,此时倒数第二个参数设置为false,检测同一模式下,bCheckFirst
为true和false的情况下,选出最优模式为最终的亮度帧内预测模式。
四、收尾工作,记录信息:设置重建块、Cbf、上下文模型、总失真等。
其中调用了重要函数:predIntraAng
(计算帧内预测模式的预测值)、getIntraDirPredictor
(利用临近PU构建MPM列表)、xRecurIntraCodingLumaQT
(进行帧内亮度分量的变换量化重构,计算率失真代价)。
代码分析:
//亮度分量预测
Void
TEncSearch::estIntraPredLumaQT(TComDataCU* pcCU,
TComYuv* pcOrgYuv,
TComYuv* pcPredYuv,
TComYuv* pcResiYuv,
TComYuv* pcRecoYuv,
Pel resiLuma[NUMBER_OF_STORED_RESIDUAL_TYPES][MAX_CU_SIZE * MAX_CU_SIZE]
DEBUG_STRING_FN_DECLARE(sDebug))
{
const UInt uiDepth = pcCU->getDepth(0); //划分深度
const UInt uiInitTrDepth = pcCU->getPartitionSize(0) == SIZE_2Nx2N ? 0 : 1; //当CU为2Nx2N时,初始变换深度为0;否则为1。
const UInt uiNumPU = 1<<(2*uiInitTrDepth); //PU分块数
const UInt uiQNumParts = pcCU->getTotalNumPart() >> 2;
const UInt uiWidthBit = pcCU->getIntraSizeIdx(0);
const ChromaFormat chFmt = pcCU->getPic()->getChromaFormat(); //颜色格式
const UInt numberValidComponents = getNumberValidComponents(chFmt);
const TComSPS &sps = *(pcCU->getSlice()->getSPS()); //SPS
const TComPPS &pps = *(pcCU->getSlice()->getPPS()); //PPS
Distortion uiOverallDistY = 0;
UInt CandNum; //候选数
Double CandCostList[ FAST_UDI_MAX_RDMODE_NUM ]; //候选代价列表
Pel resiLumaPU[NUMBER_OF_STORED_RESIDUAL_TYPES][MAX_CU_SIZE * MAX_CU_SIZE]; //亮度残差
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());
// Lambda calculation at equivalent Qp of 4 is recommended because at that Qp, the quantisation divisor is 1.
#if FULL_NBIT
const Double sqrtLambdaForFirstPass= (m_pcEncCfg->getCostMode()==COST_MIXED_LOSSLESS_LOSSY_CODING && pcCU->getCUTransquantBypass(0)) ?
sqrt(0.57 * pow(2.0, ((LOSSLESS_AND_MIXED_LOSSLESS_RD_COST_TEST_QP_PRIME - 12) / 3.0)))
: m_pcRdCost->getSqrtLambda();
#else
//计算Lambda
const Double sqrtLambdaForFirstPass= (m_pcEncCfg->getCostMode()==COST_MIXED_LOSSLESS_LOSSY_CODING && pcCU->getCUTransquantBypass(0)) ?
sqrt(0.57 * pow(2.0, ((LOSSLESS_AND_MIXED_LOSSLESS_RD_COST_TEST_QP_PRIME - 12 - 6 * (sps.getBitDepth(CHANNEL_TYPE_LUMA) - 8)) / 3.0)))
: m_pcRdCost->getSqrtLambda();
#endif
//===== set QP and clear Cbf =====
//设置QP,清除Cbf
if ( pps.getUseDQP() == true)
{
pcCU->setQPSubParts( pcCU->getQP(0), 0, uiDepth );
}
else
{
pcCU->setQPSubParts( pcCU->getSlice()->getSliceQp(), 0, uiDepth );
}
//===== loop over partitions =====