VTM中打印码率控制中间变量

简介

本文主要描述了在VTM中打印码率控制中间变量的方法,这些中间变量可以用来辅助分析RD模型

几个值得注意的地方

  • frame bpp计算是整帧的bit减去估计的header bit之后再计算bpp
  • 输出frame 的target bit需要在buffer regularization之后,同时不同帧类型也会有refinement,所以针对3种类型要分别添加(AI,非AI的I,B/P)

    基本格式

    预期输出的格式如下,首先是格式,然后解释每一项具体内容

    IRAP帧

    对于IRAP,格式如下。注意,这些输出在编码得到的txt中为一行,在这里为了显示的方便,人为地进行分行,只要记住实际为1行就可以了。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    RC |Pic_comp_bits: 14655        
    |frame_level:
    |[IRAP madpp:23.078666 bpp:1.129687 bpp_1:20.429248 pic_lambda:92.377179 pic_QP:21] |
    ctu_level:
    |[IRAP madpp:27.015124 bpp:1.281189 bpp_1:21.085979 ctu_lambda:97.747760 ctu_QP:21 no-update] |
    |[IRAP madpp:14.089777 bpp:0.867920 bpp_1:16.233959 ctu_lambda:63.615234 ctu_QP:20 no-update] |
    |[IRAP madpp:33.306836 bpp:1.789673 bpp_1:18.610572 ctu_lambda:78.206851 ctu_QP:20 no-update] |
    |[IRAP madpp:23.004567 bpp:1.619629 bpp_1:14.203603 ctu_lambda:50.138345 ctu_QP:19 no-update] |
    |[IRAP madpp:28.990221 bpp:1.972796 bpp_1:14.694994 ctu_lambda:51.288322 ctu_QP:19 no-update] |
    |[IRAP madpp:10.071074 bpp:0.986747 bpp_1:10.206343 ctu_lambda:50.138345 ctu_QP:19 no-update] |
    |[IRAP madpp:27.242691 bpp:2.680664 bpp_1:10.162665 ctu_lambda:50.138345 ctu_QP:19 no-update] |
    |[IRAP madpp:20.061599 bpp:7.831473 bpp_1:2.561664 ctu_lambda:50.138345 ctu_QP:19 no-update] |
    POC 0 TId: 0 ( I-SLICE, QP 21 ) 102184 bits [Y 41.8476 dB U 44.8285 dB V 44.9024 dB] [MS-SSIM Y 0.995731 U 0.995461 V 0.996197] [Y MSE 67.9896 U MSE 34.2256 V MSE 33.6483] [ET 31 ] [L0 ] [L1 ] |[pic_update:1.129687 1.022596 134.362311 112.467106] |
    |[frame-level-para 103.366762 1.771833]

输出内容的解释

字符解释

竖线|:用作python读取时的分割,没有具体。经过分割,可以分别得到整张图片的数据,CTU的数据和原始RC输出的数据。
冒号::用作连接变量名称与具体数值成为键值对。

变量解释

第一行,RC表示码率控制标志位,Pic_comp_bits表示当前帧的目标码率
第二行,指出接下来是帧级的数据
第三行,IRAP表示当前帧为IRAP帧,madpp表示每个像素平均的CostIntra的指数值(具体可以查看RC代码), bpp表示当前平均每像素的比特,bpp_1表示madpp/bpp的值,后续lambda和QP较好理解。
第四行,表示接下来数据为每个CTU的数据。
第五行到第12行,表示每个ctu的对应数据。格式与帧级相同,值得注意的是,no-update表示当前没有参数更新。没有更新是由于RC代码的逻辑造成的,可以查看TODO:xxxxx。如果没有打开LCURateCtrl也会没有输出
第13行,原始的RC输出,自行添加了SSIM的值,具体方法可以查看VTM中添加SSIM输出
第14行,表示当前帧更新的结果,四个数值分别代表:目标bpp,实际bpp,用未更新的帧级模型参数和目标bpp算出来的lambda,用未更新的帧级模型参数和实际bpp算出来的lambda
第15行,最后的帧级参数

