影像算法解析——JPEG 压缩算法

由于视频是由一帧帧图像构成的,研究视频编码首先先要研究图像编码。这篇文章就详细说一下 JPEG 是如何压缩一个图像的。

先简单介绍一下 JPEG(Joint Photographic Experts Group,联合图像专家小组):此团队创立于1986年,其于1992年发布的 JPEG 标准在1994年获得了 ISO 10918-1 的认定,成为了图片压缩标准。

JPEG 压缩为有损压缩,下面介绍一下它的压缩算法。

1 RGB 到 YUV 进行4:2:0色度抽样

首先 JPEG 文件是由 YUV 的色彩空间来表示颜色的,YUV 中 Y 表示明亮度(Luma),U 和 V 表示色度(Chrominance)和浓度(Chroma),UV 分量同时表示色差。

因为人眼对亮度的差异敏感度高于对色彩的变化。考虑到这种感知能力,允许降低色度以及浓度的带宽。YUV 的色彩空间更容易压缩 U、V 分量。


1.1 RGB 到 YUV 的转换 - 无损

RGB 转化 YUV 的公式(经过 PAL制式 CRT伽玛校正)如下:

Y = 0.299R’ + 0.587G’ + 0.114B'

U = -0.147R’ - 0.289G’ + 0.436B'

V = 0.615R’ - 0.515G’ - 0.100B'


未经伽玛校正(计算机色彩空间)的矩阵表示:

 \begin{bmatrix} Y \\ U \\ V \end{bmatrix} = \begin{bmatrix} 0.299 & 0.587 & 0.114 \\ -0.169 & -0.331 & 0.5 \\ 0.5 & -0.419 & -0.081 \end{bmatrix} \begin{bmatrix} R \\ G \\ B \end{bmatrix} + \begin{bmatrix} 0 \\ 128 \\ 128 \end{bmatrix}


1.2 色度抽样-有损

在 YUV 采样的时候可以对 U、V 分量进行色度采样,在 JPEG 压缩算法采用的是 YUV 4:2:0 的色度抽样方法。

4:2:0 并非无 V 分量,它对于每行扫描的像素来说,只有一中色度分量以 2:1 的抽样率储存,相邻行储存不同的色度分量。可以理解为奇数行采样 U,YUV 4:2:0;偶数行采样 V,YUV 4:0:2。

YUV 4:2:0 的码流为:

Yo0 Uo0 Yo1 Yo2 Uo2 Yo3
Ye0 Ve0 Ye1 Ye2 Ve2 Ye3

映射成的四个像素为:

[Yo0 Uo0 Ve0] [Yo1 Uo0 Ve0] [Yo2 Uo2 Ve2] [Yo3 Uo2 Ve2]
[Ye0 Uo0 Ve0] [Ye1 Uo0 Ve0] [Ye2 Uo2 Ve2] [Ye3 Uo2 Ve2]

YUV 4:2:0 的色度抽样率为 4:1,至此经历第一次压缩。


2 离散余弦变化(DCT)- 无损

做好色彩空间转换和 YUV 4:2:0 色彩抽样后,将输入的图片分割成 8*8 像素的单元格。对每个单元格进行离散余弦变化。

以下面的图片作为例子,下图为 8*8 的只有 Y 分量的图形,其中亮度值为矩阵中的值:

数据来自 Wikipedia [1]


由于 DCT 变化需要定义域对称,在做 DCT 变化之前要先将矩阵中的数值左移 128,使得值域落在 -128~127:

{\begin{bmatrix}-76&-73&-67&-62&-58&-67&-64&-55\\-65&-69&-73&-38&-19&-43&-59&-56\\-66&-69&-60&-15&16&-24&-62&-55\\-65&-70&-57&-6&26&-22&-58&-59\\-61&-67&-60&-24&-2&-40&-60&-58\\-49&-63&-68&-58&-51&-60&-70&-53\\-43&-57&-64&-69&-73&-67&-63&-45\\-41&-49&-59&-60&-63&-52&-50&-34\end{bmatrix}}


到此,DCT 变化的准备工作已经完成。在进行 DCT 变化之前,先说一下 DCT 的原理

信号学中,如果一个信号长度为8字节,则可以用8个不同频率的余弦波去表示它,形成频域编码。在图像中也是如此。

在 JPEG 算法中,图像被分为了 8*8 的像素组,每个像素组用自己的离散余弦变化进行频域编码。

顺带说一句,为什么选用 8*8 的像素组。采用比 8*8 更大的像素组,会大幅增加 DCT 的运算量,且编码质量也不会明显提升;采用比 8*8 更小的像素组会导致分组增多降低精度。所以8*8 的像素组是效率最优的结果。

这些像素组可以被 8*8 个余弦波精确表示,如下图所示有64个基本余弦波:

余弦波


这64个余弦波,可以组合成任意 8*8 的图形。我们只要用系数(系数表示每个单独的余弦波对整体图像所做的贡献)对这64个余弦波进行加权,就可以表示出任何的图形。

这个过程就是离线余弦变化,JPEG 中使用的是 DCT II 的公式。

根据 DTC II 的公式:

X_k = \sum_{n=0}^{N-1} x_n \cos \left[\frac{\pi}{N} \left(n+\frac{1}{2}\right) k \right] \quad \quad k = 0, \dots, N-1.

可以得出该图像在频域上的系数(经过取整,取值范围 -1024~1023):

