为了进一步节省图像的传输码率,需要对图像进行压缩,通常采用变换编码及量化来消除图像中的相关性以减少图像编码的动态范围。本文主要介绍变换编码的相关内容,并给出x264中变换编码的代码分析。
变换编码将图像时域信号变换成频域信号,在频域中图像信号能量大部分集中在低频区域,相对时域信号,码率有较大的下降。
h.264对图像或预测残差采用4×4整数离散余弦变换技术,避免了以往标准中使用的通用8×8离散余弦变换逆变换经常出现的失配问题。
在图像编码中,变换编码和量化从原理上讲是两个独立的过程。但在h.264中,将两个过程中的乘法合二为一,并进一步采用整数运算,减少编解码的运算量,提高图像压缩的实时性,这些措施对峰值信噪比(psnr)的影响很小,一般低于0.02db,可不计。h.264中整数变换及量化具体过程如下图所示,其中,如果输入块是色度块或帧内16×16预测模式的亮度块,则将宏块中各4×4块的整数余弦变换的直流分量组合起来再进行
hadamard 变换,进一步压缩码率。
下面给出编码器中变换编码及量化过程的流程。
dct变换的核心理念就是把图像的低频信息(对应大面积平坦区域)变换到系数矩阵的左上角,而把高频信息变换到系数矩阵的右下角,这样就可以在压缩的时候(量化)去除掉人眼不敏感的高频信息(位于矩阵右下角的系数)从而达到压缩数据的目的。早期的dct变换都使用了8x8的矩阵(变换系数为小数),如下左图所示。在h.264标准中新提出了一种4x4的矩阵,如下右图所示。这种4x4
dct变换的系数都是整数,一方面提高了运算的准确性,一方面也利于代码的优化。
dct模块的源码主要包括以下内容(均处于common/dct.c中),本文也主要分析以下的函数:
(1)、x264_dct_init()函数:初始化dct变换和dct反变换相关的汇编函数;
(2)、sub4x4_dct()函数:将两块4x4的图像相减求残差后,进行dct变换;
(3)、add4x4_idct()函数:将残差数据进行dct反变换,并将变换后得到的残差像素数据叠加到预测数据上;
(4)、sub8x8_dct()函数:将两块8x8的图像相减求残差后,进行4x4dct变换;
(5)、sub16x16_dct()函数:将两块16x16的图像相减求残差后,进行4x4dct变换;
(6)、dct4x4dc()函数:将输入的4x4图像块进行hadamard变换。
初始化函数x264_dct_init()是对x264_dct_function_t结构体中的函数指针进行了赋值。x264运行的过程中只要调用x264_dct_function_t的函数指针就可以完成相应的功能,主要是用于初始化dct变换和dct反变换相关的汇编函数,处于common/dct.c。
对应的函数调用关系图如下:
对应的代码分析如下:
从源代码可以看出,x264_dct_init()初始化了一系列的dct变换的函数,这些dct函数名称有如下规律:
(1)、dct函数名称前面有“sub”,代表对两块像素相减得到残差之后,再进行dct变换。
(2)、dct反变换函数名称前面有“add”,代表将dct反变换之后的残差数据叠加到预测数据上。
(3)、以“dct8”为结尾的函数使用了8x8dct,其余函数是用的都是4x4dct。
x264_dct_init()的输入参数x264_dct_function_t是一个结构体,其中包含了各种dct函数的接口。x264_dct_function_t的定义如下代码:
本节分析4*4dct变换函数sub4x4_dct()和4*4dct反变换函数add4x4_idct(),它们均处于common/dct.c中。
4*4dct变换函数sub4x4_dct()完成的功能是:将两块4x4的图像相减求残差后,进行dct变换,从源代码可以看出,sub4x4_dct()首先调用pixel_sub_wxh()求出两个输入图像块的残差,然后使用蝶形快速算法计算残差图像的dct系数。对应的代码分析如下:
求残差的代码如下:
4*4dct反变换函数add4x4_idct()完成的功能是:首先采用快速蝶形算法对dct系数进行dct反变换后得到残差像素数据,然后再将残差数据叠加到p_dst指向的像素上。需要注意这里是“叠加”而不是“赋值”。对应的代码分析如下:
sub8x8_dct()可以将两块8x8的图像相减求残差后,进行4x4dct变换,从源代码可以看出,
sub8x8_dct()将8x8的图像块分成4个4x4的图像块,分别调用了sub4x4_dct()。该函数的定义位于common\dct.c,对应的代码分析如下:
sub16x16_dct()可以将两块16x16的图像相减求残差后,进行4x4dct变换。该函数的定义位于common\dct.c,从源代码可以看出,
sub8x8_dct()将16x16的图像块分成4个8x8的图像块,分别调用了sub8x8_dct()。而sub8x8_dct()实际上又调用了4次sub4x4_dct()。所以可以得知,不论sub16x16_dct(),sub8x8_dct()还是sub4x4_dct(),本质都是进行4x4dct。对应的代码分析如下:
dct4x4dc()可以将输入的4x4图像块进行hadamard变换。该函数的定义位于common\dct.c,从源代码可以看出,dct4x4dc()实现了hadamard快速蝶形算法,对应的代码分析如下:
x264的变换编码的主要函数基本都在这儿,下一篇文章将分析宏块编码函数中的量化编码。