png/gif/jpeg/bmp图片格式的简易文档

PNG格式

  

   PNG是20世纪90年代中期开始开发的图像文件存储格式,其目的是企图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。流式网络图形格式(Portable Network Graphic Format,PNG)名称来源于非官方的“PNG's Not GIF”,是一种位图文件(bitmap file)存储格式,读成“ping”。PNG用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时,彩色图像的深度可多到48位,并且还可存储多到16位的α通道数据。PNG使用从LZ77派生的无损数据压缩算法。

         

PNG文件格式保留GIF文件格式的下列特性:

使用彩色查找表或者叫做调色板可支持256种颜色的彩色图像。

流式读/写性能(streamability):图像文件格式允许连续读出和写入图像数据,这个特性很适合于在通信过程中生成和显示图像。

逐次逼近显示(progressive display):这种特性可使在通信链路上传输图像文件的同时就在终端上显示图像,把整个轮廓显示出来之后逐步显示图像的细节,也就是先用低分辨率显示图像,然后逐步提高它的分辨率。

透明性(transparency):这个性能可使图像中某些部分不显示出来,用来创建一些有特色的图像。

辅助信息(ancillary information):这个特性可用来在图像文件中存储一些文本注释信息。

独立于计算机软硬件环境。

使用无损压缩。

          

PNG文件格式中要增加下列GIF文件格式所没有的特性:

每个像素为48位的真彩色图像。

每个像素为16位的灰度图像。

可为灰度图和真彩色图添加α通道。

添加图像的γ信息。

使用循环冗余码(cyclic redundancy code,CRC)检测损害的文件。

加快图像显示的逐次逼近显示方式。

标准的读/写工具包。

可在一个文件中存储多幅图像。

          

文件结构

PNG图像格式文件(或者称为数据流)由一个8字节的PNG文件署名(PNG file signature)域和按照特定结构组织的3个以上的数据块(chunk)组成。

PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。关键数据块定义了4个标准数据块,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。虽然PNG文件规范没有要求PNG编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。

(1) PNG文件署名域

8字节的PNG文件署名域用来识别该文件是不是PNG文件。该域的值是:

十进制数13780787113102610
十六进制数89504e470d0a1a0a

 

(2) 数据块的结构

每个数据块都由表6-07所示的的4个域组成。

 

表6-07 PNG文件数据块的结构

 

名称

 

字节数

 

说明

Length(长度)4字节指定数据块中数据域的长度,其长度不超过

 

(231-1)字节

Chunk Type Code(数据块类型码)4字节数据块类型码由ASCII字母(A-Z和a-z)组成
Chunk Data(数据块数据)可变长度存储按照Chunk Type Code指定的数据
CRC(循环冗余检测)4字节存储用来检测是否有错误的循环冗余码

 

 在表6-07中,CRC(cyclic redundancy check)域中的值是对Chunk Type Code域和Chunk Data域中的数据进行计算得到的。CRC具体算法定义在ISO 3309和ITU-T V.42中,其值按下面的CRC码生成多项式进行计算:

                                       x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1

数据块结构

1. 关键数据块

1. 关键数据块

关键数据块中的4个标准数据块是:

(1) 文件头数据块IHDR(header chunk):它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。

文件头数据块由13字节组成,它的格式如表6-08所示。

 

表6-08 PNG文件头键数据块的结构

 

域的名称

 

字节数

 

说明

Width4 bytes图像宽度,以像素为单位
Height4 bytes图像高度,以像素为单位
Bit depth1 byte图像深度:

 

索引彩色图像:1,2,4或8

灰度图像:1,2,4,8或16

真彩色图像:8或16

ColorType1 byte颜色类型:

 

0:灰度图像, 1,2,4,8或16

2:真彩色图像,8或16

3:索引彩色图像,1,2,4或8

               

4:带α通道数据的灰度图像,8或16

6:带α通道数据的真彩色图像,8或16

Compression method1 byte压缩方法(LZ77派生算法)
Filter method1 byte滤波器方法
Interlace method1 byte隔行扫描方法:

 

               

0:非隔行扫描

1: Adam7(由Adam M. Costello开发的7

遍隔行扫描方法)

 

 

(2) 调色板数据块PLTE(palette chunk):它包含有与索引彩色图像((indexed-color image))相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块(image data chunk)之前。真彩色的PNG数据流也可以有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像。调色板数据块结构如表6-09所示。

 

表6-09 调色板数据块结构

 

域的名称

 

字节数

 

说明

Red1 byte0 = 黑,255 = 红
Green">

 

0 = 黑,255 = 绿

Blue1 byte0 = 黑,255 = 蓝

 

 

调色板实际是一个彩色索引查找表,它的表项数目可以是1~256中的一个数,每个表项有3字节,因此调色板数据块所包含的最大字节数为768。

(3) 图像数据块IDAT(image data chunk):它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。

(4) 图像结束数据IEND(image trailer chunk):它用来标记PNG文件或者数据流已经结束,并且必须要放在文件的尾部。

除了表示数据块开始的IHDR必须放在最前面, 表示PNG文件结束的IEND数据块放在最后面之外,其他数据块的存放顺序没有限制。

2. 辅助数据块

PNG文件格式规范制定的10个辅助数据块是:

(1) 背景颜色数据块bKGD(background color)。

(2) 基色和白色度数据块cHRM(primary chromaticities and white point)。所谓白色度是指当R=G=B=最大值时在显示器上产生的白色度。

(3) 图像γ数据块gAMA(image gamma)。

(4) 图像直方图数据块hIST(image histogram)。

(5) 物理像素尺寸数据块pHYs(physical pixel dimensions)。

(6) 样本有效位数据块sBIT(significant bits)。

(7) 文本信息数据块tEXt(textual data)。

(8) 图像最后修改时间数据块tIME (image last-modification time)。

(9) 图像透明数据块tRNS (transparency)。

(10) 压缩文本数据块zTXt (compressed textual data)。

3. 数据块摘要

关键数据块、辅助数据块和专用公共数据块(special-purpose public chunks)综合在表6-10中。

 

表6-10 PNG文件格式中的数据块

 

数据块符号

 

数据块名称

 

多数据块

 

可选否

 

位置限制

IHDR文件头数据块

 

 

第一块
cHRM基色和白色点数据块

 

 

在PLTE和IDAT之前
gAMA图像γ数据块

 

 

在PLTE和IDAT之前
sBIT样本有效位数据块

 

 

在PLTE和IDAT之前
PLTE调色板数据块

 

 

在IDAT之前
bKGD背景颜色数据块

 

 

在PLTE之后IDAT之前
hIST图像直方图数据块

 

 

在PLTE之后IDAT之前
tRNS图像透明数据块

 

 

在PLTE之后IDAT之前
oFFs(专用公共数据块)

 

 

在IDAT之前
pHYs物理像素尺寸数据块

 

 

在IDAT之前
sCAL(专用公共数据块)

 

 

在IDAT之前
IDAT图像数据块

 

 

与其他IDAT连续
tIME图像最后修改时间数据块

 

 

无限制
tEXt文本信息数据块

 

 

无限制
zTXt压缩文本数据块

 

 

无限制
fRAc(专用公共数据块)

 

 

无限制
gIFg(专用公共数据块)

 

 

无限制
gIFt(专用公共数据块)

 

 

无限制
gIFx(专用公共数据块)

 

 

无限制
IEND图像结束数据

 

 

最后一个数据块

 

 tEXt和zTXt数据块中的标准关键字:

Title

 图像名称或者标题

 

Author

 图像作者名

 

Description

 图像说明

 

Copyright

 版权声明

 

CreationTime

 原图创作时间

 

Software

 创作图像使用的软件

 

Disclaimer

 弃权

 

Warning

 图像内容警告

 

Source

 创作图像使用的设备

 

Comment

 各种注释

 

 

 

JPEG 简易文档 V1.0

 

 

JPEG 压缩简介

-------------

1. 色彩模型

JPEG 的图片使用的是 YCrCb 颜色模型, 而不是计算机上最常用的 RGB. 关于色

彩模型, 这里不多阐述. 只是说明, YCrCb 模型更适合图形压缩. 因为人眼对图片上

