基于ERP特性的自适应QP

来源:互联网 发布:web api 数据库 编辑:程序博客网 时间:2024/06/10 18:30

在之前对投影格式的分析中已经反复提到了,ERP存在与纬度相关的横向拉伸,越向两极拉伸越严重。基于这个特性,可以对编码进行相关的优化。在JEVT F会议提案中出现了自适应QP的方法,获得了不错的效果。

JVET F会议中出现了三种基于ERP特性的自适应QP,提案号分别为F0038、F0049和F0072,其中F0038和F0049相似,而且效果要远优于F0072,下面就两者进行简单介绍。

F0038和F0049基本思想相同,都是根据纬度来调整QP,高纬度使用大QP,向赤道QP变小。两者采用了相同的公式对QP进行调整:

QP=QP3log2(w)

其中w与纬度相关。两篇提案的区别就在于w的计算方法不同。

1. F0038

F0038使用cos(πy)作为w,其中 y=φ/π,0.5<=y<=0.5,π/2<=φ<=π/2,φ是纬度弧长值,y为纬度角度值。这样其QP公式为:

QP=QP3log2(cos(πy))

这里w=cos(πy)范围为0~1。

基本QP为27时,其效果如下图:
这里写图片描述
提案中没有对一个CTU的y值做介绍,猜想可能使用的是CTU中心像素的y值。

编码结果RA下平均编码增益为4.3%,DrivingInCountry序列增益最大为9.0%。而全I帧1帧下的平均增益为2.9%,性能提升是很可观的。

2. F0049

F0049直接使用的是基于WS-PSNR的权重的w。
WS-PSNR的权重为:
这里写图片描述
F0049对WS-PSNR的权重进行了归一化。
这里写图片描述
其中Total_weight是整幅图所有高度的WS-PSNR权重和。
这里写图片描述
归一化后w(i,j)范围为0~1.570795。

因为要在CTU级别上做,每个CTU使用同一个QP,F0049使用的是CTU中所有高度对应的w(i,j)的平均值。

其效果与F0038相似,RA下平均编码增益为4.3%,DrivingInCountry序列增益最大为9.0%。

3. 两者区别
两者的区别就在于w的计算和取值范围。
F0038使用w=cos(πy),范围为0~1,此时3log2(cos(πy))均为正值。此时,QP只会变大,因此F0038是完全以牺牲质量为代价换取更小的bit数。
F0049使用的是归一化的WS-PSNR权重,范围为0~1.570795,当w小于1时,3log2(w)为正值,QP会变大,而当w大于1时,QP会变小。从ERP上考虑,两极部分由于其拉伸,使用更大的QP,而赤道部分没有拉伸,使用更小的QP,整体上就是降低了两极的质量而提高了赤道附近的质量,看上去更合理一些。

4. 实现
下面以JVET-0049为例介绍HM中的实现。

1.开启deltaQP
默认情况下,编码器熵编码中会直接使用帧层的QP。要对每个CTU使用不同的QP,就需要开启deltaQP。
这里写图片描述
查语法可知,deltaQP是由cu_qp_delta_enabled_flag控制的,找到PPS中cu_qp_delta_enabled_flag:

WRITE_FLAG( pcPPS->getUseDQP() ? 1 : 0, "cu_qp_delta_enabled_flag" );

可以确定getUseDQP对应的m_useDQP,就是cu_qp_delta_enabled_flag。

  Bool                   getUseDQP() const                                                { return m_useDQP;                              }

因此需要开启m_useDQP。在xInitPPS函数中直接设置m_useDQP为true。

    pps.setUseDQP(true);

2.CTU级别的QP
在compressSlice中会调用compressCtu对每一个CTU进行编码,在这之前计算每一个CTU的QP,记作ctuQp,计算过程简单,不再多说了。

实际QP起作用的地方是在xCompressCU中,在分块划分之前会确定iMinQP和iMaxQP,然后尝试iMinQP到iMaxQP中的所有QP,进行划分。默认情况下,iMinQP和iMaxQP相等,这样,我们只需要令iMinQP和iMaxQP都等于ctuQp即可。

3.修改lambda
在尝试实现时,发现只修改QP,性能要比JVET0049差了一半多,其实JVET0049并非只修改了QP,还根据QP修改了lambda,在提案中并没有提到,因此其性能的提升其实有很大一部分是来自修改lambda。

lambda的修改很容易,HM中已经有现成的函数根据QP来更新lambda了,只需要在compressCtu前调用即可。

updateLambda(pcSlice, (Double)ctuQP);