首页 > 代码库 > HM代码介绍

HM代码介绍

我对HM代码结构理解的启蒙文章,转自实验室前辈朱师兄的博客:http://blog.csdn.net/spark19851210/article/details/8964559

1.      环境配置

这个文档描述的版本是HM6.0

运行的方法如下可参考之前的文章:

2.      编码端主函数的调用

技术分享

 

主函数中会调用create函数,但是这里面是空函数,所以不做任何操作

encode是非常重要的函数,负责了实际的编码工作,在里面调用m_cTEncTop的encode

函数对每个GOP进行编码,并对每个GOP调用compressGOP。GOP的概念在HEVC中规定的并没有H.264/AVC那么严格,在H.264/AVC中,GOP是以I slice开始,而HEVC中并没有这样的规定,相当于弱化了HEVC中GOP的概念。

 

3.      GOP划分为Slice

GOP进而会划分为slice,有raster顺序的划分和tile的划分方式,对每个slice会调用

compressSlice来的对其选出最优的参数。然后调用encodeSlice来对其进行实际的熵编码工作。

技术分享

 

4.      Slice的划分(Slice到LCU)

到Slice层面后,会划分等大的LCU,对每个CU进行compress和encode的工作,调用的函数分别为compressCU和encodeCU。而在CompressSlice和EncoderSlice中都会调用encodeCU,是为了保证后续计算码率的准确性,重点在于保证了cabac的状态是准确的。下面将介绍compressCU和encodeCU,由于RDO的时候会编码CU的信息,所以着重介绍compressCU

 

5.      compressCU

把一个slice内部的图像划分为K个LCU,同时这K个LCU是以raster的顺序来进行扫描的。LCU的尺寸默认为64x64,可以通过配置文件中的MaxCUWidth,MaxCUHeight来进行设置。而每个LCU会调用如下的compressCU函数去决定编码的参数。 而LCU是采用四叉树的表示结构,每个LCU会被递归的划分为4个子CU,并根据RD代价来确定是否进行划分,而每个LCU相当于树的第1层,所以他会调用xCompressCU。且在xCompressCU中会对每个子CU递归的调用xCompressCU,如下图所示,当然子CU的尺寸是当前CU的四分之一。

技术分享


   然而在每次xCompressCU时,会对当前CU进行intra模式的测试,如果是B或Pslice,还对其进行merge和inter模式的测试。下面分别介绍intra,inter和merge相关的代码

5.1   帧内

intra的调用流程如下:

 

技术分享

 

estIntraPredQT主要做模式选择的工作,负责选出对于当前PU的最优模式,如DC,或方向性,或planar。estIntraPredChromaQT做了类似的工作,不过是针对于色度。xRecurIntraCodingQT和xRecurIntraChromaCodingQT函数是依据给定的候选模式进行PU的分割,进而依据TU进行重建estIntraPredQT。

5.1.1         estIntraPredQT

在这里面首先对N个候选模式进行粗粒度筛选

代价函数为,从M个模式选出N个最可能的候选模式。所涉及的函数:

predIntraLumaAng: 算出当前PU的预测值

calcHAD: 计算SATD代价

xModeBitsIntra: 计算当前模式所耗费的比特数目

xUpdateCandList: 更新模式的代价,保持前N个模式的代价最小

   在选出N个模式后,这N个模式会进入xRecurIntraCodingQT函数从而进行TU的分割

5.1.2         xRecurIntraCodingQT

为了加速RQT过程,这个函数会被调用两次:

第一次的调用不进行PU分割为TU的过程,PU直接转换为TU,只为算出N个模式的RD代价,从而选出一个最优的,在这个最优的模式被选出后,会第二次调用这个函数,再对这个最优的模式进行PU的分割。区分第一次和第二次调用的变量是bCheckFirst。这个过程所涉及的函数有:

xIntraCodingLumaBlk: 进行对当前TU进行求残差,对残差变换,量化,反量化,反变换,重建当前TU等一系列编码工作,并求得失真

xGetIntraBitsQT: 求出当前模式的所有信息进行熵编码会产生的比特数

calcRdCos:根据xIntraCodingLumaBlk得到的失真和xGetIntraBitsQT产生的比特数目进行RD代价的计算,从而比较各模式的优劣

xSetIntraResultQT:保存最优模式的数据

5.1.3         estIntraPredChromaQT

estIntraPredChromaQT会决定当前PU采用哪个色度模型对色度分量进行编码,其中涉及的函数如下:

getAllowedChromaDir:获得可用的色度

xRecurIntraChromaCodingQT:进行当前PU的色度分量的一系列编码工作,并求得失真

xGetIntraBitsQT:进行当前PU的色度分量的熵编码工作,并得到产生的比特数

calcRdCost:根据失真和码率进行率失真代价的计算

xSetIntraResultChromaQT:保存当前色度最优模式的信息

5.1.4         xRecurIntraChromaCodingQT

这个函数是求PU的色度分量的残差,首先会对PU进行分割,分割的层数与亮度完全一致。涉及的函数如下:

xIntraCodingChromaBlk:对当前TU进行色度信息的编码工作,如求残差,变换,量化,反量化,反变换,重建等一系列工作