其他帧

此处格式针对其他B/P帧。或者非IRAP类型的I帧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
RC |Pic_comp_bits: 5477 |
frame_level:
|[inter bpp:0.054858 pic_lambda:599.993553 pic_QP:29] |
ctu_level:
|[inter ctu_bpp:0.054871 ctu_lambda:952.430397 ctu_QP:31 ctu_update:0.054858 0.000000 2708.769793 inf] |
|[inter ctu_bpp:0.063477 ctu_lambda:952.430397 ctu_QP:30 ctu_update:0.054858 0.000000 2708.769793 inf] |
|[inter ctu_bpp:0.076355 ctu_lambda:952.430397 ctu_QP:31 ctu_update:0.054858 0.000000 2708.769793 inf] |
|[inter ctu_bpp:0.183838 ctu_lambda:755.944507 ctu_QP:30 ctu_update:0.054858 0.000000 2708.769793 inf] |
|[inter ctu_bpp:0.093959 ctu_lambda:952.430397 ctu_QP:31 ctu_update:0.054858 0.000000 2708.769793 inf] |
|[inter ctu_bpp:0.117188 ctu_lambda:952.430397 ctu_QP:31 ctu_update:0.054858 0.000000 2708.769793 inf] |
|[inter ctu_bpp:0.174107 ctu_lambda:755.944507 ctu_QP:30 ctu_update:0.054858 0.000000 2708.769793 inf] |
|[inter ctu_bpp:1.200056 ctu_lambda:599.993553 ctu_QP:30 ctu_update:0.054858 0.000000 2708.769793 inf] |
POC 1 TId: 0 ( B-SLICE, QP 29 ) 1968 bits [Y 37.4837 dB U 43.8983 dB V 42.7874 dB] [MS-SSIM Y 0.992570 U 0.994312 V 0.995101] [Y MSE 185.7087 U MSE 42.4000 V MSE 54.7597] [ET 4 ] [L0 0 ] [L1 0 ]
|[pic_update:0.054858 0.019391 2708.769793 11224.329493]|
|[frame-level-para 41.068369 -1.015352]

变量解释

  • 此处用inter表示非IRAP帧,某些情况下不够严谨,日后遇到例外进行修改
  • inter是对每个CTU会有参数更新,四个变量的意义与帧级相同:目标bpp,实际bpp,用未更新的帧级模型参数和目标bpp算出来的lambda,用未更新的帧级模型参数和实际bpp算出来的lambda
  • 其余部分与IRAP时相同

中间变量的种类及加入位置

将按照下面四个部分来具体展开本节

  • 文件
  • 位置
  • 具体语句

    宏开关 PrintTemporalResult

  • 作用
    控制所有的中间变量是否输出,是为输出,否为不输出

  • 文件
    TypeDef.h
  • 位置
    任意
  • 具体语句
1
#define PrintTemporalResult                               1

帧级

每帧的目标比特

  • 文件
    EncGOP.cpp
  • 位置
    compressGOP,分配完目标码率,refine之后,判断帧类型(给定初始QP,IRAP,AI, inter)的条件语句中
  • 具体语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
