JPEG - 想法与实践/颜色成分
在计算机中,颜色由其三种原色红、绿和蓝的组成来确定,它们的比例以字节来衡量,即从 0 到 255 的整数。因此,一种颜色对应于一个三字节组,称为RGB 三元组。图片是 RGB 三元组的矩形矩阵。如果图片的宽度为 w,高度为 h,则颜色值(RGB 三元组)由 (i, j) 对索引,i = 0, ..., w-1, j = 0, ..., h-1,使得左上角的坐标集为 (0, 0)(即,纵坐标向下测量)。图片占用 3wh 字节,可以通过将由 3w 字节组成的 h 条水平线依次存储在内存块中来存储。通过将内存块(直接)传输到屏幕上以显示图片的过程称为位图。
(在Windows 的位图过程中,要求水平线上字节的数量必须能被 4 整除,这意味着内存块中的线段可能需要增加 1、2 或 3 个字节,通常用零填充。)
图片可以永久存储在一个文件中,该文件由以这种方式排列的数据字节组成,并附带一个标头,用于指定文件类型和图片的尺寸。对于Windows 的BMP 文件格式(BMP = 位图图片)来说,就是如此。BMP 文件以一个 54 字节的标头开始。由于 BMP 文件中的数据与绘制位图所使用的方式完全一致,因此可以直接从读取文件中绘制图片 - 而不涉及 RAM 内存,也不使用除基本算术运算之外的任何其他运算。
(BMP 文件的标头分为 17 个块,每个块由一个、两个或四个字节组成。两个字节确定一个从 0 到 2562 - 1 = 65535 的整数,称为字,四个字节确定一个从 0 到 2564 - 1 = 4294967295 的整数,称为双字。BMP 标头的头两个块是字节 66 和 77,与字符 'B' 和 'M' 相对应,指定文件类型。块 8 和 9 是双字,分别表示宽度和高度,块 10 和 11 是字,通常分别设置为 1 和 24(= 每种颜色的位数),块 7 是一个双字,通常设置为 40。除块 4 和 5(它们是字)之外的其他块都是双字,所有这些块都可以设置为 0,因为它们通常不会被读取文件的程序读取。)
BMP 文件格式和要传输到屏幕上的位图内存块对于计算机和程序员来说都是简单的任务,但这些存储图片的方法占用的内存空间很大:一张 1000x750 像素的图片占用 3x1000x750 = 2.2 Mb。这在图片的制作过程中或存储相对较少的图片(需要尽可能高的质量)时可以暂时接受,但对于包含数百张图片的文件夹或电影或来自互联网的传输来说,如此大的空间是不可接受的。人们会立即想到,不可能让数字化的数据占用更小的空间,因为比特组成的材料无法像照片底片那样缩小。但是,数字化数据集由比特序列组成,这些序列可以被更短的序列替换 - 如果存在重复,则重复的部分可以被充当其类型和重复次数的符号的序列替换。如果数据是某个固定集合(例如数字)的元素的副本,那么我们可以为集合的元素分配比特序列,使得使用最频繁的元素分配最短的序列。此外,如果数据集的元素是大小变化很大的数字,那么我们可以尝试删除数字之间的空白,而不是为每个数字分配相同的空间。当然,这不能随便进行,因为(由于没有第三个比特)我们需要一个工具来分离对应于数字的比特序列。然而,我们可以插入充当代码的比特序列。
只有非负整数可以直接数字化,方法是写出它的二进制数字表达式
- n = cm2m + ... + c222 + c12 + c0
其中 c0, ..., cm 是比特:0 或 1 - 我们对序列进行排序,使得最高有效位排在最前面。如果数字是理数或实数,我们必须以某种方式将其表示为两个非负整数的组合。要插入的代码可以选择与自然数一一对应,并且使得分配给代码的自然数是后续非负整数的位数。代码必须选择得当,使得最常用的自然数(表示位数)具有最短的代码,并且还能让我们确定代码何时结束。
当数据需要使用时(例如要显示图片),压缩数据集会经过解码过程,留下与原始数据集完全相同的数据集。几乎所有图像文件格式都支持以这种方式压缩数据。这种技巧当然也用于 JPEG 过程,但在这个过程中,数据在压缩之前会先进行修改:首先转换颜色值,然后通过将新的值除以某些数字并进行舍入来减少新的值。最后一个过程称为量化,它可能会引入(很小的)偏差。
基础颜色是纯色,它们是饱和度最高的“最强”颜色。纯色构成一个循环色标
-
纯色
因此,纯色由一个角度决定。任何与灰度色不同的颜色都是混合了唯一确定的纯色和灰度色而得到的。纯色的亮度并不相同:其中三种的亮度低于其他颜色,它们是原色:纯红、纯绿和纯蓝,分别对应于 0 度、120 度和 -120 度。任何不是原色的纯色都位于两种原色之间,是由其中最接近的原色与另一部分的混合而得到的。如果我们将三种原色混合在一起,就会得到白色 - 亮度最高的颜色。由此我们可以看到,每种颜色都是通过混合三种原色得到的,每种原色都或多或少地变暗。这就是 RGB 表示法。我们通常以字节来衡量三种颜色的数量,这样 255 就对应于原色,而 0 就对应于黑色。
(我们可以通过以下方法找到与颜色 C(与灰度色不同的颜色)相关的纯色:通过从白色中减去 C 的 RGB 值,我们得到颜色 C1,其 RGB 三元组为 (255-R, 255-G, 255-B)。如果我们假设蓝色在该颜色中所占的比例最大,则 C1 = β C2,其中 β <= 1,而颜色 C2 是蓝色所占的比例为 255 的颜色。通过从白色中减去 C2,我们得到颜色 C3,如果我们假设红色在该颜色中所占的比例最大,则 C3 = α C4,其中 α <= 1,而纯色 C4 是红色所占的比例为 255,蓝色所占的比例为 0 的颜色。这是与 C 相关的纯色,我们可以通过根据 α 与黑色混合,根据 β 与白色混合来得到 C。)
然而,RGB 颜色表示法有一个缺点:三个值具有同等的重要性。我们更希望有一种三元组表示法,其中一个值(第一个值)比另外两个值更重要,因为这样,在量化过程中,我们就可以允许另外两个不太重要的分量有更大的偏差。这种表示法很容易想象,如下面的四张图片所示:我们可以让三元组中的第一个值为三个 RGB 值的平均值,从而表示颜色的强度(并给出相应的灰度图片),而让另外两个值构成“颜色增量”。我们将颜色(RGB 三元组)想象成一个边长为 256 的立方体的整数点,该立方体以三个正坐标轴为边,原点位于对应于黑色的角落。在这个立方体中,灰度色位于对角线上,我们将对角线作为第一个轴。我们可以让另外两个坐标轴垂直于对角线且相互垂直,但为了得到一个简单的变换,我们让它们位于 B-G 平面和 R-G 平面。注意,新的坐标系意味着最后两个颜色值可以为负数。我们选择单位使得第一个坐标用字节来衡量,另外两个坐标用带符号字节来衡量:从 -128 到 127 的整数。新的坐标三元组通过线性变换与 RGB 三元组相关联。
我们称这种新的表示为颜色的 YCbCr 值。Y 代表亮度(或亮度),C 代表色度:Cb 代表蓝色色度,Cr 代表红色色度。我们的假设意味着存在参数 kb 和 kr,使得线性变换及其逆变换由下式给出
- Y = kr∙R + (1 - kr - kb)∙G + kb∙B
- Cb = ½(B - Y)/(1 - kb)
- Cr = ½(R - Y)/(1 - kr)
- R = Y + 2(1 - kr)∙Cr
- G = Y - (kb∙(B - Y) + kr∙(R - Y))/(1 - kb - kr)
- B = Y + 2(1 - kb)∙Cb
我们可以看到,如果一种颜色是灰度颜色,也就是说,如果 R = G = B,那么 Y 就是这个数字,而 Cb 和 Cr 为零。在数学上,将 kb 和 kr 设置为 1/4 比较自然,因为变换将获得一个简单而自然的形式
- Y = R/4 + G/2 + B/4
- Cb = -R/6 - G/3 + B/2
- Cr = R/2 - G/3 - B/6
- R = Y + (3/2)Cr
- G = Y - (3/2)(Cb + Cr)/2
- B = Y + (3/2)Cb
然而,在 JPEG 实现中——我们在这里以它为指导——参数 kb 和 kr 被设置为 0.144 和 0.299,并且使用这些值,公式变为
- Y = 0.299∙R + 0.587∙G + 0.114∙B
- Cb = -0.168736∙R - 0.331264∙G + 0.5∙B
- Cr = 0.5∙R - 0.418688∙G - 0.081312∙B
- R = Y + 1.402∙Cr
- G = Y - 0.3441∙Cb - 0.71414∙Cr
- B = Y + 1.772∙Cb
这意味着坐标轴是:对角线、G-B 平面上的直线 (-0.34, 1.77) 和 R-G 平面上的直线 (1.40, -0.71)。由于两个色度坐标的范围在 [-128, 127] 区间内,我们必须在它们中添加 128 以获得字节,以便我们可以绘制图像在坐标轴上的“投影”。现在,我们不再是通过红、绿、蓝比例来组成图像,而是通过灰度比例、蓝绿比例和红绿比例来组成图像
-
原始图像
-
灰度分量
-
蓝绿分量
-
红绿分量
由于我们希望我们的数字(整数)尽可能小,因此我们从 Y 值中减去 128,以便它像 Cb 和 Cr 一样成为一个有符号字节。