5.2   帧间

帧间按默认的配置文件设置有两种:inter模式和merge模式

5.2.1         inter模式和merge模式的流程

主要调用流程:

             Inter流程                     

 

技术分享

 

                               Merge流程

技术分享

 

流程中涉及的函数:

predInterSearch进行的是ME和MC的过程,当然会测试各种情况

Motioncompensation进行的是MC的工作,由于merge模式没有ME的过程,是将已有的MV信息直接代替当前PU的MV,所以直接进行MC

encodeResAndCalcRdInterCU是对得到预测值后求出的残差进行TU的划分及RD代价的计算

 

5.2.2         encodeResAndCalcRdInterCU

 

技术分享

 

 涉及的主要函数:

encodeSkipFlag:编码SKIP模式的flag

encodeMergeIndex:编码选用哪套运动参数的索引

xEstimateResidualQT:在非SKIP模式的时候要进行RQT的决定,即PU分割为什么样的TU在这个函数里面确定

xAddSymbolBitsInter:计算当前CU的信息在进行熵编码时所产生的比特数

xSetResidualQTData:保存当前CU的最优的残差信息

      

6.      一些其他常用的函数说明:

6.1   预测

6.1.1         帧内

initPattern:判断周围块的存在性

initAdiPattern:获取周围像素的值当做生成预测值的像素,并开辟出一片缓存 区存储经过多种滤波类型的预测值

getPredictorPtr:根据不同模式选择经过不同类型滤波的预测集

predIntraLumaAng: 对亮度信号进行预测,里面会调用xPredIntraPlanar,xPredIntraAng以及xDCPredFiltering

predIntraChromaAng: 对色度信号进行预测,里面会调用xPredIntraPlanar和xPredIntraAng

xPredIntraPlanar: planar模式的预测

xPredIntraAng: 角度的方向性预测

xDCPredFiltering: 对DC的预测值进行滤波

getLumaRecPixels: 获取亮度的重建值,为进行LM模式的预测做准备

predLMIntraChroma:对LM模式进行预测,即利用亮度的相关性,对色度进行预测

6.1.2         帧间

getInterMergeCandidates: 获取merge的候选运动参数集

motionCompensation:进行运动补偿

xMotionEstimation:进行运动估计

xEstimateMvPredAMVP:选出代价最小的MVP

xCheckBestMVP:在知道MV的情况下比较各个MVP的优劣,并保存最优的

xMergeEstimation:在inter模式时也可以使用merge模式的运动估计方法,这个函数用于计算这种情况时的代价

6.2   变换

transformNxN:会调用xT和xQuant函数

invtransformNxN:会调用xDeQuant和xIT函数

xT: 对残差信号进行变换

xQuant:对变换系数进行量化

xDeQuant:反量化

xIT:反变换

 

6.3   熵编码

在这节中主要介绍编码端为算RD代价而设计的熵编码函数,实际的熵编码函数在后面的章节中进行介绍

主要函数:

6.3.1         帧内熵编码

xEncIntraHeader:编码intra的一些头部信息,主要包括:模式号,PU的分割类型,PCM标志,如果是B或P slice,还包括skip的标志位和编码模式的类型

xEncSubdivCbfQT:会编码Cbf和TU分割的标志位

xEncCoeffQT:编码每个TU的系数

encodeCoeffNxN:调用codeCoeffNxN来编码每个TU的残差系数

encodeTransformSubdivFlag:调用codeTransformSubdivFlag来编码TU分割的标志,是否继续分割

encodeQtCbf:编码cbf标志位,检查是否有非零的系数

encodePredMode:编码所采用的编码模式

encodePartSize:编码PU的分割类型

encodeIntraDirModeLuma:编码PU的亮度模式号,这里引入了3MPM的机制,具体可参考提案H0238

encodeIntraDirModeChroma:编码PU的色度模式号

6.3.2         帧间熵编码

encodePredMode:编码CU所采用的模式,主要决定是inter还是intra

encodePartSize:编码PU的分割类型

encodePredInfo:编码运动参数

(1)       merge的标志位来区别是否采用merge模式,具体函数:encodeMergeFlag

然后分(2)和(3)两种情况。

(2)       merge模式:只需传输运动候选集的索引,具体函数:encodeMergeIndex

(3)       正常的inter模式

A.    encodeInterDirPU:编码帧间的预测方向,前向,后向,或多方向

B.     encodeRefFrmIdxPU: 编码参考帧索引

C.     encodeMvdPU:编码MV的残差MVD

D.    encodeMVPIdxPU: 编码MVP的索引

7.      EncoderCU

HEVC以LCU为基本单位,所以在进行熵编码时也是以LCU为单位进行的EncodeCU会调用从而对每个CU进行编码,如下图所示,在xEncodeCU中会调用如下几个函数:

技术分享

 

encodeSkipFlag编码是否是skip模式

encodeMergeIndex如果是skip模式会编码选用哪套MVP的参数

encodePredMode编码CU的模式,是intra还是inter

encodePartSize编码CU中的PU的类型