///// 非全I帧的条件中, m_pcRateCtrl->getRCPic()->setTargetBits(bits);之后,作为RA和AI的目标比特输出
#if PrintTemporalResult
printf("RC |Pic_comp_bits: %d |frame_level: ", bits);
}
else
{
printf("RC |Pic_comp_bits: %d |frame_level: ", estimatedBits);
#endif


///// 一般情况下,紧跟else
#if PrintTemporalResult
printf("RC |Pic_comp_bits: %d |frame_level: ", estimatedBits);
#endif

IRAP帧的参数

这些参数包括:

  • MADPerPixel,16*16的块减去均值之后的MAD,除以像素的个数
  • bitsPerPixel,平均每个像素的比特
  • 更正过的bpp(依据mad和bitsPerPixel计算得出,在R-$\lambda$中为MADPerPixel / bitsPerPixel)

在此函数中,IRAP帧和他的CTU共享这个输出。
具体来说

  • 文件
    RateCtrl.cpp
  • 位置
    calculateLambdaIntra,return之前
  • 具体语句
    1
    2
    3
    #if PrintTemporalResult 
    printf("|[IRAP madpp %f bpp %f bpp_1 %f ", MADPerPixel, bitsPerPixel, MADPerPixel / bitsPerPixel);
    #endif

inter帧的参数

  • 文件
    RateCtrl.cpp
  • 位置
    estimatePicLambda,estLambda = alpha * pow( bpp, beta );之后
  • 具体语句
    1
    2
    3
    4
        estLambda = alpha * pow( bpp, beta );
    #if PrintTemporalResult
    printf("|[inter bpp %f ", bpp);
    #endif

每帧的$\lambda$和QP

  • 文件
    EncGOP.cpp
  • 位置
    compressGOP,求解完$\lambda$和QP
    lambda = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->isIRAP());
    之后
  • 具体语句
1
2
3
#if PrintTemporalResult  
printf("pic_lambda: %f pic_QP: %d] |ctu_level: ", lambda, sliceQP);
#endif

编码完成后目标比特,实际比特,目标$\lambda$与实际$\lambda$

  • 文件
    RateCtrl.cpp
  • 位置
    updateAlphaBetaIntra,判断帧的类型是否为IRAP之前

  • 具体语句

1
2
3
4
5
6
7
#if PrintTemporalResult  
double bpp_real = (double)m_picActualBits / (double)m_numberOfPixel;
double bpp_comp = (double)m_targetBits / (double)m_numberOfPixel;

printf(" |[pic_update: %f %f", bpp_comp, bpp_real);
printf(" %f %f] |", (alpha)*pow(bpp_comp, (beta)), (alpha)*pow(bpp_real, (beta)));
#endif

输出帧级参数

  • 文件
    RateCtrl.cpp
  • 位置
    updateAlphaBetaIntra,更新完参数之后,TRCParameter rcPara;之前

  • 具体语句

1
2
3
#if PrintTemporalResult  
printf("|[frame-level-para %f %f] \n", alpha, beta);
#endif

CTU级

IRAP帧的CTU

和IRAP帧的修改相同,不需要额外修改

B/P帧的CTU

  • 文件
    RateCtrl.cpp
  • 位置
    getLCUEstLambda,return之前

  • 具体语句

1
2
3
#if PrintTemporalResult  
printf("|[inter ctu_bpp %f ctu_lambda %f ",bpp, estLambda);
#endif

CTU的更新

IRAP

无更新,但是需要对每个CTU加上结束符。同时在加上结束符之前判断是否打开CTU级码率控制,如果打开则按照前面的格式输出,如果未打开,则每个CTU的格式都是|[no-ctu_rc] |

  • 文件
    EncSlice.cpp
  • 位置
    EncSlice::encodeCtus中,updateAfterCTU之后
  • 具体语句
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #if PrintTemporalResult  

    if (!pCfg->getLCULevelRC())
    {
    printf("|[no-ctu_rc] |");
    }
    else
    {
    if (pcSlice->isIRAP())
    {
    printf("no-update] |");
    }
    }
    #endif

inter

  • 文件
    RateCtrl.cpp
  • 位置
    updateAfterCTU,更新参数之前

  • 具体语句

1
2
3
4
5
6
7
#if PrintTemporalResult  
double bpp_real = (double)m_LCUs[LCUIdx].m_actualBits / (double)m_LCUs[LCUIdx].m_numberOfPixel;
double bpp_comp = (double)m_LCUs[LCUIdx].m_targetBits / (double)m_LCUs[LCUIdx].m_numberOfPixel;

printf("ctu_update %f %f", bpp_comp, bpp_real);
printf(" %f %f] |", (alpha)*pow(bpp_comp, (beta)), (alpha)*pow(bpp_real, (beta)));
#endif

未打开CTU级码率控制时的修改

见CTU更新的IRAP部分