HM10.1码率控制模块代码详解(1)---------序列级码率控制类TEncRCSeq介绍

由于要修改码率控制算法,所以需要对该模块代码了解的非常细致,写一篇文章记录自己的学习过程,尽可能的注释每一行代码,以及每一步操作的来源。

HM10.1中使用的码率控制算法来源于K0103提案,该提案对应的论文为李斌博士所写的“λ Domain Rate Control Algorithm for High Efficiency Video Coding”。

HM中与码率控制相关的文件只有两个,一个是TEncRateCtrl.h,一个是与其对应的源文件TEncRateCtrl.cpp,这两个文件都在项目TLibEncoder中。

在这里将按照TEncRateCtrl.h中的代码顺序进行分析,然后遇到函数声明时会在TEncRateCtrl.cpp中寻找该函数定义,并进行详细分析。

在这里插入图片描述
62至66行代码定义了一些常量。
62行定义了一个QP值为-999,当模式为skip mode时,将会设置QP为该值。
63行定义了平滑窗口,该窗口使用在GOP级比特分配中,如下图。
在这里插入图片描述
该图片为从李斌博士的论文中截取的公式图,其中SW就是63行定义的平滑窗口大小。
68行至76行定义了一个结构体,该结构体用于描述一个LCU在进行码率控制时的各个参数。
70行的m_actualBits表示当前LCU编码之后真正需要的比特数。
71行的m_QP表示编码当前LCU时选择的量化参数。
72行的m_targetBits表示分配给当前LCU的目标比特数。
73行的m_lambda表示当前LCU的λ值。
74行的m_MAD的作用以及计算方式如下图。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由公式(15)可以看出,m_MAD是一个LCU的预测残差的绝对值均值。
由公式(16)和(14)可以得出,m_MAD的值作为当前LCU分配目标比特时的权重,当m_MAD的值越大,其分配的目标比特数越多,这里需要注意的是在为当前LCU分配目标比特时,利用的m_MAD是和当前帧距离最近的同级别已编码帧中相同位置的LCU的m_MAD值。
75行的m_numberOfPixel表示的是当前LCU中含有的像素个数。
78至82行定义了一个结构体,该结构体用于记录R-λ公式中的两个参数值,如下公式。
在这里插入图片描述

接下来主要介绍TEncRateCtrl.h中的类,该头文件中有4个类,按照在头文件中的顺序如下。
1.class TEncRCseq:序列级码率控制
2.class TEncRCGOP:图片组GOP级码率控制
3.class TEncRCPic:图片级/帧级码率控制
4.class TEncRateCtrl:码率控制管理类

在这里插入图片描述
*

为了区分是头文件还是源文件中的代码,这里将源文件代码解析加粗

84行声明了一个类TEncRCSeq,即序列级码率控制类。
87行是其构造函数,完成一些初始化功能,具体定义如下,初始化的是该类中的成员变量。
在这里插入图片描述
49行的m_totalFrames表示需要编码的帧数,初始化为0。
50行的m_targetRate表示的是目标比特率,初始化为0,该值对应的就是配置文件中码率控制模块下的TargetBitrate,单位是bps,如下图。
在这里插入图片描述

51行的m_frameRate表示的是帧率,初始化为0。
52行的m_targetBits表示的是分配给当前视频序列的目标比特数,利用目标比特率、帧率和视频中的帧数进行计算,初始化为0。
53行的m_GOPSize表示的是图像组中含有的图像帧数,初始化为0。
54行的m_picWidth表示的是图片的宽度,初始化为0。
55行的m_picHeight表示的是图片的高度,初始化为0。
56行的m_LCUWidth表示的是LCU的宽度,初始化为0。
57行的m_LCUHeight表示的是LCU的高度,初始化为0。
58行的m_numberOfLevel表示的是视频序列中图像的层级数量或者level数量,初始化为0。
59行的m_numberOfLCU表示的是一帧图像中含有LCU的数量,初始化为0。
60行的m_averageBits表示的是每一帧分配到的目标比特数,初始化为0。
61行的m_bitsRatio是一个整型指针,用于记录一个GOP中每个图像所占的比特数比例,初始化为null。
62行的m_GOPID2Level也是一个整型指针,好像是给GOP中图像分级的。
63行的m_picPara是一个结构体指针,用于记录每个图片的alpha和beta参数,初始化为空。
64行的m_LCUPara是一个指向指针的指针,可以看做是一个二维数组,第一个下标标识LCU所在的帧数,第二个下标标识LCU在帧中的位置,二维数组是一个结构体,其中记录着LCU的alpha和beta参数。
65行的m_numberOfPixel表示的是一帧图像中的像素数量,初始化为0。
66行的m_framesLeft表示的是剩余的未编码的帧数,初始化为0。
67行的m_bitsLeft表示的是剩余的未使用的比特数目,初始化未0。
68行的m_useLCUSeparateModel是一个布尔类型,表示的是是否使用R-λ模型用于计算LCU的编码参数,初始为false。