的亮度 Y 的变化远比色度 C 的变化敏感. 我们完全可以每个点保存一个 8bit 的亮

度值, 每 2x2 个点保存一个 Cr Cb 值, 而图象在肉眼中的感觉不会起太大的变化.

所以, 原来用 RGB 模型, 4 个点需要 4x3=12 字节. 而现在仅需要 4+2=6 字节; 平

均每个点占 12bit. 当然 JPEG 格式里允许每个点的 C 值都记录下来; 不过 MPEG 里

都是按 12bit 一个点来存放的, 我们简写为 YUV12.

[R G B] -> [Y Cb Cr] 转换

-------------------------

(R,G,B 都是 8bit unsigned)

| Y | | 0.299 0.587 0.114 | | R | | 0 |

| Cb | = |- 0.1687 - 0.3313 0.5 | * | G | + |128|

| Cr | | 0.5 - 0.4187 - 0.0813| | B | |128|

Y = 0.299*R + 0.587*G + 0.114*B (亮度)

Cb = - 0.1687*R - 0.3313*G + 0.5 *B + 128

Cr = 0.5 *R - 0.4187*G - 0.0813*B + 128

[Y,Cb,Cr] -> [R,G,B] 转换

-------------------------

R = Y + 1.402 *(Cr-128)

G = Y - 0.34414*(Cb-128) - 0.71414*(Cr-128)

B = Y + 1.772 *(Cb-128)

一般, C 值 (包括 Cb Cr) 应该是一个有符号的数字, 但这里被处理过了, 方法

是加上了 128. JPEG 里的数据都是无符号 8bit 的.

2. DCT (离散余弦变换)

JPEG 里, 要对数据压缩, 先要做一次 DCT 变换. DCT 变换的原理, 涉及到数学

知识, 这里我们不必深究. 反正和傅立叶变换(学过高数的都知道) 是差不多了. 经过

这个变换, 就把图片里点和点间的规律呈现出来了, 更方便压缩.JPEG 里是对每 8x8

个点为一个单位处理的. 所以如果原始图片的长宽不是 8 的倍数, 都需要先补成 8

的倍数, 好一块块的处理. 另外, 记得刚才我说的 Cr Cb 都是 2x2 记录一次吗? 所

以大多数情况, 是要补成 16x16 的整数块.按从左到右, 从上到下的次序排列 (和我

们写字的次序一样). JPEG 里是对 Y Cr Cb 分别做 DCT 变换的.

JPEG 编码时使用的是 Forward DCT (FDCT) 解码时使用的 Inverse DCT (IDCT)

下面给出公式:

FDCT:

c(u,v) 7 7 2*x+1 2*y+1

F(u,v) = --------- * sum sum f(x,y) * cos (------- *u*PI)* cos (------ *v*PI)

4 x=0 y=0 16 16

u,v = 0,1,...,7

{ 1/2 当 u=v=0 时

c(u,v) = {

{ 1 其他情况

IDCT:

1 7 7 2*x+1 2*y+1

f(x,y) = --- * sum sum c(u,v)*F(u,v)*cos (------- *u*PI)* cos (------ *v*PI)

4 u=0 v=0 16 16

x,y=0,1...7

这个步骤很花时间, 另外有种 AA&N 优化算法, 大家可以去 inet 自己找一下.

在 Intel 主页上可以找到 AA&N IDCT 的 MMX 优化代码.

3. 重排列 DCT 结果

DCT 将一个 8x8 的数组变换成另一个 8x8 的数组. 但是内存里所有数据都是线

形存放的, 如果我们一行行的存放这 64 个数字, 每行的结尾的点和下行开始的点就

没有什么关系, 所以 JPEG 规定按如下次序整理 64 个数字.

 0, 1, 5, 6,14,15,27,28,

 2, 4, 7,13,16,26,29,42,

 3, 8,12,17,25,30,41,43,

 9,11,18,24,31,40,44,53,

10,19,23,32,39,45,52,54,

20,22,33,38,46,51,55,60,

21,34,37,47,50,56,59,61,

35,36,48,49,57,58,62,63

这样数列里的相邻点在图片上也是相邻的了.

4. 量化

对于前面得到的 64 个空间频率振幅值, 我们将对它们作幅度分层量化操作.方

法就是分别除以量化表里对应值并四舍五入.

for (i = 0 ; i<=63; i++ )

    vector[i] = (int) (vector[i] / quantization_table[i] + 0.5)

下面有张 JPEG 标准量化表. (按上面同样的弯曲次序排列)

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

这张表依据心理视觉阀制作, 对 8bit 的亮度和色度的图象的处理效果不错.

当然我们可以使用任意的量化表. 量化表是定义在 jpeg 的 DQT 标记后. 一般

为 Y 值定义一个, 为 C 值定义一个.

量化表是控制 JPEG 压缩比的关键. 这个步骤除掉了一些高频量, 损失了很高

细节. 但事实上人眼对高空间频率远没有低频敏感.所以处理后的视觉损失很小.

另一个重要原因是所有的图片的点与点之间会有一个色彩过渡的过程. 大量的图象

信息被包含在低空间频率中. 经过量化处理后, 在高空间频率段, 将出现大量连续

的零.

5. 0 RLC 编码

现在我们矢量中有许多连续的 0. 我们可以使用 RLC 来压缩掉这些 0. 这里我们

将跳过第一个矢量 (后面将解释为什么) 因为它的编码比较特别. 假设有一组矢量

(64 个的后 63 个) 是

57,45,0,0,0,0,23,0,-30,-16,0,0,1,0,0,0, 0 , 0 ,0 , 0,..,0

经过 RLC 压缩后就是

(0,57) ; (0,45) ; (4,23) ; (1,-30) ; (0,-16) ; (2,1) ; EOB

EOB 是一个结束标记, 表示后面都是 0 了. 实际上, 我们用 (0,0) 表示 EOB

但是, 如果这组数字不以 0 结束, 那么就不需要 EOB.

由于后面 huffman 编码的要求, 每组数字前一个表示 0 的数量的必须是 4 bit,

就是说, 只能是 0~15, 所以我们实际这样编码:

(0,57) ; (15,0) (2,3) ; (4,2) ; (15,0) (15,0) (1,895) , (0,0)

注意 (15,0) 表示了 16 个连续的 0.

6. huffman 编码

为了提高储存效率, JPEG 里并不直接保存数值, 而是将数值按位数分成 16 组:

数值 组 实际保存值

0 0 -

-1,1 1 0,1

-3,-2,2,3 2 00,01,10,11

-7,-6,-5,-4,4,5,6,7 3 000,001,010,011,100,101,110,111

-15,..,-8,8,..,15 4 0000,..,0111,1000,..,1111

-31,..,-16,16,..,31 5 00000,..,01111,10000,..,11111

-63,..,-32,32,..,63 6 .

-127,..,-64,64,..,127 7 .

-255,..,-128,128,..,255 8 .

-511,..,-256,256,..,511 9 .

-1023,..,-512,512,..,1023 10 .

-2047,..,-1024,1024,..,2047 11 .

-4095,..,-2048,2048,..,4095 12 .

-8191,..,-4096,4096,..,8191 13 .

-16383,..,-8192,8192,..,16383 14 .

-32767,..,-16384,16384,..,32767 15 .

还是来看前面的例子:

(0,57) ; (0,45) ; (4,23) ; (1,-30) ; (0,-8) ; (2,1) ; (0,0)

只处理每对数右边的那个:

57 是第 6 组的, 实际保存值为 111001 , 所以被编码为 (6,111001)

45 , 同样的操作, 编码为 (6,101101)

23 -> (5,10111)

-30 -> (5,00001)

-8 -> (4,0111)

1 -> (1,1)

前面的那串数字就变成了:

(0,6), 111001 ; (0,6), 101101 ; (4,5), 10111; (1,5), 00001; (0,4) , 0111 ;

(2,1), 1 ; (0,0)

括号里的数值正好合成一个字节. 后面被编码的数字表示范围是 -32767..32767.

合成的字节里, 高 4 位是前续 0 的个数, 低 4 位描述了后面数字的位数.

继续刚才的例子, 如果 06 的 huffman 编码为 111000

69 = (4,5) --- 1111111110011001

21 = (1,5) --- 11111110110

4 = (0,4) --- 1011

33 = (2,1) --- 11011

0 = EOB = (0,0) --- 1010

那么最后对于前面的例子表示的 63 个系数 (记得我们将第一个跳过了吗?) 按位流

写入 JPG 文件中就是这样的:

111000 111001 111000 101101 1111111110011001 10111 11111110110 00001

1011 0111 11011 1 1010

DC 的编码

---------

记得刚才我们跳过了每组 64 个数据的第一个吧, DC 就是指的这个数字 (后面 63

个简称 AC) 代入前面的 FDCT 公式可以得到

c(0,0) 7 7

DC = F(0,0) = --------- * sum sum f(x,y) * cos 0 * cos 0 其中 c(0,0) = 1/2

4 x=0 y=0

1 7 7

= --- * sum sum f(x,y)

8 x=0 y=0

即一块图象样本的平均值. 就是说, 它包含了原始 8x8 图象块里的很多能量. (通常

会得到一个很大的数值)

JPEG 的作者指出连续块的 DC 率之间有很紧密的联系, 因此他们决定对 8x8 块的

DC 值的差别进行编码. (Y, Cb, Cr 分别有自己的 DC)

Diff = DC(i) - DC(i-1)

所以这一块的 DC(i) 就是: DC(i) = DC(i-1) + Diff

JPG 从 0 开始对 DC 编码, 所以 DC(0)=0. 然后再将当前 Diff 值加在上一个值上得

到当前值.

下面再来看看上面那个例子: (记住我们保存的 DC 是和上一块 DC 的差值 Diff)

例如上面例子中, Diff 是 -511, 就编码成

(9, 000000000)

如果 9 的 Huffman 编码是 1111110 (在 JPG 文件中, 一般有两个 Huffman 表, 一

个是 DC 用, 一个是 AC 用) 那么在 JPG 文件中, DC 的 2 进制表示为

1111110 000000000

它将放在 63 个 AC 的前面, 上面上个例子的最终 BIT 流如下:

1111110 000000000 111000 111001 111000 101101 1111111110011001 10111

11111110110 00001 1011 0111 11011 1 1010

下面简单叙述一下针对一个数据单元的图片 Y 的解码

-----------------------------------------------

在整个图片解码的开始, 你需要先初始化 DC 值为 0.

1) 先解码 DC:

a) 取得一个 Huffman 码 (使用 Huffman DC 表)

b) Huffman解码, 看看后面的数据位数 N

