最近在做RDOQ算法优化和硬件系统结构设计,建立其时序模型,估算算法的硬件复杂度。在做这部分工作的同时,博主学习了HEVC算法所有的算法原理,为了便于形成完整的知识体系结构,将相关的知识点框架总结如下:
参考博客:https://blog.youkuaiyun.com/NB_vol_1/article/details/55522822
HEVC帧内预测的大致流程是这样的
- 遍历所有的预测模式,得到每种模式下的残差信号,再对残差信号进行Hadamard变换计算SATD值
- 利用SATD值计算每种预测模式的率失真代价,选取率失真代价最小的几种模式(与PU大小相关)为预测模式集
- 将已编码相邻块的预测模式补充到预测模式集中
- 遍历模式集合中的所有模式,并对残差信号进行正常编码(熵编码),计算率失真代价
- 选取最优的预测模式作为该PU的最优模式
- 当亮度块的模式确定之后,把该模式以及DC、planar、水平方向模式、垂直方向模式作为色度块的候选模式,选取最优的模式即可
HM编码函数
入口函数是estIntraPredQT,流程如下:
- 1.遍历CU下面的每一个PU,对于每一个PU,进行下面的操作
- 2.先初始化访问相邻像素块的工具类
- 3.调用initAdiPattern,对参考像素值进行预处理和滤波
- 4.首先计算需要进行完整RD率失真优化操作的模式的数量numModesForFullRD,可以根据PU的宽度来得到RMD模式的个数与PU的大小有关。PU较小(4x4或8x8)时,RMD个数为8;PU较大(16x16或32x32或64x64)时,RMD个数为3。选择RMD时,uiMode按代价uiCost在CandCostList中从小到大排列在CandModeList中。
- 5.检测快速搜索标识doFastSearch,通过比较numModesForFullRD和帧内预测模式的总数量(35种)是否相等,如果不相等,那么使用快速搜索模式,否则使用普通模式;(一般来说doFastSearch都是true)
- 6.如果doFastSearch是true,那么进行下面的操作:
(1)遍历帧内预测的35种模式,对于每一种模式进行帧内预测,然后使用Hadamard变换计算SATD值,利用SATD的值计算每种模式的率失真代价
(2)从35个模式中选取numModesForFullRD个代价比较优的模式,组成模式候选列表
(3)调用getIntraDirLumaPredictor,根据相邻块的预测模式来对当前块的模式进行预测,若干预测模式
(4)遍历预测模式,如果它不在候选模式列表中,那么把它添加到候选模式列表中 - 7.如果doFastSearch是false,那么表示numModesForFullRD的数量是35,那么所有的帧内预测模式都被添加到候选模式列表中
- 8.用一句话表述步骤6和7,那就是,快速搜索模式下,只选取几种最优可能的模式作为候选模式,普通模式下,所有的帧内预测模式都是候选模式
- 9.遍历候选模式列表,对于其中的每一模式,进行下面的操作:
(1)调用xRecurIntraCodingQT,进行预测变换量化,注意该函数倒数第二个参数是true,表示会按照四叉树的方式继续向下划分
(2)根据率失真代价选取最优的模式 - 10.经过步骤9,我们已经选取了最优的模式,但是该模式下的编码块是继续向下划分的,因此,我们还要计算该模式下,编码块不向下划分的时候的代价(调用xRecurIntraCodingQT,倒数第二个参数设置为false),通过比较编码块划分和不划分两种情况,得到最优的参数和模式
- 11.经过了上面的步骤之后,我们已经得到最优模式和变换系数了,此时我们应该重建当前块,因为后面的PU需要使用该重建块作为预测块。
/*
** 亮度块的帧内预测的入口函数
*/
Void TEncSearch::estIntraPredQT( TComDataCU* pcCU,
TComYuv* pcOrgYuv,
TComYuv* pcPredYuv,
TComYuv* pcResiYuv,
TComYuv* pcRecoYuv,
UInt& ruiDistC,
Bool bLumaOnly )
{
// 删除无关代码*****
// 候选的cost列表
Double CandCostList[ FAST_UDI_MAX_RDMODE_NUM ];
//===== set QP and clear Cbf =====
// 设置QP参数,清理Cbf
if ( pcCU->getSlice()->getPPS()->getUseDQP() == true)
{
pcCU->setQPSubParts( pcCU->getQP(0), 0, uiDepth );
}
else
{
// 进入此处
pcCU->setQPSubParts( pcCU->getSlice()->getSliceQp(), 0, uiDepth );
}
//===== loop over partitions =====
UInt uiPartOffset = 0;
// 循环处理CU下的每一个预测块PU
for( UInt uiPU = 0; uiPU < uiNumPU; uiPU++, uiPartOffset += uiQNumParts )
{
//===== init pattern for luma prediction =====
Bool bAboveAvail = false; // 上面的块是否有效
Bool bLeftAvail = false; // 左边的块是否有效
// 初始化访问相邻块的工具类
pcCU->getPattern()->initPattern ( pcCU, uiInitTrDepth, uiPartOffset );
// 这个函数很重要:主要是在着帧内预测之前,使用重建后的YUV图像对当前PU的相邻样点进行滤波,为接下来的进行的角度预测
// 提供参考样点值
pcCU->getPattern()->initAdiPattern( pcCU, uiPartOffset, uiInitTrDepth, m_piYuvExt, m_iYuvExtStride, m_iYuvExtHeight, bAboveAvail, bLeftAvail );
//===== determine set of modes to be tested (using prediction signal only) =====
// 35种帧内预测模式
Int numModesAvailable = 35; //total number of Intra modes
// 在原始的YUV中获取获亮度的地址
Pel* piOrg = pcOrgYuv ->getLumaAddr( uiPU, uiWidth );
// 在预测的YUV中获取亮度的地址
Pel* piPred = pcPredYuv->getLumaAddr( uiPU, uiWidth );
// 偏移
UInt uiStride = pcPredYuv->getStride(); // 8
UInt uiRdModeList[FAST_UDI_MAX_RDMODE_NUM];
Int numModesForFullRD = g_aucIntraModeNumFast[ uiWidthBit ];//8
Bool doFastSearch = (numModesForFullRD != numModesAvailable);//true
// 使用快速搜索模式
if (doFastSearch)
{
assert(numModesForFullRD < numModesAvailable);
for( Int i=0; i < numModesForFullRD; i++ )
{
// 用于存储每一种模式的消耗
CandCostList[ i ] = MAX_DOUBLE;
}
CandNum = 0;
// 遍历35种帧内预测模式,选取若干个代价比较小的模式作为后续处理的模式
for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ ) // 总共有35种模式,numModesAvailable = 35
{
UInt uiMode = modeIdx;
// 对亮度块进行预测
predIntraLumaAng( pcCU->getPattern(), uiMode, piPred, uiStride, uiWidth, uiHeight, bAboveAvail, bLeftAvail );
// use hadamard transform here
// 使用hadamard变换,计算SATD的值
UInt uiSad = m_pcRdCost->calcHAD(g_bitDepthY, piOrg, uiStride, piPred, uiStride, uiWidth, uiHeight );
UInt iModeBits = xModeBitsIntra( pcCU