视频编码中最重要的编码工具之一是变换编码,变换编码是指把视频内容从一个域变换到另外一个域,从而实现能量的集中。变换后,同样的内容往往可以用更少的比特来表示。
在视频编码中,变换一般是放在最前面的,后面接着量化和熵编码,它们的关系如图13所示。
图13分块、变换、量化、熵编码关系图
先把图像分成n×n的小块,一般是8×8的小块,对每个小块做离散余弦变换(Discrete Cosine Transform,简称DCT),然后对DCT后的值做量化,最后编码这些量化后的值。
什么是从一个域变换到另外一个域呢?可以这样来理解,举个简单的例子:在图14中,平面中有2个向量和。
图14 域变换示意图1
如果把坐标轴旋转90度变成(1,1)和(-1,1)方向,那么这两个向量的值就变成了(1,0)和(0,1),如图15所示。
图15 域变换示意图2
可见,针对内容,找到合适的坐标轴(也可以叫坐标基向量,简称坐标基),可以大大节省表示内容所需要的比特。这种坐标基的改变,就可以理解为从一个域变换到另一个域。
学过数字信号处理的读者都知道,离散傅里叶变换可以把时域上的离散信号变换到频域,用各个频率的分量的和来表示,如公式(1.1)所示:
同样,在视频编码中,用2维离散余弦变换,可以把一个视频空间信号用各个频域的分量和来表示。DCT的本质可以理解为坐标基的改变,把原来的(1,0,0,..,0),(0,1,0,...,0) . . . (0,0,...,0,1)坐标基向量表示的内容用DCT坐标基向量来表示。可以用以下代码生成DCT基向量和图像:
def makeDCT(N):
C = np.zeros((N,N))
C[0,:]=1*np.sqrt(1/N)
for i in range(1,N):
for j in range(N):
C[i,j]=np.sqrt(2/N)*np.cos(np.pi*i*(2*j+1)/(2*N))
for i in range(N):
plt.subplot(N//4,N//(N//4),i+1)
plt.plot(range(N),C[i,:])
plt.show()
return C
当N=8时,DCT的8个坐标基向量的图像如图16所示。
图16 DCT坐标基向量图像
为什么可以称这8个向量为基向量呢?因为它们都是单位向量,并且互相正交。
图17是2维DCT向量的示意图,所谓“2维”,一般是先对每一行做一次DCT,再对每一列做一次DCT。
图17 2维DCT向量的示意图
对8×8的块做二维DCT,可以理解为用这个块与图17中的每一个块做内积。计算出的值越大,说明当前块与此基向量的相似度越大。
所以,对图像做DCT的过程,一般被称为从空间域到频率域(或者DCT域)的变换。这种变换有三个好处:
- 对于自然图像,DCT后的频域值,靠近左上角的值较大(低频系数),靠近右下角的值较小(高频系数)。能量主要集中在低频区域。
- 由于人眼对高频区域信号不敏感,所以高频信号可以使用更低的精度保存。
- DCT后的频域值,通过使用DCT的逆运算IDCT,可以复原出原始的空间域的图像。
有兴趣的读者可以从K-L变换(Karhunen-Loeve Transform)的角度研究DCT的适用性。
图18和图19展示了对一幅灰度图像做DCT的过程,变换后的频域图像见图110。
把图像分成8×8的小块,见图19。
对每个小块分别做DCT,然后把每个块的值归一化到区间[0,255],用灰度图的方式显示,结果如图110所示。
图110中,值越大就越白,值越小就越黑。所有的8×8的小块的左上角都是最白的,在大部分块中,白色区域在第一行,第一列或左上角。很多块除了左上角,其他地方几乎是全黑的。
这种把图像分成8×8的块,每块做DCT的方式,被早期的图像编码标准JPEG(Joint Photographic Experts Group)和视频编码标准H263、MPEG2、MPEG4所使用。
除了DCT,随着视频编码技术的发展,其他的变换方法也被引入。例如:H265中引入了离散正弦变换(Discrete Sine Transform,DST)。DST变换系数的公式如(1.2)所示:
以下代码可以生成N=4的DST基向量(简称DST4)和图像:
def makeDST(N):
C = np.zeros((N,N))
for i in range(N):
for j in range(N):
C[i,j]=np.sqrt(4/(2*N+1))*np.sin(np.pi*(2*i+1)*(j+1)/(2*N+1))
if True:
for i in range(N):
plt.subplot(N//4,N//(N//4),i+1)
plt.plot(range(N),C[i,:])
plt.show()
return C
输出图像如图111所示。
用DST变换矩阵乘自己的转置矩阵可以得到单位矩阵,因此, DST和DCT一样,行向量都是单位向量并相互正交。在H265中,当帧内块预测使用水平或垂直预测模式时,对预测残差使用DST变换。
H265中使用的这个DST变换有一个小的问题,就是它不能像DCT一样做蝶形分解。因为它的变换系数公式中的分母为2N+1,无法分解,也就不能做快速算法。对4×4的小块,因为有单指令多数据(Single Instruction Multiple Data,SIMD)的加持,蝶形分解的快速运算的优势没有那么明显,但是对8×8或16×16甚至更大的块,直接运算的劣势就非常明显了。
于是,在AV1标准中,在DST的基础上,引入了非对称离散正弦变换( Asymmetric Discrete Sine Transform,ADST)和翻转非对称离散正弦变换(FLIPADST)。ADST的变换系数公式如(1.3)所示:
N=8时,ADST的基向量(简称ADST8)图像如图112所示。
ADST的优点就是它可以做蝶形分解快速运算,因此,可以应用在较大的块上。
另外,AV1除了有DCT、ADST和FLIPADST外,还有一种变换:恒等变换(Identity Transform,IDTX),也就是不做任何变换。因为在实际编码中,有的块在空间上并不平稳,做了变换还不如不做变换。
基于块的变换编码对局域相关性很大的平稳区域,有很好的压缩性能,对自然图像具有编解码速度快,压缩性能高的特点。在近50年的视频编码的发展过程中,基于块的变换编码一直占据着主流位置。JPEG2000是一个很少的例外,它使用小波变换,不再使用固定的分块,彻底摒弃了基于块的离散傅里叶(Discrete Fourier Transform,DFT)家族的变换。而且天生地支持分层编码,渐进传输,但该编码到现在也没有普及。
DFT家族的变换在音频编码中也同样重要,音乐压缩格式MP3,使用了时域重叠的DCT(MP3标准中称为MDCT)。具有更高压缩效率的AAC格式,使用了DFT。因为对声音,DFT变换同样有着集中能量的作用,再加上人耳在频率上的掩蔽效应,时频转换的意义就更大了。
DCT可以把能量聚集起来,但也有不适用DCT的情况,如图113所示。
图113中,左图右上角黑色、左下角白色的像素块在进行DCT后,却变得更加复杂,能量也更加分散。因为自然图像大多是渐变的,没有这种锋利的边缘,所以对于自然图像来说,DCT大多是适用的。对左边这种像素块,有方向的帧内预测是很好的解决办法。