c) 取得 N 位, 计算 Diff 值

d) DC + = Diff

e) 写入 DC 值: " vector[0]=DC "

2) 解码 63 个 AC:

------- 循环处理每个 AC 直到 EOB 或者处理到 64 个 AC

a) 取得一个 Huffman 码 (使用 Huffman AC 表)

b) Huffman 解码, 得到 (前面 0 数量, 组号)

[记住: 如果是(0,0) 就是 EOB 了]

c) 取得 N 位(组号) 计算 AC

d) 写入相应数量的 0

e) 接下来写入 AC

-----------------

下一步的解码

------------

上一步我们得到了 64 个矢量. 下面我们还需要做一些解码工作:

1) 反量化 64 个矢量 : "for (i=0;i<=63;i++) vector[i]*=quant[i]"

2) 重排列 64 个矢量到 8x8 的块中

3) 对 8x8 的块作 IDCT

对 8x8 块的 (Y,Cb,Cr) 重复上面的操作 [Huffman 解码, 步骤 1), 2), 3)]

4) 将所有的有符号的 8bit 数加上 128

5) 转换 YCbCr 到 RGB

JPG 文件(Byte 级)里怎样组织图片信息

-----------------------------------

注意 JPEG/JFIF 文件格式使用 Motorola 格式, 而不是 Intel 格式, 就是说, 如果

是一个字的话, 高字节在前, 低字节在后.

JPG 文件是由一个个段 (segments) 构成的. 每个段长度 <=65535. 每个段从一个标

记字开始. 标记字都是 0xff 打头的, 以非 0 字节和 0xFF 结束. 例如 'FFDA' ,

'FFC4', 'FFC0'. 每个标记有它特定意义, 这是由第2字节指明的. 例如, SOS (Start

Of Scan = 'FFDA') 指明了你应该开始解码. 另一个标记 DQT (Define Quantization

Table = 0xFFDB) 就是说它后面有 64 字节的 quantization 表

在处理 JPG 文件时, 如果你碰到一个 0xFF, 而它后面的字节不是 0, 并且这个字节

没有意义. 那么你遇到的 0xFF 字节必须被忽略. (一些 JPG 里, 常用用 0xFF 做某

些填充用途) 如果你在做 huffman 编码时碰巧产生了一个 0xFF, 那么就用 0xFF

0x00 代替. 就是说在 jpeg 图形解码时碰到 FF00 就把它当作 FF 处理.

另外在 huffman 编码区域结束时, 碰到几个 bit 没有用的时候, 应该用 1 去填充.

然后后面跟 FF.

下面是几个重要的标记

--------------------

SOI = Start Of Image = 'FFD8'

这个标记只在文件开始出现一次

EOI = End Of Image = 'FFD9'

JPG 文件都以 FFD9 结束

RSTi = FFDi ( i = 0..7) [ RST0 = FFD0, RST7=FFD7]

= 复位标记

通常穿插在数据流里, 我想是担心 JPG 解码出问题吧(应该配合 DRI 使用). 不过很

多 JPG 都不使用它

(SOS --- RST0 --- RST1 -- RST2 --...

...-- RST6 --- RST7 -- RST0 --...)

----

标记

----

下面是必须处理的标记

SOF0 = Start Of Frame 0 = FFC0

SOS = Start Of Scan = FFDA

APP0 = it's the marker used to identify a JPG file which uses the JFIF

specification = FFE0

COM = Comment = FFFE

DNL = Define Number of Lines = FFDC

DRI = Define Restart Interval = FFDD

DQT = Define Quantization Table = FFDB

DHT = Define Huffman Table = FFC4

JPG 文件中 Haffman 表的储存

---------------------------

JPEG 里定义了一张表来描述 Haffman 树. 定义在 DHT 标记后面. 注意: Haffman

代码的长度限制在 16bit 内.

一般一个 JPG 文件里会有 2 类 Haffman 表: 一个用于 DC 一个用于 AC (实际有 4

个表, 亮度的 DC,AC 两个, 色度的 DC,AC 两个)

这张表是这样保存的:

1) 16 字节:

第 i 字节表示了 i 位长的 Huffman 代码的个数 (i= 1 到 16)

2) 这表的长度 (字节数) = 这 16 个数字之和

现在你可以想象这张表怎么存放的吧? 对应字节就是对应 Haffman 代码等价数字. 我

不多解释, 这需要你先了解 Haffman 算法. 这里只举一个例子:

Haffman 表的表头是 0,2,3,1,1,1,0,1,0,0,0,0,0,0,0,0

就是说长度为 1 的代码没有

长度为 2 的代码为 00

01

长度为 3 的代码是 100

101

110

长度为 4 的代码是 1110

长度为 5 的代码是 11110

长度为 6 的代码是 111110

长度为 7 的代码没有 (如果有一个的话应该是 1111110)

长度为 8 的代码是 11111100

.....

后面都没有了.

如果表下面的数据是

45 57 29 17 23 25 34 28

就是说

45 = 00

57 = 01

29 = 100

17 = 101

23 = 110

等等...

如果你懂 Haffman 编码, 这些不难理解

采样系数

--------

下面讲解的都是真彩 JPG 的解码, 灰度 JPG 的解码很简单, 因为图形中只有亮度信

息. 而彩色图形由 (Y, Cr, Cb) 构成, 前面提到过, Y 通常是每点采样一次, 而 Cr,

Cb 一般是 2x2 点采样一次, 当然也有的 JPG 是逐点采样, 或者每两点采样 (横向

两点, 纵向一点) 采样系数均被定义成对比最高采样系数的相对值.

