AV1的量化参数的范围是0~255,比H264、H265中的最大范围52大了很多。量化步长和量化参数的关系图如图146所示。
对比图1-44,可以看出,图1-46的曲线与图1-44的曲线走向非常相似,只是范围变大了。另外,DC系数的量化步长小于AC系数,因为DC系数更重要,要保存更高的精度。
AV1参考代码的编码器输入参数中有一项名为cq_level,它的取值范围是0~63,cq_level与量化参数的关系用下面代码表示:
static const int quantizer_to_qindex[] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48,
52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100,
104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152,
156, 160, 164, 168, 172, 176, 180, 184, 188, 192, 196, 200, 204,
208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 249, 255,
};
量化参数与量化步长的映射关系用下面代码表示:
static const int16_t dc_qlookup_QTX[QINDEX_RANGE] = {
4, 8, 8, 9, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18,
19, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 29, 30,
31, 32, 32, 33, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42,
43, 43, 44, 45, 46, 47, 48, 48, 49, 50, 51, 52, 53, 53,
54, 55, 56, 57, 57, 58, 59, 60, 61, 62, 62, 63, 64, 65,
66, 66, 67, 68, 69, 70, 70, 71, 72, 73, 74, 74, 75, 76,
77, 78, 78, 79, 80, 81, 81, 82, 83, 84, 85, 85, 87, 88,
90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 108, 110,
111, 113, 114, 116, 117, 118, 120, 121, 123, 125, 127, 129, 131, 134,
136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 161, 164,
166, 169, 172, 174, 177, 180, 182, 185, 187, 190, 192, 195, 199, 202,
205, 208, 211, 214, 217, 220, 223, 226, 230, 233, 237, 240, 243, 247,
250, 253, 257, 261, 265, 269, 272, 276, 280, 284, 288, 292, 296, 300,
304, 309, 313, 317, 322, 326, 330, 335, 340, 344, 349, 354, 359, 364,
369, 374, 379, 384, 389, 395, 400, 406, 411, 417, 423, 429, 435, 441,
447, 454, 461, 467, 475, 482, 489, 497, 505, 513, 522, 530, 539, 549,
559, 569, 579, 590, 602, 614, 626, 640, 654, 668, 684, 700, 717, 736,
755, 775, 796, 819, 843, 869, 896, 925, 955, 988, 1022, 1058, 1098, 1139,
1184, 1232, 1282, 1336,
};
量化运算的除法运算,在实际计算中都要改成乘法加右移运算。在AV1中,通过以下代码来完成转换:
// Qstep是量化步长
for (l = 0; Qstep> 1; l++) Qstep>>= 1;
// 根据量化步长的范围,把65536放大一定倍数后除量化步长,也就是计算量化步长的倒数。如果用65536直接除量化步长,把结果保存下来用在量化过程,那就等于对不同的量化步长,保存了不同的精度。量化步长越大,量化步长的倒数保存的精度越低。为了避免这种情况发生,AV1中根据量化步长的值的范围,在用65536除量化步长之前,先把65536根据量化步长的范围放大一定的倍数,这样就保持了较高的精度
m = 1 + (1 <<(16 + l)) / Qstep ;
// 把量化步长倒数与65536的差保存下来,
*quant_ptr= (int16_t)(m - (1 << 16));
// 把l也保留下来
*quant_shift_ptr = 1 << (16 - l);
AV1为了保持更高的量化精度,做了精心的设计,包括:量化步长倒数需要保存的精度、右移的位数、量化中需要完成前面DCT遗留的移位运算。这让整个的量化过程看上去很复杂。
AV1编码中的量化与反量化过程是在函数aom_quantize_b_helper_c()中完成的,部分代码如下:
for (i = 0; i < non_zero_count; i++) {
// 按照扫描顺序得到索引
const int rc = scan[i];
// 用索引得到DCT后的系数
const int coeff = coeff_ptr[rc];
// 得到当前系数的符号
const int coeff_sign = AOMSIGN(coeff);
// 得到当前系数的绝对值
const int abs_coeff = (coeff ^ coeff_sign) - coeff_sign;
int tmp32;
// 如果使用量化矩阵,就取出量化矩阵中对应位置的值,否则,使用32
const qm_val_t wt = qm_ptr != NULL ? qm_ptr[rc] : (1 << AOM_QM_BITS);
// 判断一下当前DCT值是否大于量化步长,如果大于,再做量化。
if (abs_coeff * wt >= (zbins[rc != 0] << AOM_QM_BITS)) {
// 对DCT值做rounding,clip操作
int64_t tmp =
clamp(abs_coeff + ROUND_POWER_OF_TWO(round_ptr[rc != 0], log_scale), INT16_MIN, INT16_MAX);
// 以下这两行,是在做量化,量化运算虽然是一个除法运算,但是实际运算时都是把除数放大2的N次方,做乘法,然后再右移N位。((tmp * quant_ptr[rc != 0])>> 16) + tmp操作的来源就是上面的 *quant_ptr= (int16_t)(m - (1 << 16));。这个量化过程看上去与众不同,多了一个+tmp的操作,因为在计算保存quant_ptr的时候,做了一个- (1 << 16)操作。这里面的log_scale,是跟变换块大小相关的,小于32×32的为0,32×32的为1,大于32×32的为2。它是变换操作遗留的移位运算。
tmp *= wt;
tmp32 = (int)(((((tmp * quant_ptr[rc != 0])>> 16) + tmp) *
quant_shift_ptr[rc != 0])>>(16 - log_scale + AOM_QM_BITS));
// 把量化后的值加上符号,保存起来
qcoeff_ptr[rc] = (tmp32 ^ coeff_sign) - coeff_sign;
// 下面是反量化的过程,首先得到反量化矩阵中的对应值,如果没有,就是32
const int iwt = iqm_ptr != NULL ? iqm_ptr[rc] : (1 << AOM_QM_BITS);
// 得到反量化系数
const int dequant =
(dequant_ptr[rc != 0] * iwt + (1 <<(AOM_QM_BITS - 1))) >>AOM_QM_BITS;
// 做反量化,这个简单,就是一个乘法操作
const tran_low_t abs_dqcoeff = (tmp32 * dequant) >> log_scale;
// 添加符号位,保存起来
dqcoeff_ptr[rc] = (tran_low_t)((abs_dqcoeff ^ coeff_sign) - coeff_sign);
// 记录最后一个非0值的索引
if (tmp32) eob = i;
}
}
从以上代码可以看出,AV1的量化过程还是比较复杂的。但是,从它的反量化代码来看,量化过程就是一个除法运算。反量化代码为:
abs_dqcoeff = (tmp32 * dequant) >> log_scale;
AV1中也提供了15组量化矩阵,针对不同的变化块大小、亮度和色度分量使用不同的量化矩阵。