我对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个最可能的候选模式。所涉及的函数:
l predIntraLumaAng: 算出当前PU的预测值
l calcHAD: 计算SATD代价
l xModeBitsIntra: 计算当前模式所耗费的比特数目
l xUpdateCandList: 更新模式的代价,保持前N个模式的代价最小
在选出N个模式后,这N个模式会进入xRecurIntraCodingQT函数从而进行TU的分割
5.1.2 xRecurIntraCodingQT
为了加速RQT过程,这个函数会被调用两次:
第一次的调用不进行PU分割为TU的过程,PU直接转换为TU,只为算出N个模式的RD代价,从而选出一个最优的,在这个最优的模式被选出后,会第二次调用这个函数,再对这个最优的模式进行PU的分割。区分第一次和第二次调用的变量是bCheckFirst。这个过程所涉及的函数有:
l xIntraCodingLumaBlk: 进行对当前TU进行求残差,对残差变换,量化,反量化,反变换,重建当前TU等一系列编码工作,并求得失真
l xGetIntraBitsQT: 求出当前模式的所有信息进行熵编码会产生的比特数
l calcRdCos:根据xIntraCodingLumaBlk得到的失真和xGetIntraBitsQT产生的比特数目进行RD代价的计算,从而比较各模式的优劣
l xSetIntraResultQT:保存最优模式的数据
5.1.3 estIntraPredChromaQT
estIntraPredChromaQT会决定当前PU采用哪个色度模型对色度分量进行编码,其中涉及的函数如下:
l getAllowedChromaDir:获得可用的色度
l xRecurIntraChromaCodingQT:进行当前PU的色度分量的一系列编码工作,并求得失真
l xGetIntraBitsQT:进行当前PU的色度分量的熵编码工作,并得到产生的比特数
l calcRdCost:根据失真和码率进行率失真代价的计算
l xSetIntraResultChromaQT:保存当前色度最优模式的信息
5.1.4 xRecurIntraChromaCodingQT
这个函数是求PU的色度分量的残差,首先会对PU进行分割,分割的层数与亮度完全一致。涉及的函数如下:
l xIntraCodingChromaBlk:对当前TU进行色度信息的编码工作,如求残差,变换,量化,反量化,反变换,重建等一系列工作
5.2 帧间
帧间按默认的配置文件设置有两种:inter模式和merge模式
5.2.1 inter模式和merge模式的流程
主要调用流程:
Inter流程
Merge流程
流程中涉及的函数:
l predInterSearch进行的是ME和MC的过程,当然会测试各种情况
l Motioncompensation进行的是MC的工作,由于merge模式没有ME的过程,是将已有的MV信息直接代替当前PU的MV,所以直接进行MC
l encodeResAndCalcRdInterCU是对得到预测值后求出的残差进行TU的划分及RD代价的计算
5.2.2 encodeResAndCalcRdInterCU
涉及的主要函数:
l encodeSkipFlag:编码SKIP模式的flag
l encodeMergeIndex:编码选用哪套运动参数的索引
xEstimateResidualQT:在非SKIP模式的时候要进行RQT的决定,即PU分割为什么样的TU在这个函数里面确定
l xAddSymbolBitsInter:计算当前CU的信息在进行熵编码时所产生的比特数
l xSetResidualQTData:保存当前CU的最优的残差信息
6. 一些其他常用的函数说明:
6.1 预测
6.1.1 帧内
l initPattern:判断周围块的存在性
l initAdiPattern:获取周围像素的值当做生成预测值的像素,并开辟出一片缓存 区存储经过多种滤波类型的预测值
l getPredictorPtr:根据不同模式选择经过不同类型滤波的预测集
l predIntraLumaAng: 对亮度信号进行预测,里面会调用xPredIntraPlanar,xPredIntraAng以及xDCPredFiltering
l predIntraChromaAng: 对色度信号进行预测,里面会调用xPredIntraPlanar和xPredIntraAng
l xPredIntraPlanar: planar模式的预测
l xPredIntraAng: 角度的方向性预测
l xDCPredFiltering: 对DC的预测值进行滤波
l getLumaRecPixels: 获取亮度的重建值,为进行LM模式的预测做准备
l predLMIntraChroma:对LM模式进行预测,即利用亮度的相关性,对色度进行预测
6.1.2 帧间
l getInterMergeCandidates: 获取merge的候选运动参数集
l motionCompensation:进行运动补偿
l xMotionEstimation:进行运动估计
l xEstimateMvPredAMVP:选出代价最小的MVP
l xCheckBestMVP:在知道MV的情况下比较各个MVP的优劣,并保存最优的
l xMergeEstimation:在inter模式时也可以使用merge模式的运动估计方法,这个函数用于计算这种情况时的代价
6.2 变换
l transformNxN:会调用xT和xQuant函数
l invtransformNxN:会调用xDeQuant和xIT函数
l xT: 对残差信号进行变换
l xQuant:对变换系数进行量化
l xDeQuant:反量化
l xIT:反变换
6.3 熵编码
在这节中主要介绍编码端为算RD代价而设计的熵编码函数,实际的熵编码函数在后面的章节中进行介绍
主要函数:
6.3.1 帧内熵编码
l xEncIntraHeader:编码intra的一些头部信息,主要包括:模式号,PU的分割类型,PCM标志,如果是B或P slice,还包括skip的标志位和编码模式的类型
l xEncSubdivCbfQT:会编码Cbf和TU分割的标志位
l xEncCoeffQT:编码每个TU的系数
l encodeCoeffNxN:调用codeCoeffNxN来编码每个TU的残差系数
l encodeTransformSubdivFlag:调用codeTransformSubdivFlag来编码TU分割的标志,是否继续分割
l encodeQtCbf:编码cbf标志位,检查是否有非零的系数
l encodePredMode:编码所采用的编码模式
l encodePartSize:编码PU的分割类型
l encodeIntraDirModeLuma:编码PU的亮度模式号,这里引入了3MPM的机制,具体可参考提案H0238
l encodeIntraDirModeChroma:编码PU的色度模式号
6.3.2 帧间熵编码
l encodePredMode:编码CU所采用的模式,主要决定是inter还是intra
l encodePartSize:编码PU的分割类型
l 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所有的相关信息,里面重要的数据结构包括:
l m_uiAbsIdxInLCU:当前CU在LCU中的位置,位置用Z扫描顺序
l m_puhWidth: CU的宽度
l m_puhHeight:CU的高度
l m_puhDepth: CU所处的深度
l m_pePartSize: PU的类型
l m_pePredMode:编码模式
l m_pcTrCoeffY,m_pcTrCoeffCb,m_pcTrCoeffCr:量化后的系数
l m_puhLumaIntraDir:亮度的模式信息
l m_puhChromaIntraDir:色度的模式信息
l m_puhInterDir:帧间的预测方向
l m_apiMVPIdx:MVP索引
l 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时所用到的主要临时变量
l m_ppcQTTempCoeffY,m_ppcQTTempCoeffCb,m_ppcQTTempCoeffCr:RQT时每层的量化系数,都保存在此,是为了确定最终分割后可以很容易的获取最优值
l m_pcQTTempCoeffY,m_pcQTTempCoeffCb,m_pcQTTempCoeffCr:CU层的量化系数暂存地,只有帧间编码时才会用到,是中间变量
l m_pcQTTempTComYuv: 重建视频的暂存缓冲区
l m_puhQTTempCbf: cbf的暂存
l m_puhQTTempTrIdx:变换层数的暂存
l m_ppcBestCU:存储每层最优(RD代价最小)的CU的信息
l m_ppcTempCU: 存储每层CU的信息的临时变量
l m_ppcPredYuvBest: 存储每层最优的预测值
l m_ppcResiYuvBest:存储每层最优的残差值
l m_ppcRecoYuvBest:存储每层最优的重建值
l m_ppcPredYuvTemp:存储每层预测值的临时变量
l m_ppcResiYuvTemp:存储每层残差值的临时变量
l m_ppcRecoYuvTemp:存储每层重建值的临时变量
l 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的逆过程。涉及的函数如下:
l decodeSkipFlag:解码skip的flag,看是不是skip模式
l decodePredMode:解码编码模式
l decodePartSize: 解码PU分割的类型
l decodePredInfo:解码预测信息,帧内就是解码模式信息,帧间是解码运动信息
l decodeCoeff:解码量化系数
9.2 xDecompressCU: 具体的任务为重建这个LCU,涉及的函数如下:
9.2.1 xReconInter
负责inter部分的重建,主要函数如下:
l xDecodeInterTexture:分别对YUV分量调用invRecurTransformNxN
l invRecurTransformNxN:对特定分量进行TU的反量化和反变换
l addClip:得到残差后会加上预测值形成重建指
l copyPartToPartYuv:如果系数全是零,则直接将重构值赋值为预测值
9.2.2 xReconIntraQT
负责intra部分的重建
xIntraLumaRecQT:亮度信息的重建,会对每个TU调用xIntraRecLumaBlk
xIntraRecLumaBlk:TU的亮度信息反量化及重建工作
xIntraChromaRecQT:色度信息的重建,会对每个TU调用xIntraRecChromaBlk:TU的色度信息反量化及重建工作