Python图像处理教程(Pillow)3位图图像数据
位图图像数据
位图图像由二维的像素阵列组成,每个像素都有自己的颜色。该图像的位图数据由颜色值数组组成,每个像素一个。
3.1 数据布局
考虑一个非常小的图像(比如一个8×8像素的图标),总共有64个像素。我们可以把它想象成这样。
然而,计算机内存是一维的,所以像素数据必须按顺序存储。通常情况下,数据是按扫描线顺序存储的--即第一行,接着是第二行,以此类推。
这并不是唯一可能的顺序,但它是非常普遍的。数据存储第一行的8个像素的颜色信息,然后是第二行的8个像素的颜色信息,等等。
当位图数据被存储在图像文件中时,它通常使用类似的格式。
3.2 每通道8位的图像
大多数位图都是使用每像素每颜色8比特(即1字节)来存储的。这适用于各种颜色模型。
3.2.1 24位RGB
对于RGB图像,最常见的数据存储方式是每个像素每色使用1字节(或8比特)。这意味着每个像素占用3个字节(或24比特),像这样。
在这种情况下,第一个字节代表像素的红色成分,第二个字节代表绿色,第三个字节代表蓝色。连续的像素被一个接一个地储存在内存中,像这样。
这是一种非常方便的格式,原因有二。
一个字节为每种颜色提供256个可能的亮度等级。正如我们前面所看到的,这个级别的数量足以提供良好的图像质量。
将每个像素通道放在单独的字节中,意味着它可以直接从内存缓冲区中读/写,而不需要重新排列位序(不像后面描述的其他格式)。
我们假设的颜色顺序是红、绿、蓝。这是最常见的排序,但有些格式可能使用不同的排序,例如蓝、绿、红。你应该检查你所使用的数据的具体格式,以确保你有正确的顺序。这种考虑同样适用于这里描述的所有格式。
3.2.2 32位CMYK
类似的格式可以用来存储CMYK位图数据。这次每个像素需要4个字节(32位)来容纳青色、品色、黄色和黑色成分。
3.2.3 8位灰度
对于灰度数据,只有一个通道,所以每个像素占用一个字节。
3.2.4 32位RGBA
RGB图像有时会用一个透明的alpha通道来存储。这意味着每个像素的透明度都可以被控制。它可以是
- 完全不透明,也就是完全隐藏在它后面的东西。
- 完全透明,所以像素是完全看不见的。
- 部分透明,这样后面的像素就部分可见。
有256个级别的透明度可供选择。
3.3 较少层次的位图数据
当个人计算机从20世纪70年代末开始流行时,计算机内存非常昂贵,特别是视频硬件所需的快速内存。此外,磁盘驱动器的容量要低得多,而网络的速度一般也慢得多。
为了使个人电脑能够负担得起,大多数早期的视频系统每像素只使用1字节或2字节,这意味着它们不能显示那么多颜色。
如今,当内存、大磁盘和快速网络都很容易获得时,这就不再是一个考虑因素。然而,我们今天仍在使用的一些图像文件格式支持这些模式。
3.3.1 8位RGB
在8位RGB中,整个RGB颜色被存储在一个字节中。可能的方案是这样的。
在这里,每种颜色都被存储为一个2位的数量。这意味着每种颜色只有4个可能的等级。
- 0对应的是0%。
- 1对应于33.3%
- 2对应于66.7%
- 3对应100%
除了画质低,你需要进行位操作来分离红色、绿色和蓝色成分。支持这种编码的视频硬件通常有对这些操作的硬件支持,这使得电路板的设计稍微复杂一些。
这种方案只使用每个字节中可用的8位中的6位。有时,额外的比特被用来表示其他品质,如透明度,或反色,甚至是闪烁的像素。有时还使用不同的编码方案,像这样。
在这种情况下,红色和绿色通道用3位而不是2位进行编码,因此有8个可能的红色和绿色级别。蓝色仍有2位,因为一个字节中只有8位可用。之所以这样做是因为眼睛对蓝色的敏感度低于其他颜色。
这种方法使图像质量稍好,但仍然不是特别好。
3.3.2 16位RGB
另一种编码方案使用2个字节(16比特)来存储RGB值。这允许每个通道有5比特,像这样。
5比特给了每种颜色32个不同的等级,这给了质量明显的改善。
这个结果要好得多,尽管在颜色变化缓慢的区域仍有一些带状现象(如天空,看起来有点块状)。它使用的存储量是8位的两倍,但仍比完整的24位RGB少用三分之一。
每种颜色使用5比特意味着仍有一个未使用的比特。与8位RGB的情况一样,一些方案将这一额外的位用于特殊意义(如透明度)。使用6比特的绿色、5比特的红色和5比特的蓝色也很常见。绿色得到额外的位,是因为我们的眼睛对光谱中绿色部分的颜色变化更敏感。额外的位会带来非常小的改善。
3.3.3 抖动
8位和16位RGB图像的一个问题是,由于缺乏可用的颜色,在图像中颜色平淡且变化缓慢的部分会出现 "带状 "现象。例如,天空可能会在微妙的不同蓝色之间逐渐变化,但在16位图像中,你会看到大面积的完全相同的蓝色,然后突然变成完全不同的蓝色。
抖动试图通过在原始图像中混合一些随机噪声来解决这个问题。这意味着,在图像的任何部分,像素原来是相同的值,现在像素的值都略有不同,但它们的平均值是原来的值。
当我们减少颜色深度时,不是所有的像素都被放在同一个波段,而是有些像素被放在不同的波段。你的眼睛会倾向于平均这种变化,你会看到类似于平滑变化的东西(但你也会看到一点噪音)。
在这张图片中,左边显示的是每色8比特的原始平滑绿色渐变。中间部分显示的是同样的梯度被减少到每色5比特的情况--带状现象非常清晰可见。右边的部分显示了同样的东西,但加入了噪声。
右侧图像中的每个像素都有一个与中间图像相同的颜色。但由于像素被混在一起,似乎有额外的颜色。
虽然在色带方面,右边的图像显然比中间的图像好,但噪音却相当令人不快,所以左边的图像(每个像素全8位)是最好的,正如你所期望的那样。
大多数现代图像格式都使用压缩。噪声往往会降低压缩的效果,因此,具有讽刺意味的是,使用每像素8比特加上抖动往往会导致文件大小比仅仅使用每像素24比特更大。因此,你会得到两个世界中最坏的结果,一个质量较差的图像和一个较大的文件。
3.4 双层图像
有一些类型的图像不需要颜色,甚至不需要灰色的色调。比如说。
- 白色背景上的黑色文字。
- 白色背景上的黑线图。
在这些情况下,图像中的每个像素都是黑色或白色。这些图像有时被称为二层图像,因为像素只有两种可能的层次或状态。
我们可以用一个比特来表示每个像素,这意味着我们可以在一个字节的数据中存储8个像素。
双层图像本身就比其他类型的图像小得多(每个像素有一个比特,而不是RGB图像的24比特)。还有一些特殊的压缩方法,只对双水平数据起作用,取得了非常好的效果。
使用双级图像的系统的例子有。
- 老式的传真机,用于通过模拟电话线在两个地点之间发送文本文件(现在在博物馆外很少见到)。
- 高速激光打印机,只用于打印黑色文本。
- 传统的印刷机(胶印机),仍然用于印刷大量相同的文件(如报纸)。
我们不会过多地讨论这些类型的图像。它们是相当专业的,而且这里描述的大多数技术对双级图像都不太适用。
3.5 具有更多层次的位图数据
现代数码相机通常对每个分量采集10到12比特(有些相机可以达到14比特)。10比特对应于1024个层次,14比特对应于16384个层次。
为了正常使用,这些数据被减少到每个分量的8比特,并以JPEG格式保存在相机本身中。毕竟,眼睛无法区分256个不同的层次,更不用说16,384个。
然而,如果你想编辑你的照片,特别是如果你想调整亮度和对比度,额外的级别可以派上用场。例如,如果你拍了一张照片,但其中一部分太暗。可能有一些额外的细节被相机捕捉到了,但在8位图像中无法看到。使用14位的图像,你也许可以提高这些区域的亮度,以显示细节,然后可以将其存储为8位图像。
这种数据通常以下列格式存储。
- 每组10比特,共30比特,存储为4个字节。
- 每组16比特,共48比特,存储为6个字节。12位或14位的数据也可以用这种格式存储。
3.6 基于调色板的图像
基于调色板的图像与8位RGB图像相似,但它们采取的方法略有不同,可以得到更好的质量。
基于调色板的图像通常每像素使用8比特。每个像素都包含介于0和255之间的数字,它为代表该像素颜色的色表提供索引。
在这种情况下,我们的图像是一个简单的8×8像素的图标。该图像只有5种不同的颜色,它们被储存在单独的表格中(调色板)。
对于数值为0的像素,我们取调色板的第0元素(第一个元素),也就是白色。
对于数值为1的像素,我们取调色板中的第1个元素(第二个元素),这是一种红色。以此类推。
尽管每个像素被存储为一个字节,调色板表中的颜色是一个24位的RGB值。因此,举例来说,我们用于索引为1的像素的红色可以是我们想要的确切的红色阴影。
3.6.1 超过256色的图像
只要图像有256种或更少的不同颜色,每种不同的颜色都可以在调色板表中有自己的条目,所以我们可以给每个像素提供准确的颜色。
这对于拥有有限数量不同颜色的标志、图表等来说非常有效。但是,如果我们试图将一张照片存储为基于调色板的图像,会怎么样呢?
嗯,一张照片很可能包含超过256种不同的颜色,所以调色板不能代表所有可能的颜色。我们的图像将不得不使用调色板中最接近的可用颜色。有两种不同的方法来做到这一点。
第一种方法是使用固定的调色板。在这种情况下,调色板包含一系列不同的颜色。这套颜色是固定的,这意味着无论图像包含什么,这套颜色都是一样的。这与8位RGB图像的情况很相似。如果一个像素的颜色没有出现在调色板中,我们只需选择调色板中出现的最接近的颜色,并使用它。
第二种方法是使用自适应调色板。在图像被存储之前,成像软件会分析图像,看哪些颜色被实际使用。然后它选择256种最能代表图像的颜色。
因此,举例来说,想象一下你有一张绿草如茵的山丘和明亮的蓝色天空的照片。如果你使用固定调色板保存,你将只有有限的浅蓝色和绿色可以在图像中使用。标准调色板还将包括明亮的红色、橙色、紫色和许多深色,这些颜色在图像中根本不存在。
如果你使用自适应调色板,你可以将256种可用颜色中的大部分主要用于浅绿色和蓝色,所以你可以有两倍于图像使用的不同颜色,而完全没有红色、橙色或紫色。你可能仍然无法为每个像素挑选准确的颜色,但你将能够选择更接近的颜色,所以整体图像质量会更好。
3.7 处理透明度
透明度也适用于图像。如果我们把图像的某些像素变成透明的,那就可以让背景显示出来,所以我们可以实现这样的效果。
当然,图像本身并不是真的透明。只是某些像素被标记为透明。由渲染该图像的软件来决定是显示该图像还是背景。例如,如果网站上有透明的图像,那就是由网络浏览器来实现透明。
图像提供透明度有三种不同的方式。
- 使用阿尔法通道。
- 使用透明的调色板条目。
- 将特定的颜色定义为透明的。
PNG格式,可能是用来支持透明度的最流行的格式,使用额外的阿尔法通道。
3.7.1 阿尔法通道
实现透明度的一种方法是使用额外的颜色通道。例如,我们不使用RGB图像,而是使用RGBA图像(A代表alpha,也就是透明)。
这样就可以控制每个像素的透明程度。比如说。
- alpha值为255时,像素是不透明的。
- α值为0使像素完全透明。
- alpha值为128时,像素为半透明,所以最终的颜色是图像像素和背景的混合。
这给了我们256级的透明度。这样做的好处是,我们可以在前景图像的边缘使用抗锯齿,使边界像素部分透明。这使得边缘看起来漂亮而平滑。
这种技术的缺点是,RGB图像现在有4个通道,而不是3个,所以它的体积比较大(尽管图像压缩可以减少这种影响)。
3.7.2 透明的调色板条目
如果我们有一个基于调色板的图像,另一种可能性是将特定的调色板条目定义为透明。例如,我们可以将调色板条目0定义为透明指标。
当图像被渲染时,任何索引为0的像素都不会被绘制,背景将被允许显示出来。
这种技术的优点是它不会增加图像的大小。缺点是像素可以是完全透明的,也可以是完全不透明的,所以不可能使透明图像的边缘平滑。另外,该技术只适用于基于调色板的图像格式。
GIF格式使用这种技术。
3.7.3 透明色
有时使用的最后一种技术是采用正常的RGB格式,但将某种特定的颜色定义为透明的。
例如,我们可以将RGB颜色(1,1,1)定义为透明。因此,如果我们需要一个像素是透明的,我们只需将颜色设置为(1,1,1)。
这就出现了一个小问题--如果我们有一个正常的像素,它并不打算是透明的,但恰好有(1, 1, 1)的颜色,怎么办?那么,这个颜色是非常非常深的灰色,几乎和黑色没有区别,所以我们可以直接将颜色设置为(0,0,0)。这不会产生任何可见的区别。
这并不是任何流行的图像格式所使用的技术,它主要用于专有图像格式。
3.8 隔行扫描和备用像素排序
上面所有的例子都假设位图数据是按扫描线顺序存储的(图像第一行的所有像素,接着是图像第二行的所有像素,以此类推)。这就是通常的情况。
然而,有些格式可以选择交错数据,这意味着以不同的顺序存储像素。这样做的好处是,在所有数据可用之前,有可能显示图像的低分辨率版本。因此,举例来说,如果你在一个慢速连接上浏览一个网页,而该网页包含一个大的图像,浏览器可以在下载完成之前显示一个低质量的完整图像版本。然后,当最终图像到达时,它可以用最终图像取代低质量的图像。
像素按以下顺序存储。
- 首先,按照扫描线的顺序,每隔一行的第二个像素被存储。
- 其次,所有剩余的像素被存储,同样是按照扫描线的顺序。
每个像素只存储一次,但由于它们是以不同的顺序存储的,这意味着只要第一组像素可用(当图像只下载了25%),就有可能显示最终图像的合理(略微模糊)的表现。
一些交织方案涉及两个以上的阶段。例如,PNG使用的Adam7算法有7个阶段。第一阶段存储每8行的第8个像素,这意味着在下载了64分之一的图像数据后,就可以显示出完整图像的块状版本。然后,图像在另外6个阶段得到改善,直到显示完整的图像。
一些格式(例如TIFF)允许像素数据以不同的顺序存储。例如,TIFF文件可以被排序为一组瓦片,其中每个瓦片是完整图像的一个矩形区域。这允许成像软件独立加载图像的每个部分。这是因为TIFF图像通常非常大,在TIFF的早期,大多数计算机没有足够的内存来一次加载整个图像。这在现代计算机中往往不是一个问题。
参考资料
- 本文涉及的python测试开发库 谢谢点赞!
- 本文相关海量书籍下载