一般情况 (即: Y 逐点采样, Cr Cb 每 2x2 点一次) 下: Y 有最高的采样率, 横向采

样系数HY=2 纵向采样系数 VY=2; Cb 的横向采样系数 HCb=1, 纵向采样系数 VCb=1;

同样 HCr=1, VCr=1

在 Jpeg 里, 8x8 个原始数据, 经过 RLC, Huffman 编码后的一串数据流称为一个

Data Unit (DU) JPG 里按 DU 为单位的编码次序如下:

1) for (counter_y=1;counter_y<=VY;counter_y++)

for (counter_x=1;counter_x<=HY;counter_x++)

{ 对 Y 的 Data Unit 编码 }

2) for (counter_y=1;counter_y<=VCb ;counter_y++)

for (counter_x=1;counter_x<=HCb;counter_x++)

{ 对 Cb 的 Data Unit 编码 }

3) for (counter_y=1;counter_y<=VCr;counter_y++)

for (counter_x=1;counter_x<=HCr;counter_x++)

{ 对 Cr 的 Data Unit 编码 }

按我上面的例子: (HY=2, VY=2 ; HCb=VCb =1, HCr,VCr=1) 就是这样一个次序

YDU,YDU,YDU,YDU,CbDU,CrDU

这些就描述了一块 16x16 的图形. 16x16 = (Hmax*8 x Vmax*8) 这里 Hmax=HY=2

Vmax=VY=2

一个 (Hmax*8,Vmax*8) 的块被称作 MCU (Minimun Coded Unix) 前面例子中一个

MCU = YDU,YDU,YDU,YDU,CbDU,CrDU

如果 HY =1, VY=1

HCb=1, VCb=1

HCr=1, VCr=1

这样 (Hmax=1,Vmax=1), MCU 只有 8x8 大, MCU = YDU,CbDU,CrDU

对于灰度 JPG, MCU 只有一个 DU (MCU = YDU)

JPG 文件里, 图象的每个组成部分的采样系数定义在 SOF0 (FFC0) 标记后

简单说一下 JPG 文件的解码

-------------------------

解码程序闲从 JPG 文件中读出采样系数, 这样就知道了 MCU 的大小, 算出整个图象

有几个 MCU. 解码程序再循环逐个对 MCU 解码, 一直到检查到 EOI 标记. 对于每个

MCU, 按正规的次序解出每个 DU, 然后组合, 转换成 (R,G,B) 就 OK 了

附:JPEG 文件格式

~~~~~~~~~~~~~~~~

- 文件头 (2 bytes): $ff, $d8 (SOI) (JPEG 文件标识)

- 任意数量的段 , 见后面

- 文件结束 (2 bytes): $ff, $d9 (EOI)

段的格式:

~~~~~~~~~

- header (4 bytes):

$ff 段标识

n 段的类型 (1 byte)

sh, sl 该段长度, 包括这两个字节, 但是不包括前面的 $ff 和 n.

注意: 长度不是 intel 次序, 而是 Motorola 的, 高字节在前,

低字节在后!

- 该段的内容, 最多 65533 字节

注意:

- 有一些无参数的段 (下面那些前面注明星号的)

这些段没有长度描述 (而且没有内容), 只有 $ff 和类型字节.

- 每一个段结束到下一个 $ff 间的数据都是合法的, 必须被忽略掉.

段的类型:

~~~~~~~~~

*TEM = $01 可以忽略掉

SOF0 = $c0 帧开始 (baseline JPEG), 细节附后

SOF1 = $c1 dito

SOF2 = $c2 通常不支持

SOF3 = $c3 通常不支持

SOF5 = $c5 通常不支持

SOF6 = $c6 通常不支持

SOF7 = $c7 通常不支持

SOF9 = $c9 arithmetic 编码(Huffman 的一种扩展算法), 通常不支持

SOF10 = $ca 通常不支持

SOF11 = $cb 通常不支持

SOF13 = $cd 通常不支持

SOF14 = $ce 通常不支持

SOF14 = $ce 通常不支持

SOF15 = $cf 通常不支持

DHT = $c4 定义 Huffman Table, 细节附后

JPG = $c8 未定义/保留 (引起解码错误)

DAC = $cc 定义 Arithmetic Table, 通常不支持

*RST0 = $d0 RSTn 用于 resync, 通常被忽略

*RST1 = $d1

*RST2 = $d2

*RST3 = $d3

*RST4 = $d4

*RST5 = $d5

*RST6 = $d6

*RST7 = $d7

SOI = $d8 图片开始

EOI = $d9 图片结束

SOS = $da 扫描行开始, 细节附后

DQT = $db 定义 Quantization Table, 细节附后

DNL = $dc 通常不支持, 忽略

DRI = $dd 定义重新开始间隔, 细节附后

DHP = $de 忽略 (跳过)

EXP = $df 忽略 (跳过)

APP0 = $e0 JFIF APP0 segment marker (细节略)

APP15 = $ef 忽略

JPG0 = $f0 忽略 (跳过)

JPG13 = $fd 忽略 (跳过)

COM = $fe 注释, 细节附后

其它的段类型都保留必须跳过

SOF0: Start Of Frame 0:

~~~~~~~~~~~~~~~~~~~~~~~

- $ff, $c0 (SOF0)

- 长度 (高字节, 低字节), 8+components*3

- 数据精度 (1 byte) 每个样本位数, 通常是 8 (大多数软件不支持 12 和 16)

- 图片高度 (高字节, 低字节), 如果不支持 DNL 就必须 >0

- 图片宽度 (高字节, 低字节), 如果不支持 DNL 就必须 >0

- components 数量(1 byte), 灰度图是 1, YCbCr/YIQ 彩色图是 3, CMYK 彩色图

是 4

- 每个 component: 3 bytes

- component id (1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q)

- 采样系数 (bit 0-3 vert., 4-7 hor.)

- quantization table 数

DRI: Define Restart Interval:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- $ff, $dd (DRI)

- 长度 (高字节, 低字节), 必须是 4

- MCU 块的单元中的重新开始间隔 (高字节, 低字节),

意思是说, 每 n 个 MCU 块就有一个 RSTn 标记.

第一个标记是 RST0, 然后是 RST1 等, RST7 后再从 RST0 重复

DQT: Define Quantization Table:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- $ff, $db (DQT)

- 长度 (高字节, 低字节)

- QT 信息 (1 byte):

bit 0..3: QT 号(0..3, 否则错误)

bit 4..7: QT 精度, 0 = 8 bit, 否则 16 bit

- n 字节的 QT, n = 64*(精度+1)

评论:

- 一个单独的 DQT 段可以包含多个 QT, 每个都有自己的信息字节

- 当精度=1 (16 bit), 每个字都是高位在前低位在后

DAC: Define Arithmetic Table:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

法律原因, 现在的软件不支持 arithmetic 编码.

不能生产使用 arithmetic 编码的 JPEG 文件

DHT: Define Huffman Table:

~~~~~~~~~~~~~~~~~~~~~~~~~~

- $ff, $c4 (DHT)

- 长度 (高字节, 低字节)

- HT 信息 (1 byte):

bit 0..3: HT 号 (0..3, 否则错误)

bit 4 : HT 类型, 0 = DC table, 1 = AC table

bit 5..7: 必须是 0

- 16 bytes: 长度是 1..16 代码的符号数. 这 16 个数的和应该 <=256

- n bytes: 一个包含了按递增次序代码长度排列的符号表

(n = 代码总数)

评论:

- 一个单独的 DHT 段可以包含多个 HT, 每个都有自己的信息字节

COM: 注释:

~~~~~~~~~~

- $ff, $fe (COM)

- 注释长度 (高字节, 低字节) = L+2

- 注释为长度为 L 的字符流

SOS: Start Of Scan:

~~~~~~~~~~~~~~~~~~~

- $ff, $da (SOS)

- 长度 (高字节, 低字节), 必须是 6+2*(扫描行内组件的数量)

- 扫描行内组件的数量 (1 byte), 必须 >= 1 , <=4 (否则是错的) 通常是 3

