JPEG 文件数据结构

1
JPEG 文件数据结构以及将位图保存为JPG 的代码
一、简述
JPEG 是一个压缩标准,又可分为标准JPEG、渐进式JPEG 及JPEG2000 三种:
①标准JPEG:以24 位颜色存储单个光栅图像,是与平台无关的格式,支持
最高级别的压缩,不过,这种压缩是有损耗的。此类型图片在网页下载时只能
由上而下依序显示图片,直到图片资料全部下载完毕,才能看到全貌。
②渐进式JPEG:渐进式JPG 为标准JPG 的改良格式,支持交错,可以在网
页下载时,先呈现出图片的粗略外观后,再慢慢地呈现出完整的内容,渐进式
JPG 的文件比标准JPG 的文件要来得小。
③JPEG2000:新一代的影像压缩法,压缩品质更好,其压缩率比标准JPEG
高约30%左右,同时支持有损和无损压缩。一个极其重要的特征在于它能实现
渐进传输,即先传输图像的轮廓,然后逐步传输数据,让图像由朦胧到清晰显
示。
以一幅24 位彩色图像为例,JPEG 的压缩分为四个步骤:
①颜色转换:在将彩色图像进行压缩之前,必须先对颜色模式进行数据转
换。转换完成之后还需要进行数据采样。
②DCT 变换:是将图像信号在频率域上进行变换,分离出高频和低频信息
的处理过程,然后再对图像的高频部分(即图像细节)进行压缩。首先以象素
为单位将图像划分为多个8×8 的矩阵,然后对每一个矩阵作DCT 变换。把8×
8 的象素矩阵变成8×8 的频率系数矩阵(所谓频率就是颜色改变的速度),频
率系数都是浮点数。
③量化:由于下面第四步编码过程中使用的码本都是整数,因此要对频率
系数进行量化,将之转换为整数。数据量化后,矩阵中的数据都是近似值,和
原始图像数据之间有了差异,这一差异是造成图像压缩后失真的主要原因。这
一过程中,质量因子的选取至为重要。值选得大,可以大幅度提高压缩比,但
是图像质量就比较差,质量因子越小图像重建质量越好,但是压缩比越低。
④编码:编码是基于统计特性的方法。
四个步骤都完成后的JPEG 文件,其基本数据结构为两大类型:“段”和经
过压缩编码的图像数据。
二、数据结构
2
1.段的一般结构如下表所示:
表1:段的一般结构
-----------------------------------------------------------------
名称字节数数据说明
-----------------------------------------------------------------
段标识1 FF 每个新段的开始标识
段类型1 类型编码(称作“标记码”)
段长度2 包括段内容和段长度本身,不包括段标识和段类型
段内容≤65533 字节
-----------------------------------------------------------------
说明:
①JPG 文件中所有关于宽度高度长度间隔这一类数据,凡是>1字节的,均采
用Motorola 格式,即:高位在前,低位在后。
②有些段没有长度描述也没有内容,只有段标识和段类型。文件头和文件尾均
属于这种段。
③段与段之间无论有多少FF 都是合法的,这些FF 称为“填充字节”,必须被
忽略掉。
2.段类型有30 种,但只有10 种是必须被所有程序识别的,其它的类型都
可以忽略。所以下面只列出这10 种类型。
表2:段类型
---------------------------------------
名称标记码说明
---------------------------------------
SOI D8 文件头
EOI D9 文件尾
SOF0 C0 帧开始(标准JPEG)
SOF1 C1 同上
DHT C4 定义Huffman 表(霍夫曼表)
SOS DA 扫描行开始
DQT DB 定义量化表
DRI DD 定义重新开始间隔
3
APP0 E0 定义交换格式和图像识别信息
COM FE 注释
-----------------------------------------------------------
说明:有的文章也将DNL 段(标记码=DC,定义扫描行数)列为必须段。
3.以下按一般JPEG 文件的段排列顺序详细介绍各种段的结构:
表3:SOI(文件头)
-----------------
名称字节数值
-----------------
段标识1 FF
段类型1 D8
-----------------
说明:这两个字节构成了JPEG 文件头。
表4:APP0(图像识别信息)
--------------------------------------------------------------------
------
名称字节数值说明
--------------------------------------------------------------------
------
段标识1 FF
段类型1 E0
段长度2 0010 如果有RGB 缩略图就=16+3n
(以下为段内容)
交换格式5 4A46494600 “JFIF”的ASCII 码
主版本号1
次版本号1
密度单位1 0=无单位;1=点数/英寸;2=点数/厘米
X 像素密度2 水平方向的密度
Y 像素密度2 垂直方向的密度
缩略图X 像素1 缩略图水平像素数目
4
缩略图Y 像素1 缩略图垂直像素数目
(如果“缩略图X 像素”和“缩略图Y 像素”的值均>0,那么才有下面的数据)
RGB 缩略图3×n n=缩略图像素总数=缩略图X 像素×缩略图
Y 像素
--------------------------------------------------------------------
------
说明:
①JFIF 是JPEG File Interchange Format 的缩写,即JPEG 文件交换格式,另
外还有TIFF 等格式,很少用
②“如果有RGB 缩略图就=16+3n”是什么意思呢?比如说“缩略图X 像素”
和“缩略图Y 像素”的值均为48,就表示有一个48×48 像素的缩略图(n=48
×48),缩略图是24 位真彩位图,用3个字节来表示一个像素,所以共占用3n
个字节。但大多数JPG 文件都没有这个“鸡肋”缩略图。
表5:COM(注释)
--------------------------------------------------------------------
------
名称字节数值说明
--------------------------------------------------------------------
------
段标识1 FF
段类型1 FE
段长度2 其值=注释字符的字节数+2
段内容注释字符
--------------------------------------------------------------------
------
说明:有的JPEG 文件没有这个段。
表6:DQT(定义量化表)
--------------------------------------------------------------------
------
名称字节数值说明
5
--------------------------------------------------------------------
------
段标识1 FF
段类型1 DB
段长度2 43 其值=3+n(当只有一个QT 时)
(以下为段内容)
QT 信息1 0-3 位:QT 号
4-7 位:QT 精度(0=8bit,1 字节;否则=16bit,
2 字节)
QT n n=64×QT 精度的字节数
--------------------------------------------------------------------
------
说明:
①JPEG 文件一般有2个DQT 段,为Y 值(亮度)定义1个, 为C 值(色度)定
义1个。
②一个DQT 段可以包含多个QT, 每个都有自己的信息字节
表7:SOF0(图像基本信息)
--------------------------------------------------------------------
------
名称字节数值说明
--------------------------------------------------------------------
------
段标识1 FF
段类型1 C0
段长度2 其值=8+组件数量×3
(以下为段内容)
样本精度1 8 每个样本位数(大多数软件不支持12 和16)
图片高度2
图片宽度2
组件数量1 3 1=灰度图,3=YCbCr/YIQ 彩色图,4=CMYK 彩色图
(以下每个组件占用3字节)
6
组件ID 1 1=Y, 2=Cb, 3=Cr, 4=I, 5=Q
采样系数1 0-3 位:垂直采样系数
4-7 位:水平采样系数
量化表号1
--------------------------------------------------------------------
-------
说明:
①JPEG 大都采用yCrCb 色彩模型(y 表示亮度,Cr 红色分量,Cb 表示蓝色分量),
所以组件数量一般=3
②样本就是单个像素的颜色分量,也可理解为一个样本就是一个组件
③采样系数是实际采样方式与最高采样系数之比,而最高采样系数一般=0.5
(分数表示为1/2)。比如说,垂直采样系数=2,那么2×0.5=1,表示实际
采样方式是每个点采一个样,也就是逐点采样;如果垂直采样系数=1,那么:
1×0.5=0.5(分数表示为1/2),表示每2个点采一个样
表8:DHT(定义Huffman 表)
--------------------------------------------------------------------
------
名称字节数值说明
--------------------------------------------------------------------
------
段标识1 FF
段类型1 C4
段长度2 其值=19+n(当只有一个HT 表时)
(以下为段内容)
HT 信息1 0-3 位:HT 号
4 位: HT 类型, 0=DC 表,1=AC 表
5-7 位:必须=0
HT 位表16 这16 个数的和应该≤256
HT 值表n n=表头16 个数的和
--------------------------------------------------------------------
------
7
说明:
①JPEG 文件里有2类Haffman 表:一类用于DC(直流量),一类用于AC(交
流量)。一般有4个表:亮度的DC 和AC,色度的DC 和AC。最多可有6个。
②一个DHT 段可以包含多个HT 表, 每个都有自己的信息字节
③HT 表是一个按递增次序代码长度排列的符号表。
表9:DRI(定义重新开始间隔)
--------------------------------------------------------------------
------
名称字节数值说明
--------------------------------------------------------------------
------
段标识1 FF
段类型1 DD
段长度2 4
(以下为段内容)
开始间隔2 n 复位标记的间隔距离
--------------------------------------------------------------------
-------
说明:
①开始间隔表示在压缩数据流中,每隔n个MCU 块就有一个RST 标记,RST 标
记将Huffman 的解码数据流复位,DC 也重新从0开始,因此,RST 标记是一种
复位标记
②RST 标记是一种特殊的段,它只具有段标识和段类型(长度=2字节),但
它不是独立的段,只能穿插在数据流中(文件头和文件尾段也只有段标识和段
类型,却都是独立的段)。
③RST 标记共有8个(RST0-RST7),从RST0 起开始使用,然后是RST1....直
至RST7,再从RST0 重复。
④RST 标记的标识码是FFD0-FFD7,对应RST0-RST7
⑤笔者查看了许多自己电脑上的JPG 文件的数据,未发现DRI 段和RST 标记,
所以无法进行详尽的考察
8
表10:SOS(扫描行开始)
--------------------------------------------------------------------
------
名称字节数值说明
--------------------------------------------------------------------
------
段标识1 FF
段类型1 DA
段长度2 000C 其值=6+2×扫描行内组件数量
(以下为段内容)
扫描行内组件数量1 3 必须≥1,≤4(否则错误),通常=3
(以下每个组件占用2字节)
组件ID 1 1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q
Huffman 表号1 0-3 位:AC 表号(其值=0...3)
4-7 位:DC 表号(其值=0...3)
3 最后3个字节用途不明,忽略
--------------------------------------------------------------------
------
说明:紧接SOS 段后的是压缩的图像数据(一个个扫描行),数据存放顺
序是从左到右、从上到下。
表11:EOI(文件尾)
------------------
名称字节数值
------------------
段标识1 FF
段类型1 D9
------------------
说明:这两个字节构成了JPEG 文件尾。
三、实例分析
9
用系统自带的画图程序画一个32×24 的红色方块,另存为JPG 文件,这个
文件的全部数据如下:
0000: FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 60
0010: 00 60 00 00 FF DB 00 43 00 08 06 06 07 06 05 08
0020: 07 07 07 09 09 08 0A 0C 14 0D 0C 0B 0B 0C 19 12
0030: 13 0F 14 1D 1A 1F 1E 1D 1A 1C 1C 20 24 2E 27 20
0040: 22 2C 23 1C 1C 28 37 29 2C 30 31 34 34 34 1F 27
0050: 39 3D 38 32 3C 2E 33 34 32 FF DB 00 43 01 09 09
0060: 09 0C 0B 0C 18 0D 0D 18 32 21 1C 21 32 32 32 32
0070: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
0080: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
0090: 32 32 32 32 32 32 32 32 32 32 32 32 32 32 FF C0
00A0: 00 11 08 00 18 00 20 03 01 22 00 02 11 01 03 11
00B0: 01 FF C4 00 1F 00 00 01 05 01 01 01 01 01 01 00
00C0: 00 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09
00D0: 0A 0B FF C4 00 B5 10 00 02 01 03 03 02 04 03 05
00E0: 05 04 04 00 00 01 7D 01 02 03 00 04 11 05 12 21
00F0: 31 41 06 13 51 61 07 22 71 14 32 81 91 A1 08 23
0100: 42 B1 C1 15 52 D1 F0 24 33 62 72 82 09 0A 16 17
0110: 18 19 1A 25 26 27 28 29 2A 34 35 36 37 38 39 3A
0120: 43 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A
0130: 63 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A
0140: 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99
0150: 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7
0160: B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5
0170: D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 E8 E9 EA F1
0180: F2 F3 F4 F5 F6 F7 F8 F9 FA FF C4 00 1F 01 00 03
0190: 01 01 01 01 01 01 01 01 01 00 00 00 00 00 00 01
01A0: 02 03 04 05 06 07 08 09 0A 0B FF C4 00 B5 11 00
01B0: 02 01 02 04 04 03 04 07 05 04 04 00 01 02 77 00
01C0: 01 02 03 11 04 05 21 31 06 12 41 51 07 61 71 13
10
01D0: 22 32 81 08 14 42 91 A1 B1 C1 09 23 33 52 F0 15
01E0: 62 72 D1 0A 16 24 34 E1 25 F1 17 18 19 1A 26 27
01F0: 28 29 2A 35 36 37 38 39 3A 43 44 45 46 47 48 49
0200: 4A 53 54 55 56 57 58 59 5A 63 64 65 66 67 68 69
0210: 6A 73 74 75 76 77 78 79 7A 82 83 84 85 86 87 88
0220: 89 8A 92 93 94 95 96 97 98 99 9A A2 A3 A4 A5 A6
0230: A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 C4
0240: C5 C6 C7 C8 C9 CA D2 D3 D4 D5 D6 D7 D8 D9 DA E2
0250: E3 E4 E5 E6 E7 E8 E9 EA F2 F3 F4 F5 F6 F7 F8 F9
0260: FA FF DA 00 0C 03 01 00 02 11 03 11 00 3F 00 E2
0270: E8 A2 8A F9 93 F7 10 A2 8A 28 00 A2 8A 28 00 A2
0280: 8A 28 03 FF D9
分析:
0000-0001:FF D8(文件头)
0002-0013:APP0 段,其中:
0002-0003:FF E0(段标记)
0004-0005:00 10(段长度=16 字节)
0006-000A:4A 46 49 46 00(“JFIF”的ASCII 码)
000B-000C:01 01(主次版本号=1.01)
000D :01(密度单位为点数/英寸)
000E-0011:00 60 00 60(水平和垂直的密度均为每英寸96 像素)
0012-0013:00 00(没有缩略图)
0014-0058:DQT 段,其中:
0014-0015:FF DB(段标记)
0016-0017:00 43(段长度=67 字节)
0018 :00(QT 号=0,QT 精度=8bit)
0019-0058:QT
0059-009D:DQT 段,其中:
0059-005A:FF DB(段标记)
005B-005C:00 43(段长度=67 字节)
005D :01(QT 号=1,QT 精度=8bit)
11
005E-009D:QT
009E-00B0:SOF0 段,其中:
009E-009F:FF C0(段标记)
00A0-00A1:00 11(段长度=17 字节)
00A2 :08(样本精度=8bit)
00A3-00A6:00 18 00 20(图像高=24,图像宽=32)
00A7 :03(组件数量=3,YCbCr 彩色图)
00A8 :01(ID 号=1,这是亮度Y 组件)
00A9 :22(化为二进制=00100010,表明垂直和水平采样系数均=2)
00AA :00(使用QT 号=0 的量化表)
00AB :02(ID 号=2,这是色度Cb 组件)
00AC :11(化为二进制=00010001,表明垂直和水平采样系数均=1)
00AD :01(使用QT 号=1 的量化表)
00AE :03(ID 号=3,这是色度Cr 组件)
00AF :11(化为二进制=00010001,表明垂直和水平采样系数均=1)
00B0 :01(使用QT 号=1 的量化表)
00B1-00D1:DHT 段,其中:
00B1-00B2:FF C4(段标记)
00B3-00B4:00 1F(段长度=31 字节)
00B5 :00(HT 号=0,DC 表)
00B6-00C5:这16 个数字之和=12,说明下面的HT 值表的长度=12 字节)
00C6-00D1:HT 值表
00D2-0188:DHT 段,这是HT 号=0 的AC 表,参照上面分析
0189-01A9:DHT 段,这是HT 号=1 的DC 表,参照上面分析
01AA-0260:DHT 段,这是HT 号=1 的AC 表,参照上面分析
0261-026E:SOS 段,其中:
0261-0262:FF DA(段标记)
0263-0264:00 0C(段长度=12 字节)
0265 :03(组件数量=3)
0266-0267:01 00(组件ID=1,Y 组件,使用HT 表号=0 的AC 表)
0268-0269:02 11(组件ID=2,Cb 组件,使用HT 表号=1 的DC 表)
026A-026B:03 11(组件ID=3,Cr 组件,使用HT 表号=1 的DC 表)
12
026C-026E:00 3F 00(几乎每个JPG 文件中这3个字节的值都相同,但用
途不明)
026F-0282:图像压缩数据
0283-0284:FF D9(文件尾)
四、将位图保存为JPG 的函数代码:
Option Explicit
Private Declare Function CLSIDFromString Lib "ole32" (ByVal str As Long,
id As GUID) As Long
Private Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory"
(lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
Private Declare Function GdipCreateBitmapFromHBITMAP Lib "gdiplus"
(ByVal hbm As Long, ByVal hpal As Long, Bitmap As Long) As Long
Private Declare Function GdiplusStartup Lib "gdiplus.dll" (ByRef token
As Long, ByRef inputX As GdiplusStartupInput, ByVal Output As Long) As
Long
Private Declare Function GdipSaveImageToFile Lib "gdiplus" (ByVal Image
As Long, ByVal Filename As Long, clsidEncoder As GUID, encoderParams As
Any) As Long
Private Declare Function GdipDisposeImage Lib "gdiplus" (ByVal Image As
Long) As Long
Private Declare Function GdiplusShutdown Lib "gdiplus.dll" (ByVal token
As Long) As Long
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type
Private Type GdiplusStartupInput
13
GdiplusVersion As Long
DebugEventCallback As Long
SuppressBackgroundThread As Long
SuppressExternalCodecs As Long
End Type
Private Type EncoderParameter
GUID As GUID
NumberOfvalues As Long
type As Long
Value As Long
End Type
Private Type EncoderParameters
Count As Long
Parameter As EncoderParameter
End Type
'输入参数:必需的:1.对象;2.要保存的全路径文件名;可选的:3.jpg 品质
因素
Private Function SaveJpg(ByVal pict As StdPicture, ByVal Filename As
String, Optional ByVal JPG_Quality As Byte = 80) As Boolean
On Error GoTo 100
Dim lGDIP As Long
Dim lBitmap As Long
Dim aEncParams() As Byte
Dim udtData As GdiplusStartupInput
udtData.GdiplusVersion = 1 'GDI+初始化
If GdiplusStartup(lGDIP, udtData, 0) = 0 Then
If GdipCreateBitmapFromHBITMAP(pict.Handle, 0, lBitmap) = 0 Then '
从句柄创建GDI+ 图像
Dim tJpgEncoder As GUID
Dim tParams As EncoderParameters
14
CLSIDFromString StrPtr("{557CF401-1A04-11D3-9A73-0000F81EF32E}"),
tJpgEncoder '初始化解码器的GUID 标识
tParams.Count = 1 '设置解码器参数
With tParams.Parameter '设置品质因数
CLSIDFromString
StrPtr("{1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}"), .GUID ' 获取
JPG_Quality 参数的GUID 标识
.NumberOfvalues = 1
.type = 4
.Value = VarPtr(JPG_Quality)
End With
ReDim aEncParams(1 To Len(tParams))
Call CopyMemory(aEncParams(1), tParams, Len(tParams))
GdipSaveImageToFile lBitmap, StrPtr(Filename), tJpgEncoder,
aEncParams(1) '保存图像
GdipDisposeImage lBitmap '销毁GDI+图像
GdiplusShutdown lGDIP
Erase aEncParams
SavePic = True
End If
End If
Exit Function
100
End Function

你可能感兴趣的:(JPEG 文件数据结构)