JPEG - 想法与实践/头部部分
JPEG 文件的头部部分被划分为段,每个段以一个标记开始,标识该段。 通常,JPEG 文件包含 7 个不同的标记。标记是一个字节对,第一个字节是 255,第二个字节不同于 0 和 255。我们通过第二个字节来标识一个标记。有两个标记是独立的(因此不打开一个段):打开文件的标记 SOI(开始图像)= 216 和关闭文件的标记 EOI(结束图像)= 217。(还有另一种独立的标记,但它不在我们这里讨论的顺序 DCT 模式中使用:它标志着扫描的重启,并由 0、1、...、7 中的一个数字索引:RST0、...、RST7(重启)= 208、...、215)。其他标记打开一个段,在这种情况下,接下来的两个字节(b1,b2)表示段的长度(包括这两个字节):l = b1 * 256 + b2。接下来的 l - 2 个字节序列是该段的内容。以下是段的类型(用它们的标记标识)
- APP0、APP1、...、APP15(应用) 224-239
- COM(注释) 254
- SOF(开始帧) 192-207,除了 196、200 和 204
- DHT(定义霍夫曼表) 196
- DQT(定义量化表) 219
- SOS(开始扫描) 218
(以及其他几个在此处未使用的标记:DNL(定义行数 = 220)、DRI(定义重启间隔 = 221)、DHP(定义分层渐进 = 222)、EXP(扩展参考分量 = 223)、DAC(定义算术编码条件 = 204)、TEM(用于算术编码中的 TEMporary 使用 = 1)以及一些保留标记:JPG(保留用于 JPeG 扩展 = 200、240、241、...、253)和 RES(REServed = 2、...、191))
前两个 - APP 和 COM - 指定了在 JPEG 程序之外的内容。通常只存在一个 APP 段(即 APP0),它指定了实现。APP 段还可以包含有关相机类型和拍摄图片时间的信息。COM 可以说明用于创建文件的程序、选择的质量百分比等。
JPEG 程序的出发点是“图片”,图片可以定义为数字、数字对、数字三元组或数字四元组的(矩形)矩阵。也就是说,图片是具有 1-4 之间的一个数字的长度的数组的矩阵。灰度图片是字节矩阵。彩色图片是 RGB 三元组(字节)矩阵或 YCbCr 三元组(有符号字节)矩阵。因此,图片可以被认为是由一个或多个(最多四个)整数矩阵组成的,这样的矩阵被称为图片的分量。每个分量都分配一个分量标识符(字节):例如,灰度图片的(一个)分量为 0,彩色图片的三个分量为 0、1 和 2。
图片的尺寸、分量标识符和分量的顺序在帧段 SOF 中指定,以及如何处理分量之间的关系。由于颜色通常只在位置之间缓慢变化(而且我们不太擅长区分颜色的微小变化),对于两个颜色分量,我们可以例如将图片分成 2x2 像素的方块并取平均值,这样我们就可以将这个方块看作是一个像素,从而处理四倍小的彩色图片。我们也可以只限于两个像素,要么水平排列,要么垂直排列。每个分量的数字对 (Hi, Vi) 决定了如何扫描分量之间的关系。Hi 和 Vi 可以从 1 到 4(Hi 和 Vi 必须比较小:它们的乘积之和不能超过 10)。分别设 H 和 V 为最大的 Hi 和 Vi 值。这些最大值通常与 Y 分量相关联,而 ((Hi, Vi) = (H, V)) 意味着像素按原样取值:水平样本数与图片的宽度相同,水平行数与图片的高度相同。如果一个(颜色)分量的对为 (Hi, Vi),则水平线上的样本数是 (Hi/H) 乘以宽度,采样线的数量是 (Vi/V) 乘以高度,也就是说,收集了 (H/Hi)x(V/Vi) 像素的小矩形(并被认为是一个像素)。通常情况下,颜色分量的 (Hi, Vi) = (1, 1),而 Y 分量的 (Hi, Vi) = (1, 1) 或 (2, 1) 或 (1, 2) 或 (2, 2)。(Hi, Vi) = (2, 2) 意味着收集了四个颜色像素,并且“这个”像素与四个 Y 像素组合在一起。由于图片被划分为 8x8 的方块,这意味着 Y 分量的四个 8x8 方块与颜色分量的一个 8x8 方块组合在一起。四个 Y 方块的编码数据(编码的 64 数组)按通常的扫描顺序写入文件:从左到右沿行,从上到下。接下来是两个颜色分量的编码数据(编码的 64 数组)。当只收集两个像素(水平或垂直)时,类似的过程。由所有分量和收集的 8x8 方块产生的数据流的这一部分被称为最小编码单元(MCU)。
这张图片展示了当收集四个 Y 分量 8x8 方块时的绘制方式(像素对像素 - 并且按放大的比例) - 你要想象中间的四个 8x8 方块,两个(最上面的)已经绘制了,第三个正在绘制
-
绘制
以下图片(占用 3.2 Kb)下面的两张图片是这张图片,每隔一条垂直线都绘制为黑色,但以不同的方式扫描:对于颜色分量,分别在垂直和水平方向上收集两个像素(即颜色分量的 (Hi, Vi) = (1, 1),而 Y 分量的 (Hi, Vi) = (1, 2) 和 (2, 1))。在第一张图片(占用 5.9 Kb)中,颜色是正确的,在第二张图片(占用 4.7 Kb)中,颜色是褪色的,因为它们与线的黑色混合在一起
-
原始
-
垂直子采样
-
水平子采样
帧段 SOF 由以下字节组成:标记 (255, b),其中字节 b 指定扫描模式。我们假设这里 b = 192,表示基线顺序 DCT 模式;然后是表示段长度的字节对(包括这两个字节),这对字节为 (0, 8 + 3 * 分量数);然后是一个表示颜色值位数的字节,这里设置为 8(表示颜色值为字节),但在扩展模式中为 12;然后是一对字节 (b1, b2) 表示图片的高度 (= b1 * 256 + b2) 和一对字节表示宽度;最后是一个表示分量数(1-4)的字节,以及每个分量的以下字节:分量标识符(字节)、Hi(½ 字节)和 Vi(½ 字节)(字节 = Hi * 16 + Vi)以及量化表目标选择器(字节)。
这里,颜色分量的对 (Hi, Vi) 为 (1, 1),而 Y 分量的 (Hi, Vi) 为 (1, 1)、(1, 2)、(2, 1) 或 (2, 2)。量化表目标选择器是 0-3 之间的数字之一,例如,Y 分量为 0,颜色分量为 1。
通常,灰度图片的文件中会有两个霍夫曼表段,彩色图片中有四个:每个分量的 DC 和 AC 数字的编码方式不同,Y 分量和两个颜色分量的编码方式也不同。在霍夫曼段中,信息(在标记和表示长度的字节对之后)以这种方式排列:如果霍夫曼表用于 DC 数字,则第一个半字节为 0,如果用于 AC 数字,则为 1。下一个半字节是霍夫曼表目标标识符(0 或 1),例如,Y 分量为 0,颜色分量为 1(将在扫描段 SOS 中引用,在 SOS 中指定了霍夫曼表)。接下来的 16 个字节序列是列表 BITS,它表示 i = 1、...、16 时长度为 i 的代码数量。然后是列表 HUFFVAL,它表示霍夫曼值:对于每个非零的代码长度,将有与该长度的代码数量相同的数值。如果我们将霍夫曼值的数量称为 nhv,则段中的字节数(包括表示长度的字节对)为 19 + nhv。
量化表是一个 8x8 字节矩阵,按之字形原则排序。通常情况下,Y 分量和颜色分量有不同的量化表。在 T.81 的附录“示例和指南”中,你可以分别找到 Y 分量和颜色分量的以下内容
- 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
- 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
它指出“如果将这些量化值除以 2,则生成的重建图像通常与源图像几乎无法区分”。通过我们的程序“JPEG_File”,你可以看到一张图片的表格(使用顺序 DCT 程序,并且)指定了名称“pict”。在我们生成(真实的)JPEG 文件的程序中,我们为 Y 分量选择了另一个表格,而不是上面的表格,即在图像编辑程序(IrfanView)中使用,将质量设置为 70% 时使用的表格
- 10 7 6 10 14 24 31 37
- 7 8 11 16 35 36 33
- 8 8 10 14 24 34 41 34
- 10 13 17 31 52 48 37
- 11 13 22 34 41 65 62 46
- 14 21 33 38 49 62 68 55
- 29 38 47 52 62 73 72 61
- 43 55 57 59 67 60 62 59
量化表在 DQT 段中指定。DQT 段以标记 DQT = 219 和长度开头,长度为 (0, 67)。然后是一个字节,其前半部分为 0,表示该表由字节(8 位数字 - 对于扩展模式,为 1,表示该表由字,16 位数字)组成,后半部分为表的 *目标标识符* (0-3),例如,Y 分量为 0,颜色分量为 1。接下来是表的 64 个数字(字节)。
紧接扫描段 SOS 之后是图片的编码数据,扫描段指定了用于分量的哈夫曼表。该段以标记 SOS = 218 和长度开头,长度为 (0, 6 + 2 * 分量数)。然后是一个字节,表示分量数 (1-4),然后是每个分量的两个字节,第一个是分量标识符(在帧段中定义),第二个分为两部分,第一部分表示 DC 哈夫曼表的目标选择器,第二部分表示 AC 哈夫曼表的目标选择器(例如,Y 分量为 0,颜色分量为 1)。该段以三个字节结束,在我们的案例(顺序 DCT)中,它们是 0、63 和 0(最后一个分为两个半字节)。