- 每个组件(部分): 2 bytes

- component id (1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q), 见 SOF0

- 使用的 Huffman 表00-1-14:

- bit 0..3: AC table (0..3)

- bit 4..7: DC table (0..3)

- 忽略 3 bytes (???)

评论:

- 图片数据 (一个个扫描行) 紧接着 SOS 段.

 

 

           GIF文档

 

 

  | |   

 

 

 

1.概述

~~~~~~~~

  GIF(Graphics Interchange Format,图形交换格式)文件是由 CompuServe公司开发的图形文件格式,版权所有,任何商业目的使用均须 CompuServe公司授权。

  GIF图象是基于颜色列表的(存储的数据是该点的颜色对应于颜色列表的索引值),最多只支持8位(256色)。GIF文件内部分成许多存储块,用来存储多幅图象或者是决定图象表现行为的控制块,用以实现动画和交互式应用。GIF文件还通过LZW压缩算法压缩图象数据来减少图象尺寸(关于LZW算法和GIF数据压缩>>...)。

 

2.GIF文件存储结构

~~~~~~~~~~~~~~~~~~~

  GIF文件内部是按块划分的,包括控制块( Control Block )和数据块(Data Sub-blocks)两种。控制块是控制数据块行为的,根据不同的控制块包含一些不同的控制参数;数据块只包含一些8-bit的字符流,由它前面的控制块来决定它的功能,每个数据块大小从0到255个字节,数据块的第一个字节指出这个数据块大小(字节数),计算数据块的大小时不包括这个字节,所以一个空的数据块有一个字节,那就是数据块的大小0x00。下表是一个数据块的结构:

BYTE 7 6 5 4 3 2 1 0 BIT

0 块大小

 Block Size - 块大小,不包括这个这个字节(不计算块大小自身)

1   Data Values - 块数据,8-bit的字符串

2  

...  

254  

255  

  一个GIF文件的结构可分为文件头(File Header)、GIF数据流(GIF Data Stream)和文件终结器(Trailer)三个部分。文件头包含GIF文件署名(Signature)和版本号(Version);GIF数据流由控制标识符、图象块(Image Block)和其他的一些扩展块组成;文件终结器只有一个值为0x3B的字符(';')表示文件结束。下表显示了一个GIF文件的组成结构:

  GIF署名 文件头  

  版本号

  逻辑屏幕标识符 GIF数据流  

  全局颜色列表  

  ...  

  图象标识符 图象块                              

  图象局部颜色列表图

                            基于颜色列表的图象数据  

 

  ...  

  GIF结尾 文件结尾  

  下面就具体介绍各个部分:

文件头部分(Header)

~~~~~~~~~~~~~~~~~

GIF署名(Signature)和版本号(Version)

~~~~~~~~~~~~~~~~~~~~~~~~~~~

GIF署名用来确认一个文件是否是GIF格式的文件,这一部分由三个字符组成:"GIF";文件版本号也是由三个字节组成,可以为"87a"或"89a".具体描述见下表:

BYTE 7 6 5 4 3 2 1 0 BIT

1 'G' GIF文件标识

2 'I'

3 'F'

4 '8' GIF文件版本号:87a - 1987年5月

        89a - 1989年7月

5 '7'或'9'

6 'a'

GIF数据流部分(GIF Data Stream)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~

逻辑屏幕标识符(Logical Screen Descriptor)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

这一部分由7个字节组成,定义了GIF图象的大小(Logical Screen Width & Height)、颜色深度(Color Bits)、背景色(Blackground Color Index)以及有无全局颜色列表(Global Color Table)和颜色列表的索引数(Index Count),具体描述见下表:

BYTE 7 6 5 4 3 2 1 0 BIT  

1 逻辑屏幕宽度 像素数,定义GIF图象的宽度

2

3 逻辑屏幕高度 像素数,定义GIF图象的高度

4

5 m cr s pixel 具体描述见下...

6 背景色 背景颜色(在全局颜色列表中的索引,如果没有全局颜色列表,该值没有意义)

7 像素宽高比 像素宽高比(Pixel Aspect Radio)

m - 全局颜色列表标志(Global Color Table Flag),当置位时表示有全局颜色列表,pixel值有意义.

cr - 颜色深度(Color ResoluTion),cr+1确定图象的颜色深度.

s - 分类标志(Sort Flag),如果置位表示全局颜色列表分类排列.

pixel - 全局颜色列表大小,pixel+1确定颜色列表的索引数(2的pixel+1次方).

全局颜色列表(Global Color Table)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

全局颜色列表必须紧跟在逻辑屏幕标识符后面,每个颜色列表索引条目由三个字节组成,按R、G、B的顺序排列。

BYTE 7 6 5 4 3 2 1 0 BIT

1 索引1的红色值  

2 索引1的绿色值  

3 索引1的蓝色值  

4 索引2的红色值  

5 索引2的绿色值  

6 索引2的蓝色值  

7 ...                             

图象标识符(Image Descriptor)

~~~~~~~~~~~~~~~~~~~~~~~~~

一个GIF文件内可以包含多幅图象,一幅图象结束之后紧接着下是一幅图象的标识符,图象标识符以0x2C(',')字符开始,定义紧接着它的图象的性质,包括图象相对于逻辑屏幕边界的偏移量、图象大小以及有无局部颜色列表和颜色列表大小,由10个字节组成:

BYTE 7 6 5 4 3 2 1 0 BIT  

1 0 0 1 0 1 1 0 0 图象标识符开始,固定值为','

2 X方向偏移量 必须限定在逻辑屏幕尺寸范围内

3

4 Y方向偏移量

5

6 图象宽度

7

8 图象高度

9

10 m i s r pixel m - 局部颜色列表标志(Local Color Table Flag)

              置位时标识紧接在图象标识符之后有一个局部颜色列表,供紧跟在它之后的一幅图象使用;值否时使用全局颜色列表,忽略pixel值。

i - 交织标志(Interlace Flag),置位时图象数据使用交织方式排列(详细描述...),否则使用顺序排列。

s - 分类标志(Sort Flag),如果置位表示紧跟着的局部颜色列表分类排列.

r - 保留,必须初始化为0.

pixel - 局部颜色列表大小(Size of Local Color Table),pixel+1就为颜色列表的位数

局部颜色列表(Local Color Table)

~~~~~~~~~~~~~~~~~~~~~~~~~~

如果上面的局部颜色列表标志置位的话,则需要在这里(紧跟在图象标识符之后)定义一个局部颜色列表以供紧接着它的图象使用,注意使用前应线保存原来的颜色列表,使用结束之后回复原来保存的全局颜色列表。如果一个GIF文件即没有提供全局颜色列表,也没有提供局部颜色列表,可以自己创建一个颜色列表,或使用系统的颜色列表。局部颜色列表的排列方式和全局颜色列表一样:RGBRGB......

基于颜色列表的图象数据(Table-Based Image Data)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

由两部分组成:LZW编码长度(LZW Minimum Code Size)和图象数据(Image Data)。

BYTE 7 6 5 4 3 2 1 0 BIT

1 LZW编码长度 LZW编码初始码表大小的位数,详细描述见LZW编码...

 

 

 

...

  图象数据,由一个或几个数据块(Data Sub-blocks)组成

数据块

 

...

 

GIF图象数据使用了LZW压缩算法(详细介绍请看后面的『LZW算法和GIF数据压缩』),大大减小了图象数据的大小。图象数据在压缩前有两种排列格式:连续的和交织的(由图象标识符的交织标志控制)。连续方式按从左到右、从上到下的顺序排列图象的光栅数据;交织图象按下面的方法处理光栅数据:

创建四个通道(pass)保存数据,每个通道提取不同行的数据:

第一通道(Pass 1)提取从第0行开始每隔8行的数据;

第二通道(Pass 2)提取从第4行开始每隔8行的数据;

第三通道(Pass 3)提取从第2行开始每隔4行的数据;

第四通道(Pass 4)提取从第1行开始每隔2行的数据;

下面的例子演示了提取交织图象数据的顺序:

行  通道1   通道2   通道3   通道4   