88行是TEncRCSeq类中析构函数的声明,函数定义如下。
在这里插入图片描述
在析构函数中,调用了TEncRCSeq的成员函数destroy(),该函数的定义如下。
在这里插入图片描述
由于是析构函数调用的函数,所以主要是执行释放堆区内存的任务,TEncRCSeq类中有4个指针类型成员变量,所以对这四个指针进行判断释放的操作,具体如下。
152行至156行,先判断m_bitsRatio是否为空,如果不为空,则说明其在堆区分配了内存,所以先释放内存,然后再将其指向空,防止野指针。
158行至162行,先判断m_GOPID2Level是否为空,如果不为空,则说明其在堆区分配了内存,所以先释放内存,然后再将其指向空,防止野指针。
164行至168行,先判断m_picPara是否为空,如果不为空,则说明其在堆区分配了内存,所以先释放内存,然后再将其指向空,防止野指针。
170行到178行,先判断m_LCUPara是否为空,如果不为空,首先释放每个图片中LCU所占据的堆区内存,然后释放m_LCUPara占据的堆区内存,然后再将其指向空,防止野指针。

接下来就是TEncRCSeq这个类中的成员函数的介绍。
91行的成员函数creat()是一个将视频序列信息赋值给TEncRCSeq类中成员变量的函数,定义如下,由于该函数比较长,分段进行分析。
在这里插入图片描述
76行至78行是参数列表。
80行调用了destroy()函数,该函数的具体定义上面刚刚分析,主要目的是释放指针指向的堆区内存,并将其指向空。
81行至90行是将基本的视频序列参数赋给给相应的成员变量,其余的成员变量的值可以由这些基本的成员变量值进行计算。
92行利用图像的宽度和高度计算一帧图像中像素的数目。
93行利用目标比特率除以帧率得到一帧图像需要的比特数,然后和视频序列的帧数相乘,得到当前视频序列的目标比特数。
94行m_seqTargetBpp是TEncRCSeq类的一个成员变量,表示该视频序列的每个像素分配的比特数,Bpp表示bits per pixel,利用目标比特率除以帧率得到一帧图像需要的比特数,然后除以一帧图像中像素的数目得到Bpp。

在这里插入图片描述
95行至109行的代码可以用以下图片中的定义来解释。
在这里插入图片描述
其中红色圆圈和蓝色圆圈中的变量分别表示TEncRCSeq这个类中的成员变量m_alphaUpdate和m_betaUpdate,这两个变量是用于更新alpha和beta时使用的,如下图。
在这里插入图片描述
在这里插入图片描述
110行利用当前视频序列分配的总的比特数除以视频序列的帧数得到一帧图像分配的比特数。
111行计算的是图像的宽度与LCU的宽度的比值,结果向上取整。
112行计算的是图像的高度与LCU的高度的比值,结果向上取整。
113行将111行和112行得到的结果相乘,计算一帧图像中含有的LCU的数量。
115行为m_bitsRatio成员变量在堆区分配内存,分配内存的大小为图像组GOP中含有的图像数量。
116行至119行为m_bitsRatio中的每个元素赋值,值都为1。
121行为m_GOPID2Level在堆区分配内存,分配内存的大小为图像组GOP中含有的图像数量。
122行至125行为m_GOPID2Level中的每个元素赋值,值都为1。
127行为m_picPara在堆区分配内存,分配内存的大小为视频序列中的图像数目。
128行至132行为m_picPara中的每个结构体赋值,m_alpha和m_beta都为0。

在这里插入图片描述
134行首先判断成员变量m_useLCUSeparateModel的值,该值对应于配置文件中码率控制模块下的RCLCUSeparateModel,如下图。一旦开启码率控制,该值默认为1,即在LCU层使用R-lambda模型。
在这里插入图片描述
136行首先为成员变量m_LCUPara在堆区分配内存空间,空间大小为视频中帧的数量。139行为m_LCUPara中的每个指针元素分配内存空间,空间大小为一帧图像中LCU的个数。
140行至144行为每一个LCU所对应的结构体中的m_alpha和m_beta赋值,其值都为0。
148行将剩余帧的数目m_frameLeft更新为m_totalFrames,因为现在还没有编码。
149行将剩余比特数更新为m_targetBits,因为现在还没有编码。

