源码阅读 x264 - 量化
x264
中 x264_quant_init
函数中初始化了量化有关的函数,本文分析部分量化函数的实现
量化是 H.264
视频压缩编码中对视频质量影响最大的地方,也是会导致信息丢失的地方。量化的原理可以表示为下面公式:
\[ FQ = round(y / Qstep) \]
其中:
- \(y\) 为输入样本点编码
- \(Qstep\) 为量化步长
- \(FQ\) 为 \(y\) 的量化值
- \(round()\) 为取整函数(其输出为与输入实数最近的整数)
其相反过程,即反量化为:
\[ y' = FQ \times Qstep \]
如果 Qstep
较大,则量化值 FQ
取值较小,其相应的编码长度较小,但是但反量化时损失较多的图像细节信息。简而言之,Qstep
越大,视频压缩编码后体积越小,视频质量越差。
在 H.264
中,量化步长 Qstep
共有 52
个值,如下表所示。其中 QP
是量化参数,是量化步长的序号。当 QP
取最小值 0 时代表最精细的量化,当 QP
取最大值 51 时代表最粗糙的量化。QP
每增加 6,Qstep
增加一倍。
x264_quant_init
void x264_quant_init(x264_t *h, uint32_t cpu, x264_quant_function_t *pf)
{
pf->quant_8x8 = quant_8x8; // 针对 8x8DCT 的量化
pf->quant_4x4 = quant_4x4; // 量化 4x4=16 个
pf->quant_4x4x4 = quant_4x4x4; // 处理 4 个 4x4 的块
//Intra16x16 中,16 个 DC 系数 Hadamard 变换后对的它们量化
pf->quant_4x4_dc = quant_4x4_dc;
pf->quant_2x2_dc = quant_2x2_dc;
pf->dequant_4x4 = dequant_4x4; // 反量化 4x4=16 个
pf->dequant_4x4_dc = dequant_4x4_dc; // 处理 4 个 4x4 的块
pf->dequant_8x8 = dequant_8x8; // 针对 8x8DCT 的反量化
// Intra16x16 中,16 个 DC 系数 Hadamard 变换后对的它们反量化
pf->idct_dequant_2x4_dc = idct_dequant_2x4_dc;
pf->idct_dequant_2x4_dconly = idct_dequant_2x4_dconly;
pf->optimize_chroma_2x2_dc = optimize_chroma_2x2_dc;
pf->optimize_chroma_2x4_dc = optimize_chroma_2x4_dc;
pf->denoise_dct = denoise_dct;
pf->decimate_score15 = decimate_score15;
pf->decimate_score16 = decimate_score16;
pf->decimate_score64 = decimate_score64;
pf->coeff_last4 = coeff_last4;
pf->coeff_last8 = coeff_last8;
pf->coeff_last[DCT_LUMA_AC] = coeff_last15;
pf->coeff_last[DCT_LUMA_4x4] = coeff_last16;
pf->coeff_last[DCT_LUMA_8x8] = coeff_last64;
pf->coeff_level_run4 = coeff_level_run4;
pf->coeff_level_run8 = coeff_level_run8;
pf->coeff_level_run[DCT_LUMA_AC] = coeff_level_run15;
pf->coeff_level_run[DCT_LUMA_4x4] = coeff_level_run16;
/* 此处省略大量平台的汇编函数初始化代码 */
......
quant_4x4
quant_4x4()
用于对 4x4
的 DCT
残差矩阵进行量化。该函数的定义位于 common/quant.c
,如下所示。
/* 功能:quant_4x4() 用于对 4x4 的 DCT 残差矩阵进行量化 */
// 输入输出都是 dct[16]
static int quant_4x4(dctcoef dct[16], udctcoef mf[16], udctcoef bias[16] ) {
int nz = 0;
for (int i = 0; i < 16; i++)
QUANT_ONE(dct[i], mf[i], bias[i] );
return !!nz;
}
/* 其中 QUANT_ONE 定义如下 */
#define QUANT_ONE(coef, mf, f) { \
if((coef) > 0 ) (coef) = (f + (coef)) * (mf) >> 16; \
else (coef) = - ((f - (coef)) * (mf) >> 16); \
nz |= (coef); \
}
其中,QUANT_ONE()
完成了一个 DCT
系数的量化工作,从 QUANT_ONE()
的定义可以看出,它实现了上文提到的 H.264
标准中的量化公式:
\[ \left | Z_{ij}\right | = (\left | W_{ij}\right | \cdot MF + f) >> qbits \]
可以看出 quant_4x4()
循环 16
次调用了 QUANT_ONE()
完成了量化工作。并且将 DCT
系数值,MF
值,bias
偏移值直接传递给了该宏。
quant_4x4x4
quant_4x4x4()
用于对 4 个 4x4 的 DCT 残差矩阵进行量化。该函数的定义位于 common/quant.c
,从 quant_4x4x4()
的定义可以看出,该函数相当于调用了 4 次 quant_4x4()
函数。对应的代码分析如下:
/* 对 4 个 4x4 的 DCT 残差矩阵进行量化, 从 quant_4x4x4() 的定义可以看出,该函数相当于调用了 4 次 quant_4x4() 函数 */
static int quant_4x4x4(dctcoef dct[4][16], udctcoef mf[16], udctcoef bias[16] ) {
int nza = 0;
for (int j = 0; j < 4; j++) {
int nz = 0;
for (int i = 0; i < 16; i++)
QUANT_ONE(dct[j][i], mf[i], bias[i] );
nza |= (!!nz)<<j;
}
return nza;
}
quant_8x8
quant_8x8()
函数的定义位于 common/quant.c
中,定义如下:
static int quant_8x8(dctcoef dct[64], udctcoef mf[64], udctcoef bias[64] ) {
int nz = 0;
for (int i = 0; i < 64; i++)
QUANT_ONE(dct[i], mf[i], bias[i] );
return !!nz;
}