0  -------------------------------------------------------- 1        

1 --------------------------------------------------------       4  

2  --------------------------------------------------------     3    

3  --------------------------------------------------------       4  

4  --------------------------------------------------------   2      

5  --------------------------------------------------------       4  

6  --------------------------------------------------------     3    

7  --------------------------------------------------------       4  

8  -------------------------------------------------------- 1        

9  --------------------------------------------------------       4  

10 --------------------------------------------------------     3    

11 --------------------------------------------------------       4  

12 --------------------------------------------------------   2      

13 --------------------------------------------------------       4  

14 --------------------------------------------------------     3    

15 --------------------------------------------------------       4  

16 -------------------------------------------------------- 1        

17 --------------------------------------------------------       4  

18 --------------------------------------------------------     3    

19 --------------------------------------------------------       4  

20 --------------------------------------------------------   2      

 

图形控制扩展(Graphic Control Extension)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

这一部分是可选的(需要89a版本),可以放在一个图象块(图象标识符)或文本扩展块的前面,用来控制跟在它后面的第一个图象(或文本)的渲染(Render)形式,组成结构如下:

BYTE 7 6 5 4 3 2 1 0 BIT

1 扩展块标识 Extension Introducer - 标识这是一个扩展块,固定值0x21

2 图形控制扩展标签 Graphic Control Label - 标识这是一个图形控制扩展块,固定值0xF9

3 块大小 Block Size - 不包括块终结器,固定值4

4 保留 处置方法 i

 t

 i - 用户输入标志;t - 透明色标志。详细描述见下...

5 延迟时间 Delay Time - 单位1/100秒,如果值不为1,表示暂停规定的时间后再继续往下处理数据流

6

7 透明色索引 Transparent Color Index - 透明色索引值

8 块终结器 Block Terminator - 标识块终结,固定值0

处置方法(Disposal Method):指出处置图形的方法,当值为:

                        0 - 不使用处置方法

                        1 - 不处置图形,把图形从当前位置移去

                        2 - 回复到背景色

                        3 - 回复到先前状态

                      4-7 -

                                   自定义

用户输入标志(Use Input Flag):指出是否期待用户有输入之后才继续进行下去,置位表示期待,值否表示不期待。用户输入可以是按回车键、鼠标点击等,可以和延迟时间一起使用,在设置的延迟时间内用户有输入则马上继续进行,或者没有输入直到延迟时间到达而继续

透明颜色标志(Transparent Color Flag):置位表示使用透明颜色

注释扩展(Comment Extension)

~~~~~~~~~~~~~~~~~~~~~~~~~~~

这一部分是可选的(需要89a版本),可以用来记录图形、版权、描述等任何的非图形和控制的纯文本数据(7-bit ASCII字符),注释扩展并不影响对图象数据流的处理,解码器完全可以忽略它。存放位置可以是数据流的任何地方,最好不要妨碍控制和数据块,推荐放在数据流的开始或结尾。具体组成:

BYTE 7 6 5 4 3 2 1 0 BIT

1 扩展块标识 Extension Introducer - 标识这是一个扩展块,固定值0x21

2 注释块标签 Comment Label - 标识这是一个注释块,固定值0xFE

 

...

  Comment Data - 一个或多个数据块(Data Sub-Blocks)组成

注释块

 

...

 

  块终结器 Block Terminator - 标识注释块结束,固定值0

图形文本扩展(Plain Text Extension)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

这一部分是可选的(需要89a版本),用来绘制一个简单的文本图象,这一部分由用来绘制的纯文本数据(7-bit ASCII字符)和控制绘制的参数等组成。绘制文本借助于一个文本框(Text Grid)来定义边界,在文本框中划分多个单元格,每个字符占用一个单元,绘制时按从左到右、从上到下的顺序依次进行,直到最后一个字符或者占满整个文本框(之后的字符将被忽略,因此定义文本框的大小时应该注意到是否可以容纳整个文本),绘制文本的颜色索引使用全局颜色列表,没有则可以使用一个已经保存的前一个颜色列表。另外,图形文本扩展块也属于图形块(Graphic Rendering Block),可以在它前面定义图形控制扩展对它的表现形式进一步修改。图形文本扩展的组成:

 

BYTE 7 6 5 4 3 2 1 0 BIT

1 扩展块标识 Extension Introducer - 标识这是一个扩展块,固定值0x21

2 图形控制扩展标签 Plain Text Label - 标识这是一个图形文本扩展块,固定值0x01

3 块大小 Block Size - 块大小,固定值12

4 文本框左边界位置 Text Glid Left Posotion - 像素值,文本框离逻辑屏幕的左边界距离

5

6 文本框上边界位置 Text Glid Top Posotion - 像素值,文本框离逻辑屏幕的上边界距离

7

8 文本框高度 Text Glid Width -像素值

9

10 文本框高度 Text Glid Height - 像素值

11

12 字符单元格宽度 Character Cell Width - 像素值,单个单元格宽度

13 字符单元格高度 Character Cell Height- 像素值,单个单元格高度

14 文本前景色索引 Text Foreground Color Index - 前景色在全局颜色列表中的索引

15 文本背景色索引 Text Blackground Color Index - 背景色在全局颜色列表中的索引

N

...

  Plain Text Data - 一个或多个数据块(Data Sub-Blocks)组成,保存要在显示的字符串。

文本数据块

 

...

 

N+1 块终结 Block Terminator - 标识注释块结束,固定值0

推荐:1.由于文本的字体(Font)和尺寸(Size)没有定义,解码器应该根据情况选择最合适的;

2.如果一个字符的值小于0x20或大于0xF7,则这个字符被推荐显示为一个空格(0x20);

3.为了兼容性,最好定义字符单元格的大小为8x8或8x16(宽度x高度)。

应用程序扩展(Application Extension)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

这是提供给应用程序自己使用的(需要89a版本),应用程序可以在这里定义自己的标识、信息等,组成:

 

BYTE 7 6 5 4 3 2 1 0 BIT

1 扩展块标识 Extension Introducer - 标识这是一个扩展块,固定值0x21

2 图形控制扩展标签 Application Extension Label - 标识这是一个应用程序扩展块,固定值0xFF

3 块大小 Block Size - 块大小,固定值11

4 应用程序标识符 Application Identifier - 用来鉴别应用程序自身的标识(8个连续ASCII字符)

5

6

7

8

9

10

11

12 应用程序鉴别码 Application Authentication Code - 应用程序定义的特殊标识码(3个连续ASCII字符)

13

14

N

...

  应用程序自定义数据块 - 一个或多个数据块(Data Sub-Blocks)组成,保存应用程序自己定义的数据

应用程序数据

 

...

 

N+1 块终结器 lock Terminator - 标识注释块结束,固定值0

文件结尾部分

~~~~~~~~~~~

文件终结器(Trailer)

~~~~~~~~~~~~~~~~

这一部分只有一个值为0的字节,标识一个GIF文件结束.

 

BYTE 7 6 5 4 3 2 1 0  

1 文件终结

 GIF Trailer - 标识GIF文件结束,固定值0x3B

2.LZW算法和GIF数据压缩

~~~~~~~~~~~~~~~~~~~~~~~~~~~

  GIF文件的图象数据使用了可变长度编码的LZW压缩算法(Variable-Length_Code LZW Compression),这是从LZW(Lempel Ziv Compression)压缩算法演变过来的,通过压缩原始数据的重复部分来达到减少文件大小的目的。

标准的LZW压缩原理:

~~~~~~~~~~~~~~~~~~

先来解释一下几个基本概念:

  LZW压缩有三个重要的对象:数据流(CharStream)、编码流(CodeStream)和编译表(String Table)。在编码时,数据流是输入对象(图象的光栅数据序列),编码流就是输出对象(经过压缩运算的编码数据);在解码时,编码流则是输入对象,数据流是输出对象;而编译表是在编码和解码时都须要用借助的对象。

字符(Character):最基础的数据元素,在文本文件中就是一个字节,在光栅数据中就是一个像素的颜色在指定的颜色列表中的索引值;

字符串(String):由几个连续的字符组成;