在这里插入图片描述
94行的destroy()成员函数已经介绍过。
95行的成员函数initBitsRatio()成员函数的定义如下。
在这里插入图片描述
183行至188行,将形参bitsRatio中的元素值赋值给m_bitsRatio。
96行的initGOPID2Level()成员函数的定义如下。
在这里插入图片描述
从191行至197行,将形参GOPID2Level中的元素值赋值为成员变量m_GOPID2Level中的元素。
97行的initPicPara()成员函数的具体定义如下。
在这里插入图片描述
201行先判断m_picPara指针是否为空,如果为空,则程序终止运行,若不为空,则继续。
203行至210行,先判断picPara是否为空,该参数默认是空的,如果为空,则将m_picPara中所有的结构体中的m_alpha置为3.2003,m_beta置为-1.367,这个初始值是李斌博士论文中给出的。如下图。

在这里插入图片描述
211行至217行,若形参的值不为空,则将形参的值赋给m_picPara。

####头文件中代码解析不加粗,源文件代码解析加粗#####

98行的成员函数initLCUPara()的定义如下。
在这里插入图片描述
222行至225行,判断成员变量m_LCUPara是否为空,若为空,则函数调用结束。
226行至236行,如果形参为空,则将m_LCUPara中的每一个LCU的结构体中的m_alpha和m_beta赋值为3.2003和-1.367。
237行至246行,若形参不为空,则将其对应的值赋给m_LCUPara中的元素。

99行的成员函数updateAfterPic()的具体定义如下。
在这里插入图片描述
该函数的含义是当编码完一帧图像之后,需要对m_bitsLeft和m_framesLeft这两个成员函数进行更新,形参bits表示的该帧图像编码所需要的真正的比特数。剩余帧数减1,剩余比特数为原本的比特数减去该帧编码真正的比特数。

100行的成员函数getRefineBitsForIntra()函数的定义如下。
在这里插入图片描述
当采用帧内编码模式时,K0103码率控制提案会对帧层的比特分配数进行修正,如下图所示。该图即对应着getRefineBitsForIntra()函数的操作。
在这里插入图片描述
257行应该是除以m_picHeight / m_picWidth,我感觉是这样,估计是个bug。
258行至267行对应的操作如上图所示。

在这里插入图片描述
103行是返回总共的帧数。
104行是返回目标比特率。
105行是返回视频序列的帧率。
106行是返回GOP中含有的图片个数。
107行返回图片的宽度。
108行返回图片的高度。
109行返回LCU的宽度。
110行返回LCU的高度。
111行返回视频中帧的级别数目,如图像组GOP中有4帧图像,被分为了三级,则第一帧为级别1,第二帧和第四帧为级别3,而第三帧为级别2,级别越低分配的比特数越多。
112行返回每一帧分配到的目标比特数。
113行返回未编码的帧平均分配到的比特数,用剩余的比特数除以剩余的帧数计算得到。
114行返回bool类型的成员变量,m_useLCUSeparateModel。

在这里插入图片描述
116行返回一帧图片中的像素数量。
117行返回视频序列分配的比特数。
118行返回一帧图片中的LCU的个数。
119行返回一个整型指针,该指针中记录着一个GOP中每个图片所占比特数比例(权重)。
120行返回一个整型数据,其值为GOP中第idx个图片在分配目标比特数时的权重。
121行至122行同119行至120行相同。
123行返回一个结构体指针,该指针指向堆区一块内存,内存中存储着每帧图像对应的alpha和beta值。
124行返回序号为level的图像对应的结构体。
125行是设置序号为level的图像对应的结构体,将其值更新为形参para的值。
126行返回一个二维数组的数组名。
127行返回的是一个指针,该指针指向序号为level的图像为其存储LCU的aplha和beta值分配的内存空间。
128行至129行返回的就是序号为level的图像中序号为LCUIdx的LCU所对应的结构体。
130行至131行就是更新序号为level的图像中序号为LCUIdx的LCU的结构体为形参para。
133行返回未编码的剩余帧数。
134行返回未使用的比特数。
136行返回视频序列的每像素比特数。
137和138返回更新模型参数的值。
在这里插入图片描述
141行至166行表示TEncRCSeq这个类中的一些私有权限成员变量,其含义已经在上面介绍过。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值