encodeIPCMInfo 如果选用了PCM模式会编码PCM模式的信息

encodePredInfo编码预测的信息,如果是帧内,编码模式号,如果是帧间,则编码运动信息

encodeCoeff编码残差系数

encodeCoeff中会编码TU的分割标志位,cbf和残差系数的信息

而具体的信息可以参照3.3节

8.      一些主要变量和数据结构的说明:

8.1  TComDataCU:LCU及其子CU的数据结构,存储了一个LCU所有的相关信息,里面重要的数据结构包括:

m_uiCUAddr:一个LCU在slice中的位置

m_uiAbsIdxInLCU:当前CU在LCU中的位置,位置用Z扫描顺序

m_puhWidth: CU的宽度

m_puhHeight:CU的高度

m_puhDepth: CU所处的深度

m_pePartSize: PU的类型

m_pePredMode:编码模式

m_pcTrCoeffY,m_pcTrCoeffCb,m_pcTrCoeffCr:量化后的系数

m_puhLumaIntraDir:亮度的模式信息

m_puhChromaIntraDir:色度的模式信息

m_puhInterDir:帧间的预测方向

m_apiMVPIdx:MVP索引

m_apiMVPNum:MVP的候选数

以上的数据结构都是以动态存储来分配空间,一般只有一维,这一维具体取值的含义就是CU里面的每个对应的4x4的小块的信息,而开辟的数目就是CU所包含的4x4的数目,而在实际编码时也是编码了这些信息。

需要着重说明2点

(1)    m_uiCUAddr是一个LCU在slice中的位置,是raster的扫描顺序

 

(2)    m_uiAbsIdxInLCU是表明CU在LCU中的位置,Z扫描顺序,最小单位为1,代表     

其中的一个4x4子块,Z扫描顺序如下图所示

(3)   Z扫描转换,如下图所示,展示了一个CU内部的Z扫描的顺序,在hevc中,Z扫描顺序是以4x4为基本单位的,一个具有默认尺寸的LCU,具有256个基本单元

 

8.2   RDO时所用到的主要临时变量

m_ppcQTTempCoeffY,m_ppcQTTempCoeffCb,m_ppcQTTempCoeffCr:RQT时每层的量化系数,都保存在此,是为了确定最终分割后可以很容易的获取最优值

m_pcQTTempCoeffY,m_pcQTTempCoeffCb,m_pcQTTempCoeffCr:CU层的量化系数暂存地,只有帧间编码时才会用到,是中间变量

m_pcQTTempTComYuv: 重建视频的暂存缓冲区

m_puhQTTempCbf: cbf的暂存

m_puhQTTempTrIdx:变换层数的暂存

m_ppcBestCU:存储每层最优(RD代价最小)的CU的信息

m_ppcTempCU:  存储每层CU的信息的临时变量

m_ppcPredYuvBest: 存储每层最优的预测值

m_ppcResiYuvBest:存储每层最优的残差值

m_ppcRecoYuvBest:存储每层最优的重建值

m_ppcPredYuvTemp:存储每层预测值的临时变量

m_ppcResiYuvTemp:存储每层残差值的临时变量

m_ppcRecoYuvTemp:存储每层重建值的临时变量

m_ppcOrigYuv::存储每层对应的原始值

 技术分享

8.3   yuv的存储的关系

8.3.1         TComYuv数据结构

由m_apiBufY,m_apiBufU以及m_apiBufV三个buffer组成,通用的yuv数据结构,存储是yuv的亮度和色度信息

8.3.2         TComPicYuv数据结构

图像层级的yuv数据结构,存储的是一帧的yuv信息,主要用于ALF和去方块滤波等处理的过程中

         TComYuv的类型的变量存储的是RDO时的值,最优的信息要存在TComPicYuv中,便于输出和进行全局处理

      

9.      解码端的简单说明

9.1    xDecodeCU: 与xEncodeCU类似,进行LCU的读取码流并存至变量的工作,可以理解为与xEncodeCU的逆过程。涉及的函数如下:

decodeSkipFlag:解码skip的flag,看是不是skip模式

decodePredMode:解码编码模式

decodePartSize: 解码PU分割的类型

decodePredInfo:解码预测信息,帧内就是解码模式信息,帧间是解码运动信息

decodeCoeff:解码量化系数

9.2   xDecompressCU: 具体的任务为重建这个LCU,涉及的函数如下:

9.2.1         xReconInter

负责inter部分的重建,主要函数如下:

xDecodeInterTexture:分别对YUV分量调用invRecurTransformNxN

invRecurTransformNxN:对特定分量进行TU的反量化和反变换

addClip:得到残差后会加上预测值形成重建指

copyPartToPartYuv:如果系数全是零,则直接将重构值赋值为预测值

9.2.2         xReconIntraQT

负责intra部分的重建

xIntraLumaRecQT:亮度信息的重建,会对每个TU调用xIntraRecLumaBlk

xIntraRecLumaBlk:TU的亮度信息反量化及重建工作

xIntraChromaRecQT:色度信息的重建,会对每个TU调用xIntraRecChromaBlk:TU的色度信息反量化及重建工作

HM代码介绍