前缀(Prefix):也是一个字符串,不过通常用在另一个字符的前面,而且它的长度可以为0;

根(Root):单个长度的字符串;

编码(Code):一个数字,按照固定长度(编码长度)从编码流中取出,编译表的映射值;

图案:一个字符串,按不定长度从数据流中读出,映射到编译表条目.

  LZW压缩的原理:提取原始图象数据中的不同图案,基于这些图案创建一个编译表,然后用编译表中的图案索引来替代原始光栅数据中的相应图案,减少原始数据大小。看起来和调色板图象的实现原理差不多,但是应该注意到的是,我们这里的编译表不是事先创建好的,而是根据原始图象数据动态创建的,解码时还要从已编码的数据中还原出原来的编译表(GIF文件中是不携带编译表信息的),为了更好理解编解码原理,我们来看看具体的处理过程:

编码器(Compressor)

~~~~~~~~~~~~~~~~

  编码数据,第一步,初始化一个编译表,假设这个编译表的大小是12位的,也就是最多有4096个单位,另外假设我们有32个不同的字符(也可以认为图象的每个像素最多有32种颜色),表示为a,b,c,d,e...,初始化编译表:第0项为a,第1项为b,第2项为c...一直到第31项,我们把这32项就称为根。

  开始编译,先定义一个前缀对象Current Prefix,记为[.c.],现在它是空的,然后定义一个当前字符串Current String,标记为[.c.]k,[.c.]就为Current Prefix,k就为当前读取字符。现在来读取数据流的第一个字符,假如为p,那么Current String就等于[.c.]p(由于[.c.]为空,实际上值就等于p),现在在编译表中查找有没有Current String的值,由于p就是一个根字符,我们已经初始了32个根索引,当然可以找到,把p设为Current Prefix的值,不做任何事继续读取下一个字符,假设为q,Current String就等于[.c.]q(也就是pq),看看在编译表中有没有该值,当然。没有,这时我们要做下面的事情:将Current String的值(也就是pq)添加到编译表的第32项,把Current Prefix的值(也就是p)在编译表中的索引输出到编码流,修改Current Prefix为当前读取的字符(也就是q)。继续往下读,如果在编译表中可以查找到Current String的值([.c.]k),则把Current String的值([.c.]k)赋予Current Prefix;如果查找不到,则添加Current String的值([.c.]k)到编译表,把Current Prefix的值([.c.])在编译表中所对应的索引输出到编码流,同时修改Current Prefix为k ,这样一直循环下去直到数据流结束。伪代码看起来就像下面这样:

编码器伪代码 Initialize String Table;

[.c.] = Empty;

[.c.]k = First Character in CharStream;

while ([.c.]k != EOF )

{

  if ( [.c.]k is in the StringTable)

  {

    [.c.] = [.c.]k;

  }

  else

  {

    add [.c.]k to the StringTable;

    Output the Index of [.c.] in the StringTable to the CodeStream;

    [.c.] = k;

  }

  [.c.]k = Next Character in CharStream;

}

Output the Index of [.c.] in the StringTable to the CodeStream;

 

来看一个具体的例子,我们有一个字母表a,b,c,d.有一个输入的字符流abacaba。现在来初始化编译表:#0=a,#1=b,#2=c,#3=d.现在开始读取第一个字符a,[.c.]a=a,可以在在编译表中找到,修改[.c.]=a;不做任何事继续读取第二个字符b,[.c.]b=ab,在编译表中不能找,那么添加[.c.]b到编译表:#4=ab,同时输出[.c.](也就是a)的索引#0到编码流,修改[.c.]=b;读下一个字符a,[.c.]a=ba,在编译表中不能找到:添加编译表#5=ba,输出[.c.]的索引#1到编码流,修改[.c.]=a;读下一个字符c,[.c.]c=ac,在编译表中不能找到:添加编译表#6=ac,输出[.c.]的索引#0到编码流,修改[.c.]=c;读下一个字符a,[.c.]c=ca,在编译表中不能找到:添加编译表#7=ca,输出[.c.]的索引#2到编码流,修改[.c.]=a;读下一个字符b,[.c.]b=ab,编译表的#4=ab,修改[.c.]=ab;读取最后一个字符a,[.c.]a=aba,在编译表中不能找到:添加编译表#8=aba,输出[.c.]的索引#4到编码流,修改[.c.]=a;好了,现在没有数据了,输出[.c.]的值a的索引#0到编码流,这样最后的输出结果就是:#0#1#0#2#4#0.

解码器(Decompressor)

~~~~~~~~~~~~~~~~~~

  好了,现在来看看解码数据。数据的解码,其实就是数据编码的逆向过程,要从已经编译的数据(编码流)中找出编译表,然后对照编译表还原图象的光栅数据。

  首先,还是要初始化编译表。GIF文件的图象数据的第一个字节存储的就是LZW编码的编码大小(一般等于图象的位数),根据编码大小,初始化编译表的根条目(从0到2的编码大小次方),然后定义一个当前编码Current Code,记作[code],定义一个Old Code,记作[old]。读取第一个编码到[code],这是一个根编码,在编译表中可以找到,把该编码所对应的字符输出到数据流,[old]=[code];读取下一个编码到[code],这就有两种情况:在编译表中有或没有该编码,我们先来看第一种情况:先输出当前编码[code]所对应的字符串到数据流,然后把[old]所对应的字符(串)当成前缀prefix [...],当前编码[code]所对应的字符串的第一个字符当成k,组合起来当前字符串Current String就为[...]k,把[...]k添加到编译表,修改[old]=[code],读下一个编码;我们来看看在编译表中找不到该编码的情况,回想一下编码情况:如果数据流中有一个p[...]p[...]pq这样的字符串,p[...]在编译表中而p[...]p不在,编译器将输出p[...]的索引而添加p[...]p到编译表,下一个字符串p[...]p就可以在编译表中找到了,而p[...]pq不在编译表中,同样将输出p[...]p的索引值而添加p[...]pq到编译表,这样看来,解码器总比编码器『慢一步』,当我们遇到p[...]p所对应的索引时,我们不知到该索引对应的字符串(在解码器的编译表中还没有该索引,事实上,这个索引将在下一步添加),这时需要用猜测法:现在假设上面的p[...]所对应的索引值是#58,那么上面的字符串经过编译之后是#58#59,我们在解码器中读到#59时,编译表的最大索引只有#58,#59所对应的字符串就等于#58所对应的字符串(也就是p[...])加上这个字符串的第一个字符(也就是p),也就是p[...]p。事实上,这种猜测法是很准确(有点不好理解,仔细想一想吧)。上面的解码过程用伪代码表示就像下面这样:

解码器伪代码 Initialize String Table;

[code] = First Code in the CodeStream;

Output the String for [code] to the CharStream;

[old] = [code];

[code] = Next Code in the CodeStream;

while ([code] != EOF )

{

  if ( [code] is in the StringTable)

  {

    Output the String for [code] to the CharStream;

                                        // 输出[code]所对应的字符串

    [...] = translation for [old]; // [old]所对应的字符串

    k = first character of translation for [code]; // [code]所对应的字符串的第一个字符

    add [...]k to the StringTable;

    [old] = [code];

                                       

  }

  else

  {

    [...] = translation for [old];

    k = first character of [...];

    Output [...]k to CharStream;

    add [...]k to the StringTable;

    [old] = [code];

                                       

  }

  [code] = Next Code in the CodeStream;

}

 

GIF数据压缩

~~~~~~~~~~~

下面是GIF文件的图象数据结构:

 

BYTE 7 6 5 4 3 2 1 0 BIT

1 编码长度

 LZW Code Size - LZW压缩的编码长度,也就是要压缩的数据的位数

  ... 数据块

  块大小 数据块,如果需要可重复多次

  编码数据

  ... 数据块

  块终结器 一个图象的数据编码结束,固定值0

把光栅数据序列(数据流)压缩成GIF文件的图象数据(字符流)可以按下面的步骤进行:

1.定义编码长度

GIF图象数据的第一个字节就是编码长度(Code Size),这个值是指要表现一个像素所需要的最小位数,通常就等于图象的色深;

2.压缩数据