{\begin{bmatrix}52&55&61&66&70&61&64&73\\63&59&55&90&109&85&69&72\\62&59&68&113&144&104&66&73\\63&58&71&122&154&106&70&69\\67&61&68&104&126&88&68&70\\79&65&60&70&77&68&58&75\\85&71&64&59&55&61&65&83\\87&79&69&68&65&76&78&94\end{bmatrix}}DCT\Rightarrow{\begin{bmatrix}-415&-30&-61&27&56&-20&-2&0\\4&-22&-61&10&13&-7&-9&5\\-47&7&77&-25&-29&10&5&-6\\-49&12&34&-15&-10&6&2&2\\12&-7&-13&-4&-2&2&-3&3\\-8&3&2&-6&-2&1&4&2\\-1&0&0&-2&-1&-3&4&-1\\0&0&-1&-4&-1&0&1&2\end{bmatrix}}


经过 DCT 转换后的频率系数矩阵分别对应64个余弦波在 8*8 图形中的权重。可以看出,左上部分低频区的系数比较大,右下高频区的系数较小。鉴于人眼对高频区的识别不敏感,所以在下面量化部分可以舍弃一些高频区的数据。这里的 DCT 变化还没开始压缩。


  • 注1:左上角第一个数为直流系数,代表特定像素组的一般强度,其余为交流系数。
  • 注2:此处的系数矩阵与亮度矩阵无对应关系。


3 量化 - 有损

在 DCT 变化后,舍弃高频区数据的过程称为量化。有两份量化表可供选择,分别为亮度量化表和色度量化表:

Q_{Y}={\begin{bmatrix}16&11&10&16&24&40&51&61\\12&12&14&19&26&58&60&55\\14&13&16&24&40&57&69&56\\14&17&22&29&51&87&80&62\\18&22&37&56&68&109&103&77\\24&35&55&64&81&104&113&92\\49&64&78&87&103&121&120&101\\72&92&95&98&112&100&103&99\end{bmatrix}} Q_{C}={\begin{bmatrix}17&18&24&47&99&99&99&99\\18&21&26&66&99&99&99&99\\24&26&56&99&99&99&99&99\\47&66&99&99&99&99&99&99\\99&99&99&99&99&99&99&99\\99&99&99&99&99&99&99&99\\99&99&99&99&99&99&99&99\\99&99&99&99&99&99&99&99\end{bmatrix}}


上表分别为亮度量化表和色彩量化表,表示 50% 的图像质量。这两张表中的数据基于人眼对不同频率的敏感程度制定。

量化表是控制 JPEG 压缩比的关键,可以根据输出图片的质量来自定义量化表,通常自定义量化表与标准量化表呈比例关系,表中数字越大则质量越低,压缩率越高。PhotoShop 有12张量化表。

量化过程为,使用量化矩阵与前面得到的 DCT 矩阵逐项相除并取整。之前为亮度矩阵,顾使用亮度量化表:

{\begin{bmatrix}-415&-30&-61&27&56&-20&-2&0\\4&-22&-61&10&13&-7&-9&5\\-47&7&77&-25&-29&10&5&-6\\-49&12&34&-15&-10&6&2&2\\12&-7&-13&-4&-2&2&-3&3\\-8&3&2&-6&-2&1&4&2\\-1&0&0&-2&-1&-3&4&-1\\0&0&-1&-4&-1&0&1&2\end{bmatrix}} \oslash {\begin{bmatrix}16&11&10&16&24&40&51&61\\12&12&14&19&26&58&60&55\\14&13&16&24&40&57&69&56\\14&17&22&29&51&87&80&62\\18&22&37&56&68&109&103&77\\24&35&55&64&81&104&113&92\\49&64&78&87&103&121&120&101\\72&92&95&98&112&100&103&99\end{bmatrix}} ={\displaystyle {\begin{bmatrix}-26&-3&-6&2&2&-1&0&0\\0&-2&-4&1&1&0&0&0\\-3&1&5&-1&-1&0&0&0\\-3&1&2&-1&0&0&0&0\\1&0&0&0&0&0&0&0\\0&0&0&0&0&0&0&0\\0&0&0&0&0&0&0&0\\0&0&0&0&0&0&0&0\end{bmatrix}}}


量化是有损的,在解码时,反量化会乘回量化表的相应值。由于存在取整,低频段会有所损失,高频段的0字段则会被舍弃,最终导致图像质量降低。


4 熵编码(zigzag scan & 霍夫曼编码)- 无损

得到量化后的矩阵就要开始编码过程了,首先要把二维矩阵变为一维数组,这里采用了 zigzag 排列,将相似频率组在一起:

zigzag [2]

得出的序列为:


霍夫曼编码中,在 JPEG 有个 EOB(End Of Block) 字段,表示从字段开始后面全为0,然后再根据霍夫曼编码再进行压缩。

至此,这个亮度 8*8 的像素组压缩编码完毕。

将图像每个 8*8 像素组进行编码就可以压缩整个图像了。


关于 JPEG 编码的内容就写到这里,如有错误或遗漏,欢迎在评论区指正。

以后也会在这个新分类下写一些其他视频编码,如果大家有其他内容想了解,也可以写在评论里。

没有微信公众号,只在专栏里写些东西,顺便自己也可以学习,欢迎关注。


参考资料:

[1] JPEG-Wikipedia

[2] JPEG 标准 | itu-t81.pdf

编辑于 2018-07-27

文章被以下专栏收录