最近需要研究HM的与块划分有关的函数(TLibEncoder\TEncCu
),但是网上可参考的资料很少,加上这部分的代码比较繁杂,所以本文基于最新的16.20版本,尝试剖析一下其实现原理,后续会更新x265对应部分的源码剖析。
HM15.0此部分的代码分析可以参考[这篇博客](https://blog.csdn.net/qq_21880777/article/details/78827285)。
CTU层次
基本分为两步,首先压缩CTU,然后编码CTU
TEncCu::compressCtu
压缩CTU
- 初始化顶层的CTU数据,
m_ppcBestCU[0]->initCtu()
和m_ppcTempCU[0]->initCtu()
- 递归的调用
xCompressCU()
压缩CTU
TEncCu::encodeCtu
编码CTU
- 根据Slice的设置初始化QP的参数
- 递归调用
xEncodeCU()
编码CTU
CU层次
xCompressCU( )
从前面的x
可以看出,该方法是一个protected类型的成员函数,根据xCheckRDCostMerge2Nx2N
和xCheckRDCostInter
定位帧间预测部分,根据xCheckRDCostIntra
定位帧内预测部份。( 补充:关于HM的命名规则和项目架构可以参考我之前的一篇博客。)
传入参数为rpcBestCU
,rpcTempCU
,uiDepth
,其中uiDepth
即为当前CU划分的深度(0、1、2)。
详细步骤如下
1. 参数初始化(读取BestCU数据和参数、初始化用于计算BestCU RD cost的量化参数QP)
参数初始化分为两部分,第一部分读取rpcBestCU
所代表CU的YUV数据、坐标信息、有效组件的数量,分别存放在m_ppcOriginYuv[yiDepth]
、uiLPelX
~uiBelY
、numberValidComponents
中,其中有效组件的类型是枚举类型:
| 枚举类型 | 值 |
| :---------------: | :--: |
| COMPONENT_Y | 0 |
| COMPONENT_Cb | 1 |
| COMPONENT_Cr | 2 |
| MAX_NUM_COMPONENT | 3 |
第二部分,解析图片参数集pps
(picture parameter set)和 序列参数集sps
(sequence parameter set),主要是解析参数集中有关量化参数QP的设置,QP由四个参数控制,分别是iBaseQP
、iMinQP
、iMaxQP
和isAddLowestQP
。
首先xComputeQP()
根据 输入父CUrpcBestCU
和 当前划分深度uiDepth
计算iBaseQP
,然后使用四种方法确定iMinQP
和`iMaxQP:
- 默认(可以是一个范围)
- 由亮度等级确定QP(value)
- 由目标码率确定QP(value,万帅)
- TQB模式(value)
// ToDo: 注释中的参数值根据第一个CU的运行情况确定
// iMinQP = 上下限截断(iBaseQP-idQP), iMaxQP = 上下限截断(iBaseQP+idQP)
// 方法1 (默认,根据pps和sps设置QP最大值和最小值), idQP
if( uiDepth <= pps.getMaxCuDQPDepth() )
{
Int idQP = m_pcEncCfg->getMaxDeltaQP(); // 0
iMinQP = Clip3( -sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP-idQP ); // clip3(minVal,maxVal,a),MAX_QP=51,-sps.getQpBDOffset(CHANNEL_TYPE_LUMA)=51
iMaxQP = Clip3( -sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP+idQP ); // 查看 iBaseQP 和 idQP的值
}
else
{
// 默认该QP的最大值和最小值相同,此时QP只能选择`rpcTempCU->getQP(0)`
iMinQP = rpcTempCU->getQP(0);
iMaxQP = rpcTempCU->getQP(0);
}
// 方法2:根据luma level计算QP offset
// 如果可以将亮度等级转化为QP等级(`m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled()==True`),则进行转化
if ( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() )
{
if ( uiDepth <= pps.getMaxCuDQPDepth() )
{
// keep using the same m_QP_LUMA_OFFSET in the same CTU
m_lumaQPOffset = calculateLumaDQP(rpcTempCU, 0, m_ppcOrigYuv[uiDepth]);
}
iMinQP = Clip3(-sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP - m_lumaQPOffset);
iMaxQP = iMinQP; // force encode choose the modifi