通过LZW压缩算法将图象的光栅数据流压缩成GIF的编码数据流。这里使用的LZW压缩算法是从标准的LZW压缩算法演变过来的,它们之间有如下的差别:

  [1]GIF文件定义了一个编码大小(Clear Code),这个值等于2的『编码长度』次方,在从新开始一个编译表(编译表溢出)时均须输出该值,解码器遇到该值时意味着要从新初始化一个编译表;

  [2]在一个图象的编码数据结束之前(也就是在块终结器的前面),需要输出一个Clear Code+1的值,解码器在遇到该值时就意味着GIF文件的一个图象数据流的结束;

  [3]第一个可用到的编译表索引值是Clear Code+2(从0到Clear Code-1是根索引,再上去两个不可使用,新的索引从Clare Code+2开始添加);

  [4]GIF输出的编码流是不定长的,每个编码的大小从Code Size + 1位到12位,编码的最大值就是4095(编译表需要定义的索引数就是4096),当编码所须的位数超过当前的位数时就把当前位数加1,这就需要在编码或解码时注意到编码长度的改变。

3.编译成字节序列

因为GIF输出的编码流是不定长的,这就需要把它们编译成固定的8-bit长度的字符流,编译顺序是从右往左。下面是一个具体例子:编译5位长度编码到8位字符

0 b b b a a a a a

1 d c c c c c b b

2 e e e e d d d d

3 g g f f f f f e

4 h h h h h g g g

  ...

N                

 

4.打包

  前面讲过,一个GIF的数据块的大小从0到255个字节,第一个字节是这个数据块的大小(字节数),这就需要将编译编后的码数据打包成一个或几个大小不大于255个字节的数据包。然后写入图象数据块中。

 

 

 

 

 

 

 

 

 

BMP图象格式

               

 

 

  BMP是bitmap的缩写形式,bitmap顾名思义,就是位图也即Windows位图。它一般由4部分组成:文件头信息块、图像描述信息块、颜色表(在真彩色模式无颜色表)和图像数据区组成。在系统中以BMP为扩展名保存。

  打开Windows的画图程序,在保存图像时,可以看到三个选项:2色位图(黑白)、16色位图、256色位图和24位位图。这是最普通的生成位图的工具,在这里讲解的BMP位图形式,主要就是指用画图生成的位图(当然,也可以用其它工具软件生成)。

  现在讲解BMP的4个组成部分:

1.文件头信息块

0000-0001:文件标识,为字母ASCII码“BM”。

0002-0005:文件大小。

0006-0009:保留,每字节以“00”填写。

000A-000D:记录图像数据区的起始位置。各字节的信息依次含义为:文件头信息块大小,图像描述信息块的大小,图像颜色表的大小,保留(为01)。

2.图像描述信息块

000E-0011:图像描述信息块的大小,常为28H。

0012-0015:图像宽度。

0016-0019:图像高度。

001A-001B:图像的plane总数(恒为1)。

001C-001D:记录像素的位数,很重要的数值,图像的颜色数由该值决定。

001E-0021:数据压缩方式(数值位0:不压缩;1:8位压缩;2:4位压缩)。

0022-0025:图像区数据的大小。

0026-0029:水平每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。

002A-002D:垂直每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。

002E-0031:此图像所用的颜色数,如值为0,表示所有颜色一样重要。

3.颜色表

  颜色表的大小根据所使用的颜色模式而定:2色图像为8字节;16色图像位64字节;256色图像为1024字节。其中,每4字节表示一种颜色,并以B(蓝色)、G(绿色)、R(红色)、alpha(32位位图的透明度值,一般不需要)。即首先4字节表示颜色号1的颜色,接下来表示颜色号2的颜色,依此类推。

4.图像数据区

  颜色表接下来位为位图文件的图像数据区,在此部分记录着每点像素对应的颜色号,其记录方式也随颜色模式而定,既2色图像每点占1位(8位为1字节);16色图像每点占4位(半字节);256色图像每点占8位(1字节);真彩色图像每点占24位(3字节)。所以,整个数据区的大小也会随之变化。究其规律而言,可的出如下计算公式:图像数据信息大小=(图像宽度*图像高度*记录像素的位数)/8。

  然而,未压缩的图像信息区的大小。除了真彩色模式外,其余的均大于或等于数据信息的大小。这是为什么呢?原因有两个:

  1.BMP文件记录一行图像是以字节为单位的。因此,就不存在一个字节中的数据位信息表示的点在不同的两行中。也就是说,设显示模式位16色,在每个字节分配两个点信息时,如果图像的宽度位奇数,那么最后一个像素点的信息将独占一个字节,这个字节的后4位将没有意义。接下来的一个字节将开始记录下一行的信息。

  2.为了显示的方便,除了真彩色外,其他的每中颜色模式的行字节数要用数据“00”补齐为4的整数倍。如果显示模式为16色,当图像宽为19时,存储时每行则要补充4-(19/2+1)%4=2个字节(加1是因为里面有一个像素点要独占了一字节)。如果显示模式为256色,当图像宽为19时,每行也要补充4-19%4=1个字节。

CSDN海神之光上传的代码均可运行,亲测可用,直接替换数据即可,适合小白; 1、代码压缩包内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2019b或2023b;若运行有误,根据提示修改;若不会,私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主或扫描博客文章底部QQ名片; 4.1 博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作 功率谱估计: 故障诊断分析: 雷达通信:雷达LFM、MIMO、成像、定位、干扰、检测、信号分析、脉冲压缩 滤波估计:SOC估计 目标定位:WSN定位、滤波跟踪、目标定位 生物电信号:肌电信号EMG、脑电信号EEG、心电信号ECG 通信系统:DOA估计、编码译码、变分模态分解、管道泄漏、滤波器、数字信号处理+传输+分析+去噪(CEEMDAN)、数字信号调制、误码率、信号估计、DTMF、信号检测识别融合、LEACH协议、信号检测、水声通信 1. EMD(经验模态分解,Empirical Mode Decomposition) 2. TVF-EMD(时变滤波的经验模态分解,Time-Varying Filtered Empirical Mode Decomposition) 3. EEMD(集成经验模态分解,Ensemble Empirical Mode Decomposition) 4. VMD(变分模态分解,Variational Mode Decomposition) 5. CEEMDAN(完全自适应噪声集合经验模态分解,Complementary Ensemble Empirical Mode Decomposition with Adaptive Noise) 6. LMD(局部均值分解,Local Mean Decomposition) 7. RLMD(鲁棒局部均值分解, Robust Local Mean Decomposition) 8. ITD(固有时间尺度分解,Intrinsic Time Decomposition) 9. SVMD(逐次变分模态分解,Sequential Variational Mode Decomposition) 10. ICEEMDAN(改进的完全自适应噪声集合经验模态分解,Improved Complementary Ensemble Empirical Mode Decomposition with Adaptive Noise) 11. FMD(特征模式分解,Feature Mode Decomposition) 12. REMD(鲁棒经验模态分解,Robust Empirical Mode Decomposition) 13. SGMD(辛几何模态分解,Spectral-Grouping-based Mode Decomposition) 14. RLMD(鲁棒局部均值分解,Robust Intrinsic Time Decomposition) 15. ESMD(极点对称模态分解, extreme-point symmetric mode decomposition) 16. CEEMD(互补集合经验模态分解,Complementary Ensemble Empirical Mode Decomposition) 17. SSA(奇异谱分析,Singular Spectrum Analysis) 18. SWD(群分解,Swarm Decomposition) 19. RPSEMD(再生相移正弦辅助经验模态分解,Regenerated Phase-shifted Sinusoids assisted Empirical Mode Decomposition) 20. EWT(经验小波变换,Empirical Wavelet Transform) 21. DWT(离散小波变换,Discraete wavelet transform) 22. TDD(时分解,Time Domain Decomposition) 23. MODWT(最大重叠离散小波变换,Maximal Overlap Discrete Wavelet Transform) 24. MEMD(多元经验模态分解,Multivariate Empirical Mode Decomposition) 25. MVMD(多元变分模态分解,Multivariate Variational Mode Decomposition)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值