本文完全原创,参考文献见文末,未经本人允许,禁止以任何形式转载全部或部分内容(包括图片、表格等,文中引用的第三方图片除外),经本人允许后转载必须添加本文原始链接并注明转载。
本文内容较长,大约两万字,内容可能比较枯燥,建议在需要深度了解图片格式的情况下再阅读。本文内容基于标准规范编写,以标准表述为准,因此与大部分教程不同,可以认为是《最全JPEG格式解析》《标准JPEG格式完全解读》《最正确的JPEG格式解析》。
在搜索JPEG格式时,看到的所有文章几乎都是在描述EXIF格式中的压缩图像数据存储格式,而没有对JPEG格式进行完整、准确的描述,并且也没有指出这一点,为了准确了解JPEG格式,本文就JPEG规范、JFIF规范、TIFF规范以及EXIF规范做了详细研究,并就这几种格式进行了详细的描述。
jpeg、jpg格式是常见的图片文件格式,实际上jpg和jpeg是同一种格式,类似htm和html的区别,下文以jpg称呼。
JPEG有着不同的概念。首先,JPEG是第一个国际图像压缩标准,在这个标准中只定义了如何处理图像数据,而没有定义具体文件格式;其次,JPEG也是一种文件格式或者说交换格式,它是在JPEG标准的附录中定义的,描述了如何存储符合JPEG标准的图像的相关数据作为图像交换文件。有些人认为JPEG只是一个压缩标准而非文件格式就是因为这两个不同的概念。本文认为,根据《ISO/IEC 10918-1:1994》和《ITU Recommendation T.81》(根据T.81规范前言,二者实际内容一致)1,JPEG既是一种图像处理和压缩标准,也是一种文件格式。本文只讨论文件交换格式,不涉及图像处理相关内容,后文所称JPEG标准格式或标准JPEG格式均指的是T.81规范或ISO/IEC 1091-1规则附录B中描述的JPEG交换格式。
为了更好地存储JPEG标准图像,基于标准规范,产生了其它不同的实现规范,例如,最常用的JFIF(JPEG File Interchange Format)格式和EXIF(Exchangeable Image File Format)格式等。目前,几乎所有的jpg图片都采用EXIF格式或JFIF格式,虽然严格意义上讲二者并不兼容,但实际上很多图片创建程序为了兼容性采取了折中的方案,后文会提到。要注意的是,JFIF格式和EXIF格式都是基于JPEG标准交换格式的,它们只是在JPEG标准交换格式之上做了一些扩展定义和简化。
形象一点的描述,一个后缀为jpg的文件,内部其实可以采用不同的实现规范,既可以使用JFIF格式也可以使用EXIF格式,不同的文件格式最终都存储了符合JPEG标准的图像数据。
JPEG标准格式
标识定义
在JPEG标准交换格式中,用到了许多标识,这些标识代表了不同的含义。每个标识都占2个字节,第一个字节固定为0xFF,第二个字节则不能为0也不能为0xFF。大部分标识都表示一个标识段的开始,在标识之后往往会跟随两个字节的标识段长度,段长的单位是字节(Byte),这个标识段长度表明了该段除标识外其余部分的长度,包括段长自身。段格式示意图如下:
规范中定义的标识如下表,其中,带“*”号的标识意味着这是一个独立标识,其后不跟任何标识段:
标记代码 | 符号表示 | 描述 |
帧开始标记、非差分、霍夫曼编码 | ||
0xFFC0 | SOF0 | 基准 DCT(Baseline DCT) |
0xFFC1 | SOF1 | 扩展顺序 DCT(Extended sequential DCT) |
0xFFC2 | SOF2 | 渐进式DCT(Progressive DCT) |
0xFFC3 | SOF3 | 无损(顺序)(Lossless (sequential)) |
帧开始标记、差分、霍夫曼编码 | ||
0xFFC5 | SOF5 | 差分顺序DCT(Differential sequential DCT) |
0xFFC6 | SOF6 | 差分渐进式DCT(Differential progressive DCT) |
0xFFC7 | SOF7 | 差分无损(顺序)(Differential lossless (sequential)) |
帧开始标记、非差分、算术编码 | ||
0xFFC8 | JPG | 预留给 JPEG 扩展 |
0xFFC9 | SOF9 | 扩展顺序 DCT |
0xFFCA | SOF10 | 渐进式DCT |
0xFFCB | SOF11 | 无损(顺序) |
帧开始标记、差分、算术编码 | ||
0xFFCD | SOF13 | 差分顺序DCT |
0xFFCE | SOF14 | 差分渐进式DCT |
0xFFCF | SOF15 | 差分无损(顺序) |
哈夫曼表规范 | ||
0xFFC4 | DHT | 定义哈夫曼表 |
算术编码条件规范 | ||
0xFFCC | DAC | 定义算术编码条件 |
重启间隔终止 | ||
0xFFD0 – 0xFFD7 | RSTm* | 以对“m”模8重新启动 |
其它标识 | ||
0xFFD8 | SOI* | 图像开始 |
0xFFD9 | EOI* | 图像结束 |
0xFFDA | SOS | 扫描开始 |
0xFFDB | DQT | 定义量化表 |
0xFFDC | DNL | 定义行数 |
0xFFDD | DRI | 定义重新启动间隔 |
0xFFDE | DHP | 定义分层递进 |
0xFFDF | EXP | 扩展参考组件 |
0xFFE0 – 0xFFEF | APPn | 预留给应用程序段 |
0xFFF0 – 0xFFFD | JPGn | 预留给 JPEG 扩展 |
0xFFFE | COM | 注释 |
保留标识 | ||
0xFF01 | TEM* | 供算术编码中临时私人使用 |
0xFF02 – 0xFFBF | RES | 保留 |
格式定义
JPEG标准交换格式概览如下图所示,每个JPEG文件都由SOI标记开始,以EOI标记结尾,中间则是一个Frame。
每个帧(Frame)中,首先以0或多个表规范或其他标记段(Table/misc.)开始,然后跟随一个帧头部(Frame Header),紧接着是一个或多个扫描段(Scan)。如果有DNL标识段,则必须跟随在第一个扫描段(Scan)后。
至于表规范和其他标记段,我们放到后面再说。
帧头部格式如下图所示:
帧头部其实是一个标识段,它由标识、段长(Lf)、采样精度(P)、图像最大行数(Y)、图像最大列数(X)、帧中图像组件的数量(Nf)以及一个或多个帧组件规格参数集(component specification parameter)构成。其中,Nf的值必须等于帧组件规格参数集的个数。每个帧组件规格参数集包含了组件标识符(C)、水平采样系数(H)、垂直采样系数(V)和量化表目标选择器(T)。
扫描段格式如下图所示:
和帧一样,每个扫描段最开始都可以跟0或多个表规范或其他标记段(Table/misc.),接着就是一个头部(Scan Header),再然后就是一个或多个ECS(entropy-coded segments)。如果只有一个ECS,则不应该出现RST,如果有多个ECS,则除第一个ECS外,后续每个ECS前必须有一个RST,ECS的数量由图像大小和定义的重新启动间隔(DRI)决定。图像的实际数据就存放在ECS中。
和帧头部一样,扫描头部也是一个标识段,同样以SOS标识开始,接着是段长(Ls)、扫描中图像组件的数量(Ns)、一个或多个扫描组件规格参数(component-specification parameters)、频谱或预测器选择的起始点(Ss)、频谱选择结束点(Se)、逐次逼近高位比特位置(Ah)、逐次逼近低位比特位置或点变换(Al)。这些参数与实际编码解码有关。每个扫描组件规格参数包含了扫描组件选择器(Cs)、DC熵编码表目标选择器(Td)、AC熵编码表目标选择器(Ta)。
表规范和其他标记段定义
表规范和其他标记段(Table-specification and miscellaneous marker segment)主要分为量化表规范(Quantization table-specification)、哈夫曼表规范(Huffman table-specification)、算术条件表规范(Arithmetic conditioning table-specification)、重启间隔定义(Restart interval definition)、注释(Comment)以及应用数据(Application data)。
量化表规范定义
量化表标识段格式定义如下:
量化表标识段以DQT标识开头,接段长(Lq),随后是一个或多个量化表(QT)。每个量化表中顺次为量化表元素精度(Pq)、量化表目标标识符(Tq)、64个量化表元素(Q)。需要说明的是,每个量化表元素的大小可以是8位也可以是16位,由量化表元素精度(Pq)决定,当Pq=0时,Q为8位;当Pq=1时,Q为16位。
哈夫曼表规范定义
哈夫曼表标识段格式定义如下:
哈夫曼表标识段以DHT标识开头,接段长(Lh),后接一个或多个哈夫曼表(HT)。每个哈夫曼表中,顺次为表类型(Tc)、哈夫曼表目标标识符(Th)、长度为 i 的霍夫曼码数量(Li)、符号长度分配表(Symbol-length assignment)。符号长度分配表中则是每个哈夫曼码相关的值(V),其中V的数量等于所有Li值的和,并且按照Li的顺序依次排列,若某个Li值为0则没有对应的V出现。例如,L1=0,L2=2,L3到L15均为0,L16=1,则符号长度分配表中有 V2,1、V2,2、V16,1 三个值。
算术条件表规范定义
算术条件表标识段格式定义如下图所示:
算术条件表标识段以DAC标识开头,接段长(La),后接一个或多个算术条件表(AC)。每个算术条件表包含了表类型(Tc)、算术条件编码表目标标识符(Tb)以及条件表值(Cs)。
重新启动间隔定义
重新启动间隔定义标识段格式定义如下图所示:
重新启动间隔定义标识段以DRI标识开头,接段长(Lr),后接重启间隔(Ri)。
注释
注释标识段格式定义如下图所示:
注释标识段格式以COM标识开头,接段长(Lc),后接一个或多个注释内容字节(Cm)。注释内容字节的数量=Lc-2。
应用数据
应用数据标识段格式定义如下:
应用数据标识段以APPn标识开头,接段长(Lp),后接一个或多个应用数据内容字节(Ap)。应用数据内容字节的数量=Lc-2。
简化理解
标准定义中的格式层级多且较为复杂,但仔细观察就能发现,整个JPEG格式物理上其实就是由开始标识、一个或多个由N个标识段和图像压缩数据组成的部分、结束标识组成的。
首先,在帧中,帧头部之前,有0或多个表规范和其他标记段,这些表规范和其它标识段都是一个个的标识段。帧头部自身也是一个标识段。帧头部后第一个扫描段后可能出现的DNL段是一个标识段。对每个扫描段而言,首先是0或多个多个表规范和其他标记段,接着是扫描头部,它也是一个标识段,最后是扫描段中包含的图像压缩数据ECS。多个ECS之间的RST也是一个标识段。这样梳理下来,JPEG中所有的数据都只能是不带标识段的标识符、从标识符和段长开始的标识段、图像压缩数据这三种形式之一。
若我们简化整个图片的构成,假设图片只含有一个扫描段,扫描段中的图像压缩数据仅由一个ECS构成,则整个图片构成如下:
在简化的前提下,可以认为一个JPEG文件就是由SOI、多个段、图像压缩数据、EOI组成,后文所述的JFIF和EXIF格式均是基于这种情况下对JPEG进行扩展和简化的。目前各种教程中讲述的JPEG文件格式大多描述的都是此情况。
JFIF格式
JFIF格式于1991年提出,最新版本1.02于1992年提出,此后再无发展,后于2009年被ECMA标准化为《ECMA TR/98》2,又于2011年被标准化为《Recommendation ITU-T T.871》和《ISO/IEC 10918-5》(二者内容相同)3。
JFIF格式完全兼容于标准JPEG格式,它只在标准JPEG格式基础上增设了一些限制,用于功能增强4。具体来说,JFIF格式要求在SOI标识后,紧跟一个APP0标识段(本文简称JFIF段):
该标识段作为JFIF格式的标志,定义如下(它当然也符合一个标识JPEG格式中的标识段要求):
JFIF格式标志APP0段由APP0标识符开头,紧跟段长(Lp),接着是一个ASCII码字符串标记(Id),该标记内容固定为0x4A46494600,即以ASCII码0结尾的字符串“JFIF”。在标记之后,是版本号(Ver)、X和Y的单位(Units)、水平像素密度(Xd)、垂直像素密度(Yd)、缩略图水平像素数(Xt)、缩略图垂直像素数(Yt)、缩略图各像素的打包RGB值(RGBn,其中n = Xt * Yt)。
在标志APP0段后,可跟随一个JFIF扩展APP0标识段,主要用于存放缩略图。它的格式定义如下:
缩略图APP0段(本文简称为JFXX段)也是由APP0标识符(APP0)起始,紧跟段长(Lp),随后是一个ASCII码字符串标记(Id),该标记内容固定为0x4A46585800,即以ASCII码0结尾的字符串“JFXX”。在标记之后,是扩展编号(Extc),最后是长度不固定的扩展数据(Extd),Extd的长度虽然不固定,但是它的长度也计入段长,意味着可以通过段长和其它部分长度计算得出。
Extd的内容由Extc的内容决定,当Extc=0x10时,表示使用标准JPEG格式存储缩略图,但有额外的限制是缩略图中不能再出现JFIF段和JFXX段;当Extc=0x11时,表示缩略图每个像素都使用一个字节存储,具体的颜色有调色盘(Pa)决定,具体来说,此时Extd的格式为缩略图水平像素数(Xt)、缩略图垂直像素数(Yt)、调色盘(Pa)、缩略图中的各个像素值(Pxn,其中n = Xt * Yt);当Extc=0x13时,表示缩略图每个像素都使用三个字节的RGB值存储,此时Extd的格式为缩略图水平像素数(Xt)、缩略图垂直像素数(Yt)、缩略图各像素的打包RGB值(RGBn,其中n = Xt * Yt)。
最后,JFIF允许自定义扩展APP0段,格式如下:
每个自定义的JFIF扩展APP0段必须以APP0标识符起始,紧跟段长(Lp),之后是标记(Id),再之后可以存放自定义的数据(Data)。标记(Id)必须是一个以ASCII码0结尾的ASCII字符串,长度没有要求,但该字符串不能为“JFIF”和“JFXX”。此外,自定义JFIF扩展APP0段必须放置在JFIF段和JFXX段之后,但不要求放在其他段之前,并且需要遵守标准JPEG格式规范。
TIFF格式
在了解EXIF格式之前,我们先来了解一下另外一个图片存储格式TIFF(Tag Image File Format)5,EXIF格式既用到了标准JPEG格式也用到了TIFF格式,所以有必要先了解TIFF格式。1986年,TIFF格式由Aldus公司创立,主要用于扫描仪和印刷出版物,在1992年发布了最后一个大版本6.06。此后,Aldus公司于1994年被Adobe公司收购,TIFF格式也由Adobe继续维护7。TIFF格式后续也发展出了一些变体,例如TIFF/EP (ISO 12234-2)、TIFF/IT (ISO 12639)、TIFF-F (RFC 2306)、TIFF-FX (RFC 3949)。TIFF标准格式自身并没有被标准化,它目前仍维持在6.0版本,一般被称为“TIFF, Revision 6.0 Final – June 3, 1992”。
TIFF实际上是由一系列的图像文件目录(Image File Directory,IFD)组成的,这些目录以链表的形式连接起来,并有一个链表头(TIFF Header)作为所有IFD的起点。每个IFD中都存放了不同数量的目录表项(Directory Entry,DE,或称IFD Entry),每个DE都存放了某种数据或是某种数据在整个文件中相对文件起始处的字节偏移量。从概念的角度上讲,每个IFD均可定义一幅图像,所以一个TIFF文件可以存放多个图像。
在本文TIFF格式一节中所说的字节偏移量均指相对于 TIFF 文件开头的位置,TIFF文件的第一个字节的偏移量为 0。
如下图所示:
接下来我们详细介绍每个部分。
TIFF头部
TIFF格式不像标准JPEG格式一样有开始标记和结束标记,它只有一个文件头:
文件头以两个字节的标记(Byte Order)开始,这两个字节用于标识整个文件的各段数据字节顺序是采用大端模式还是小端模式。小端模式数据数据低位在前,高位在后;大端模式数据高位在前,低位在后。当这两个字节的值为0x4949时,文件以小端模式存储,当这两个字节的值为0x4D4D时,文件以大端模式存储。在字节序标志之后,是一个固定的值0x2A,它占据两个字节,并以字节序标志标定的字节序存放在这两个字节中。接着就是一个表示第一个IFD字节偏移量的段,这里称之为PIFD,它占据4个字节大小。如果TIFF头后面紧跟着第0个IFD,则它的值就是0x8。
IFD(Image File Directory)
IFD的具体格式如下图所示:
每个IFD存放了多个表项DE,因而IFD的一个段nDE表示该目录中的DE数量,紧接着就是nDE个DE,每个DE长度为12字节。在DE部分之后,则是存放了下一个IFD的字节偏移量的段PIFD,根据该字段就可以找到下一个IFD的位置。两个IFD之间可能没有其它数据,也可能有任意长度的数据,取决于TIFF文件创建者的做法。TIFF中最后一个IFD的PIFD字段必须填写4字节的0,表明这是最后一个IFD,不能为其它值也不能舍弃该字段。
DE(IFD Entry)
每个目录表项DE的具体格式如下图所示:
DE的长度固定为12字节,它依次包含DE标志(Tag)、所存放数据的类型(Type)、所存放数据的数量(nV)、数据字段(Field)。不同的DE标志表明了该DE是存放什么数据的,以供编辑器或照片查看器辨认不同的DE。而数据类型和数据数量共同表示数据的长度以及数据的具体格式,假设存放了三个C语言中的有符号整数int,假设int占4个字节,则对应的TIFF DE数据类型为“SLONG”,对应的数据数量为3,该DE存放的数据总字节长度为12个字节。DE中数据字段的长度固定为4字节,无法存放这么多数据,所以需要在其它地方存放,于是将具体存放数据起始处的字节偏移量写入该DE的数据字段中,至于要存放在哪里我们后文再详述。在上述条件下,假设只存放了一个int,则对应的数据数量为1,数据总长度为4字节,数据字段刚好够放下,于是数据字段中就直接写入这个值,而无需再在其它地方存放。若存储的数据足够短,不足4字节,则数据从数据字段最左侧开始存放,后续的空间规范并未指出如何填充,但个人认为一般填充为0较为稳妥。
关于TIFF DE支持的数据类型和对应的数据类型单位长度如下表所示:
值 | 名称 | 长度(单位:bit位) | 描述 |
0x1 | BYTE | 8 | 8位无符号整数 |
0x2 | ASCII | 8 | 七位ASCII码值,整个字符串最后应该以字节NUL(二进制0)结尾 |
0x3 | SHORT | 16 | 16位无符号整数 |
0x4 | LONG | 32 | 32位无符号整数 |
0x5 | RATIONAL | 64 | 分子LONG+分母LONG表示的有理数 |
0x6 | SBYTE | 8 | 8位有符号整数 |
0x7 | UNDEFINED | 8 | 8位数据,内容自定 |
0x8 | SSHORT | 16 | 16位有符号整数 |
0x9 | SLONG | 32 | 32位有符号整数 |
0xA | SRATIONAL | 64 | 分子SLONG+分母SLONG表示的有理数 |
0xB | FLOAT | 32 | IEEE单精度浮点数 |
0xC | DOUBLE | 64 | IEEE双精度浮点数 |
需要说明的是,在描述以ASCII码存放的字符串数据时,数据数量按照实际的字符数+NUL数量计算,若存放的字符串长度短,需要在后边填充0,则填充的0不计入。一个存放ASCII字符串数据的DE可能同时存放了多个字符串,尽管并不推荐这样做,但在该情况下,相邻的字符串之间也必须以一个NUL字节分隔开,并且最多也只能用一个NUL分隔相邻的字符串。
所有的数据存放都遵循TIFF Header中标识的字节序。
在DE的数据字段存放的是数据的字节偏移量这种情况下,数据具体存放在哪里规范中并没有定义,但是通行的做法是将数据存放在DE所在IFD的后边,下一个IFD的前面。
假设IFD的字节偏移量为A,其中存放了两个DE,分别存放了3个LONG和2个LONG,则IFD的PIFD字段结束后,紧跟着就是12个字节存放第一个DE的3个LONG,再紧跟着是8个字节存放第二个DE的2个LONG。第一个DE的数据字段值为(A+30),第二个DE的数据字段值为(A+30+12)。
对一个DE中以字节偏移量形式存放的数据来说,无论该数据实际上有没有单独存放、无论字节偏移量指向的是哪个地方,均无条件认为指向的地方就是存放该数据的地方,所以理论上不同的DE可以指向相同的区域、相同的数据,甚至还可以直接指向另一个IFD,这些都是可行的,只要该文件的创建程序和处理程序能正确处理这些情况下可能产生问题即可。
预定义的DE块标识
DE的标志使得每个DE存放的数据都能有一定的意义,例如,标志为0x100的DE存放的数据表示的是图像的宽度,标志为0x101的DE存放的数据表示的是图像的高度,标志为0x110的DE存放的数据表示的是图像生成的设备型号,标志为0x10F的DE存放的数据表示的是图像生成的设备制作厂商,而标志为0x132的DE存放的数据表示的是图像变更时的日期和时间。TIFF中定义了大量的DE标志,具体可以参考TIFF规范8。下面给出一些常用的DE标志:
标签名称 | Tag ID(Hex) | 类型 | 数量 | 说明 |
图像数据相关标签 | ||||
ImageWidth | 100 | SHORT、LONG | 1 | 图像宽度 |
ImageLength | 101 | SHORT、LONG | 1 | 图像高度 |
XResolution | 11A | RATIONAL | 1 | 宽度方向每个分辨率单位的像素数 |
YResolution | 11B | RATIONAL | 1 | 高度方向每个分辨率单位的像素数 |
ResolutionUnit | 128 | SHORT | 1 | 分辨率单位 |
Compression | 103 | SHORT | 1 | 压缩方案 |
Orientation | 112 | SHORT | 1 | 图像相对于行和列的方向 |
SamplesPerPixel | 115 | SHORT | 1 | 每像素分量数 |
BitsPerSample | 102 | SHORT | SamplesPerPixel | 每分量数的位数 |
PhotometricInterpretation | 106 | SHORT | 1 | 图像数据的色彩空间 |
PlanarConfiguration | 11C | SHORT | 1 | 每个像素的分量是如何存储的 |
其它信息标签 | ||||
ImageDescription | 10E | ASCII | 图像描述 | |
Make | 10F | ASCII | 图像创建设备制造商 | |
Model | 110 | ASCII | 图像创建设备型号 | |
Software | 131 | ASCII | 用于创建图像的软件包名称和版本号 | |
DateTime | 132 | ASCII | 20 | 图像创建时间,内容格式为“YYYY:MM:DD HH:MM:SS”,24小时制 |
Artist | 13B | ASCII | 图像创建者 | |
Copyright | 8298 | ASCII | 版权提示 |
Resolution相关的标志是为了指示图像实际大小的,一般我们讨论图像的大小都是在讨论图像的像素宽高,但传统的照片图像是由相机拍摄产生的,因而这些图像还有物理大小,为了描述逻辑尺寸与物理尺寸,就引入了这些标志。默认的ResolutionUnit值为1,表示分辨率单位为英尺,则在宽度方向上1英尺对应了XResolution个像素。其它ResolutionUnit的可能值为1(单位是厘米)、0(没有绝对的计量单位)。
Compression是用于描述存放的图像是采用什么压缩方案存放的,涉及到具体图像的编解码,这里不做具体描述。
SamplesPerPixel是用于描述每个像素需要用多少个分量表示的,例如RGB像素需要3个分量,黑白图需要1个(实际黑白图不会用到这个标志,因为要么黑要么白不需要额外描述需要多少分量)、RGBA需要4个。而BitsPerSample则描述了每个分量需要使用多少bit来存放。除此之外,还有一个ExtraSamples用来描述额外的分量是什么,而PhotometricInterpretation、PlanarConfiguration也与这些值有关,具体参看规范,此处仅简单介绍这些参数的大致作用。
Orientation表示图像的方向,它的值有如下几种情况:
- 0:第 0 行代表图像的视觉顶部,第 0 列代表视觉左侧
- 2:第 0 行代表图像的视觉顶部,第 0 列代表视觉右侧
- 3:第 0 行代表图像的视觉底部,第 0 列代表视觉右侧
- 4:第 0 行代表图像的视觉底部,第 0 列代表视觉左侧
- 5:第 0 行代表图像的视觉左侧,第 0 列代表视觉顶部
- 6:第 0 行代表图像的视觉右侧,第 0 列代表视觉顶部
- 7:第 0 行代表图像的视觉右侧,第 0 列代表视觉底部
- 8:第 0 行代表图像的视觉左侧,第 0 列代表视觉底部
此外,TIFF的JPEG扩展支持JPEG标准(注意这里说的是JPEG图像压缩标准,而非文件格式)的图片,该扩展新增了一系列与JPEG有关的标志,用于存放JPEG压缩过程中的各种信息,如量化表、哈夫曼表、重启间隔等。
以TIFF格式存放的图片的所有数据最终都是由不同的DE存放的,无论是图像的元信息还是描述图像每个像素点的数据均如此,本文并不涉及具体图像编码内容,因此不再继续研究TIFF的图像数据存放细节,事实上到此已将TIFF格式解析完全了。
EXIF格式
EXIF格式由日本电子工业发展协会(JEIDA)制定发布1.0版本,于1998年升级至2.1版本,增加了对音频文件的支持,2002年发布2.2版本添加对HEIC的支持,2023年发布3.0版本引入UTF8支持以及其它更新(此时JEIDA已发展为JEITA,EXIF也由JEITA与CIPA共同维护,CIPA——日本相机与影像产品协会)9。
EXIF格式并不完全与标准JPEG格式兼容,它只在记录的图像数据类型为JPEG压缩图像数据时才与JPEG格式兼容10。而对于未压缩的RGB或YCbCr数据时,则采用TIFF Rev. 6.0规范进行存储。
采用未压缩的RGB或YCbCr数据存储图像的EXIF文件(.TIFF)
此情况下,图片以标准TIFF格式记录,但仅保存一张主图片和其对应的缩略图,因而仅有两个IFD,分别为IFD 0和IFD 1,但缩略图和主图的数据前后保存在IFD 1中DE值的后边(实际上未作强制要求,只要符合TIFF规范即可,EXIF规范中给出的格式个人认为是推荐格式),如EXIF规范中的图所示:
使用标准TIFF格式存储意味着标准TIFF中的全部DE标志均可使用,因此可用的DE标志直接参考TIFF标准即可,由于EXIF文件中只允许一张主图和一张缩略图,所以可用的DE标志也有适用范围,见EXIF规范4.6.9,即便如此,也不代表携带有不适用的DE的EXIF文件不是一个标准的EXIF文件,适用的意思是指,它只在适用情况下有意义,不适用的情况下即使出现,也应该被忽略掉。
EXIF中的DE也与标准TIFF完全一致,但EXIF规范中并没有对FLOAT和DOUBLE类型的支持,反而在最新的EXIF 3.0标准中添加了对UTF8类型的支持(这里说的支持是指标准层面,而非实际的各种应用,毕竟,实际的应用并不一定会完全遵守标准)。
值 | 名称 | 长度(单位:bit位) | 描述 |
0x81 | UTF8 | 8 | UTF8字符串,最后一个字节以 NULL(0)结束。不得使用 BOM(字节顺序标记)。 UTF-8 字符串长度应包括 NULL。 |
此外,EXIF还使用了两个私有的自定义IFD,作为IFD 0中EXIF IFD Pointer DE和GPS IFD Pointer DE指向区域的内容,相当于IFD 0的两个子IFD。这是符合TIFF规范的,因为EXIF是使用IFD 0 中的两个DE的数据作为私有IFD的存放位置的字节偏移量的。TIFF并未规定标准IFD之间只能存储IFD中DE的数据,相反,两个IFD之间可以存放任意数量的数据,即使这些数据没有被IFD中的DE指向。因此,EXIF私有的这两个IFD完全符合TIFF的规范。这两个私有IFD的PIFD字段均被设置为0,除此之外,与标准IFD完全一致。
这两个私有IFD均是用来描述主图的,包括EXIF版本、色彩、拍摄参数、拍摄时的GPS信息等,它们只出现在IFD 0中。IFD 1则是用来描述缩略图的相关信息。
IFD 0中的这两个DE标志定义如下:
标签名称 | Tag ID(Hex) | 类型 | 数量 | 说明 |
Exif IFD Pointer | 8769 | LONG | 1 | EXIF私有的Exif IFD的字节偏移量 |
GPS Info IFD Pointer | 8825 | LONG | 1 | EXIF私有的GPS IFD的字节偏移量 |
EXIF IFD
EXIF IFD中可用的部分DE标志及其说明摘录部分如下,完整列表和适用情况见EXIF规范4.6.6及4.6.9。
标签名称 | Tag ID(Hex) | 类型 | 数量 | 说明 |
ExifVersion | 9000 | UNDEFINED | 4 | EXIF版本 |
FlashpixVersion | A000 | UNDEFINED | 4 | Flashpix版本 |
ColorSpace | A001 | SHORT | 1 | 色彩空间 |
Gamma | A500 | RATIONAL | 1 | Gamma值 |
MakerNote | 927C | UNDEFINED | EXIF编辑器用于记录任何所需信息的标签 | |
UserComment | 9286 | UNDEFINED | 用户所留备注 | |
DateTimeOriginal | 9003 | ASCII | 20 | 图像创建时间,内容格式为“YYYY:MM:DD HH:MM:SS”,24小时制 |
DateTimeDigitized | 9004 | ASCII | 20 | 图像数字化(存入存储器)时间 |
OffsetTime | 9010 | ASCII | 7 | Datetime DE记录的数据相对UTC时间的偏移量,格式“±HH:MM” |
OffsetTimeOriginal | 9011 | ASCII | 7 | DateTimeOriginal DE记录的数据相对UTC时间的偏移量,格式同上 |
OffsetTimeDigitized | 9012 | ASCII | 7 | DateTimeDigitized DE记录的数据相对UTC时间的偏移量,格式同上 |
SubsecTime | 9290 | ASCII | Datetime DE记录的数据的亚秒数据 | |
SubsecTimeOriginal | 9291 | ASCII | DateTimeOriginal DE记录的数据的亚秒数据 | |
SubsecTimeDigitized | 9292 | ASCII | DateTimeDigitized DE记录的数据的亚秒数据 | |
FNumber | 829D | RATIONAL | 1 | 光圈 |
ExposureIndex | A215 | RATIONAL | 1 | 曝光指数 |
ExposureTime | 829A | RATIONAL | 1 | 曝光时间,单位秒 |
ExposureProgram | 8822 | SHORT | 1 | 曝光程序 |
ExposureBiasValue | 9204 | SRATIONAL | 1 | 曝光偏差值,单位是APEX值 |
ExposureMode | A402 | SHORT | 1 | 曝光模式 |
SpectralSensitivity | 8824 | ASCII | 光谱灵敏度 | |
PhotographicSensitivity | 8827 | SHORT | ISO或REI或SOS | |
SensitivityType | 8830 | SHORT | 1 | PhotographicSensitivity的类型 |
ShutterSpeedValue | 9201 | SRATIONAL | 1 | 快门速度,单位为摄影曝光加法系统 (APEX) 设置 |
ApertureValue | 9202 | RATIONAL | 1 | 镜头光圈,单位是APEX值 |
BrightnessValue | 9203 | SRATIONAL | 1 | 亮度值,单位是APEX值 |
WhiteBalance | A403 | SHORT | 1 | 白平衡模式 |
MaxApertureValue | 9205 | RATIONAL | 1 | 最大光圈值,镜头的最小F值。单位是APEX值 |
SubjectDistance | 9206 | RATIONAL | 1 | 目标距离,单位米 |
SubjectDistanceRange | A40C | SHORT | 1 | 目标距离类型(近景远景等) |
MeteringMode | 9207 | SHORT | 1 | 测光模式 |
LightSource | 9208 | SHORT | 1 | 光源类型 |
Flash | 9209 | SHORT | 1 | 闪光灯状态 |
SubjectLocation | A214 | SHORT | 2 | 目标位置 |
SubjectArea | 9214 | SHORT | 2,3,4 | 目标区域 |
FocalLength | 920A | RATIONAL | 1 | 焦距,单位毫米 |
FocalLengthIn35mmFilm | A405 | SHORT | 1 | 35mm等效焦距,单位毫米 |
FlashEnergy | A20B | RATIONAL | 1 | 闪光灯能量,以光束烛光功率秒 (BCPS) 为单位 |
DigitalZoomRatio | A404 | RATIONAL | 1 | 数码变焦率,为0表示不使用数码变焦 |
SceneCaptureType | A406 | SHORT | 1 | 被摄目标类型 |
GainControl | A407 | SHORT | 1 | 图像增益调整 |
Contrast | A408 | SHORT | 1 | 对比度 |
Saturation | A409 | SHORT | 1 | 饱和度 |
Sharpness | A40A | SHORT | 1 | 清晰度 |
Temperature | 9400 | SRATIONAL | 1 | 气温,单位℃ |
Humidity | 9401 | RATIONAL | 1 | 湿度,单位% |
Pressure | 9402 | RATIONAL | 1 | 气压,单位hPa |
WaterDepth | 9403 | SRATIONAL | 1 | 水深,单位米 |
CameraElevationAngle | 9405 | SRATIONAL | 1 | 相机仰角,单位° |
ImageUniqueID | A420 | ASCII | 33 | 图像的UUID |
CameraOwnerName | A430 | ASCII,UTF-8 | 相机持有者名称 | |
BodySerialNumber | A431 | ASCII | 相机机身序列号 | |
LensSpecification | A432 | RATIONAL | 4 | 镜头规格 |
LensMake | A433 | ASCII,UTF-8 | 镜头制造商 | |
LensModel | A434 | ASCII,UTF-8 | 镜头型号 | |
LensSerialNumber | A435 | ASCII | 镜头序列号 | |
ImageTitle | A436 | ASCII,UTF-8 | 图像标题 | |
Photographer | A437 | ASCII,UTF-8 | 摄影师 | |
ImageEditor | A438 | ASCII,UTF-8 | 修图者 | |
CameraFirmware | A439 | ASCII,UTF-8 | 相机固件 | |
RAWDevelopingSoftware | A43A | ASCII,UTF-8 | RAW图生成软件 | |
ImageEditingSoftware | A43B | ASCII,UTF-8 | 图像处理软件 | |
MetadataEditingSoftware | A43C | ASCII,UTF-8 | 元数据编辑软件 |
GPS IFD
GPS IFD主要用于存放照片拍摄时的位置信息,虽然GPS IFD最初是专为存放GPS信息所制定的,但也可以用来存放以其它定位系统定位并记录的位置信息,例如GNSS、北斗等,所有对应的信息都是通用的。GPS IFD中可用的DE标志及其简单说明摘录部分如下,完整列表说明和适用情况见EXIF规范4.6.6及4.6.9。
标签名称 | Tag ID(Hex) | 类型 | 数量 | 说明 |
GPSVersionID | 0 | BYTE | 4 | GPS Info IFD的版本 |
GPSLatitudeRef | 1 | ASCII | 2 | 纬度是北纬还是南纬, “N”表示北纬,“S”表示南纬。 |
GPSLatitude | 2 | RATIONAL | 3 | 维度,分别表示度分秒 |
GPSLongitudeRef | 3 | ASCII | 2 | 经度是东经还是西经。 “E”表示东经,“W”表示西经。 |
GPSLongitude | 4 | RATIONAL | 3 | 经度,分别表示度分秒 |
GPSAltitudeRef | 5 | BYTE | 1 | 记录的高度是哪种类型的高度 |
GPSAltitude | 6 | RATIONAL | 1 | 高度,单位米 |
GPSTimeStamp | 7 | RATIONAL | 3 | 以UTC记录的GPS参考时间,时分秒 |
GPSSatellites | 8 | ASCII | 测量的GPS卫星信息,无固定格式 | |
GPSStatus | 9 | ASCII | 2 | GPS接收器状态,A测量中,V中断 |
GPSMeasureMode | A | ASCII | 2 | GPS测量模式,二维为2,三维为3 |
GPSDOP | B | RATIONAL | 1 | GPS数据精度DOP,二维时写入的是HDOP,三维时写入的是PDOP |
GPSSpeedRef | C | ASCII | 2 | GPS接收器的移动速度单位,KMN分别表示KM/H、英里/H、节 |
GPSSpeed | D | RATIONAL | 1 | GPS接收器的移动速度 |
GPSTrackRef | E | ASCII | 2 | GPS 接收器移动方向的参考。 “T”表示真实方向,“M”表示磁方向 |
GPSTrack | F | RATIONAL | 1 | GPS 接收器移动的方向。值的范围是从 0.00 到 359.99。 |
GPSImgDirectionRef | 10 | ASCII | 2 | 图像方向的参考, “T”表示真实方向,“M”表示磁方向。 |
GPSImgDirection | 11 | RATIONAL | 1 | 图像方向的值,范围同GPSTrack |
GPSMapDatum | 12 | ASCII | GPS接收器使用的地面测量数据 | |
GPSDestLatitudeRef | 13 | ASCII | 2 | 目的地点的纬度是北纬还是南纬。 “N”表示北纬,“S”表示南纬。 |
GPSDestLatitude | 14 | RATIONAL | 3 | 目的地点纬度,格式同GPSLatitude |
GPSDestLongitudeRef | 15 | ASCII | 2 | 目的地点的经度是东经还是西经。 “E”表示东经,“W”表示西经。 |
GPSDestLongitude | 16 | RATIONAL | 3 | 目标点经度,格式同GPSLongitude |
GPSDestBearingRef | 17 | ASCII | 2 | 目标点方向, “T”表示真实方向,“M”表示磁方向。 |
GPSDestBearing | 18 | RATIONAL | 1 | 到目的地点的方向 |
GPSDestDistanceRef | 19 | ASCII | 2 | 到目的地点距离单位。 “K”、“M”和“N”分别代表公里、英里和海里 |
GPSDestDistance | 1A | RATIONAL | 1 | 到目的地点的距离 |
GPSProcessingMethod | 1B | UNDEFINED | 用于定位的方法名称 | |
GPSAreaInformation | 1C | UNDEFINED | GPS区域名称 | |
GPSDateStamp | 1D | ASCII | 11 | 以UTC记录的GPS参考日期,格式“YYYY:MM:DD.” |
GPSDifferential | 1E | SHORT | 1 | 是否对 GPS 接收器应用差分校正 |
GPSHPositioningError | 1F | RATIONAL | 1 | 水平定位误差,单位米 |
Interoperability IFD
最后,EXIF规范中还存在一个Interoperability IFD,但仅列出了该IFD的相关规范,没有具体的使用说明,此IFD中只有一个名为“InteroperabilityIndex”的DE,标志为0x1,类型为ASCII,有”R98″、”THM”和“R03”三种可能的值,分别表示符合ExifR98规定的R98文件或符合相机文件系统设计规则规定的DCF基本文件的文件、符合相机文件系统设计规则规定的DCF缩略图文件的文件、符合相机文件系统设计规则规定的DCF选项文件的文件。此IFD应该是用于描述文件兼容性的,但并没有提供更多有用的信息,这类元信息一般都是由图片创建软件关心的,大部分情况下应该也无需过多关注。
采用JPEG压缩图像数据的EXIF文件(.JPG)
该情况兼容JPEG格式,即记录的图像数据类型为JPEG压缩图像数据,也是目前能见到的最多的EXIF图片。此情况下,EXIF标准中简化了很多标准JPEG中的概念,但它仍然是遵循标准JPEG规范的,此处我们按照标准JPEG中的概念和格式来描述EXIF格式。
此情况下,EXIF规范要求在SOI标识符之后,立即跟随一个APP1段作为EXIF标志段,这点与JFIF相似,但是这也导致JFIF与EXIF从标准上来说完全不可能兼容。在APP1段之后,可选跟随一个或两个APP2段,两个APP2段含义并不一致。在APP2段之后则可选跟随一个APP11段,若没有APP2段则APP11段跟随在APP1段之后,但APP11段不能出现在APP2段之前。
在APP11段之后,可选跟随任意数量的自定义的APPn段或注释段(COM),若无APP11段则跟随在APP2段之后,若无APP2段则跟随在APP1段之后。
接着,是DQT段、DHT段、DRI段、SOF段(即Frame Header)四者的任意顺序排列,其中除DRI段可以没有或只有一个外,其它三个段有且仅有一个。在这四者(或三者)之后,就是SOS段(Scan Header)和图像数据,这点与标准JPEG规范中一致,其中ECS的数量由DRI存在与否以及图像数据大小决定,ECS和RST的规则也和标准JPEG规范中一致。
APP1段
APP1段主要用于存放图片相关的信息,包括缩略图,它采用TIFF格式记录所有的数据信息,APP1段内的TIFF只有两个IFD,分别为IFD 0和IFD 1,前者用来描述JPEG图片信息(即主图信息),后者用来记录主图缩略图。记录的信息包括图片相关的各种信息,但是,不包括JPEG自身已经携带的信息,即,其它JPEG段携带的信息,若TIFF格式中也有相应的标志,则以JPEG段信息为准,TIFF仅仅是对JPEG的信息扩充和完善,不应该再携带JPEG中已有的数据。同时,TIFF虽然也能携带图片,但IFD 0是用于描述主图的,因此它不应该再携带任何图像数据(即图像每个像素的颜色等),而IFD 1则只需要携带主图缩略图的图像数据即可,不需要携带其它图片信息(例如GPS信息等),缩略图始终是由主图压缩而来的。
APP1段格式定义如下:
APP1段遵循JPEG标准段格式,首先是APP1段的标记(0xFFE1)(APP1),接着是段长(Lp)。在内容部分,它首先以“EXIF”ASCII字符串开头,由于ASCII字符串末尾的NULL标记,导致这个标识需要占用5字节(Id),为此需要再填充一个空字节(0x0)(Pad),来使内容从偶数字节开始。在“EXIF”标识字符串之后就是真正存放TIFF格式的部分(TIFF)。TIFF部分格式即标准TIFF,部分限制前文已经表述过。注意,TIFF部分的长度必须在64KB之内(更准确的说是64KB-8B),这是由于Lp字段长度所限制的。
APP2段和APP11段
APP2段主要用于存放Flashpix格式扩展信息的,Flashpix格式几乎不怎么流行,但又涉及新的格式,内容较多,本文不再赘述,有兴趣的可以查阅EXIF规范4.7.3部分以及EXIF规范附件F。该段在EXIF 2.1版本引入。
此外,还有一种APP2段,用于存放原始图像,该段存储内容采用MPF(Multi-Picture Format,规范CIPA DC-00711)格式存储,同样也不常见,并且涉及新的格式,内容过多,本文不再赘述,有兴趣可以自行查询相关规范。原始保存图像旨在当需要准确指示图像捕获时的信息时,通过保留原始图像的记录来增加主图像的可信度。该段也是在EXIF 3.0中引入的。
APP11段是一个存储元数据的标记段,该段使用 JPEG 系统第5部分:JUMBF(ISO/IEC 19566-5:2019)12中定义的框数据结构记录数据,以XML格式或JSON(JSON-LD)格式将JPEG压缩图像等的注释数据写入该标记中。该段使用到了JPEG扩展部分,在最新的EXIF 3.0中被启用,目前市面上大部分EXIF图片都使用的是较早的版本,因而此部分本文也不再赘述,感兴趣的可以自行查阅EXIF规范4.7.5以及ISO/IEC 19566-5:2019规范进行了解。
常见图片格式
在了解完标准JPEG、JFIF、TIFF以及EXIF之后,我们就知道如何准确判断一张图片到底是什么格式的了。JFIF格式和EXIF格式从标准层面看是不兼容的,图片阅读器在读取时,往往只读取JFIF信息而忽略EXIF信息,或只读取EXIF信息而忽略JFIF信息。现代图像生成/处理软件为了能保持最大的兼容性,往往会在一张图片中既添加EXIF的段,又添加JFIF的段,因此会出现一张图片中既有JFIF段也有EXIF段。现代的图片阅读器也会因兼容性既支持读取JFIF段又支持读取EXIF段,并基于自己的策略展示相应的图片信息。所以,现在常见的图片格式,从概念上说,一定是一张标准的JPEG格式图片,可能是JFIF格式图片或EXIF格式图片。在处理图像信息时,不能死板地照搬JFIF标准或EXIF标准进行处理,而应该同时考虑二者,基于自己的应用侧重使用不同的策略进行处理。
总结
总的来说,JPEG既是图像处理过程标准,同时也提供了标准的交换格式规范,后来的JFIF格式是对标准JPEG格式的简单扩展,而现如今最常用的EXIF格式则是对TIFF格式和JPEG格式两种格式的扩展。现在我们所见到的.jpg文件,一定是标准JPEG格式文件,但可能是JFIF格式文件或EXIF格式文件。现在我们所见到的.tiff文件,一定是标准TIFF格式文件,但可能是EXIF格式文件。图片格式领域百舸争流,百花齐放,你中有我,我中有你,层出不穷。
- T.81 : Information technology – Digital compression and coding of continuous-tone still images – Requirements and guidelines (itu.int) ↩︎
- ECMA TR/98 – Ecma International (ecma-international.org) ↩︎
- T.871 : Information technology – Digital compression and coding of continuous-tone still images: JPEG File Interchange Format (JFIF) (itu.int) ↩︎
- JPEG JFIF (w3.org) ↩︎
- [SPECS-TIFF 6.0] TIFF 6.0 Specifications (itu.int) ↩︎
- TIFF – Wikipedia ↩︎
- What are TIFF files and how do you open them? | Adobe ↩︎
- EXIF Spec DC-008-Translation-2023-E ↩︎
- Exif – 维基百科,自由的百科全书 (wikipedia.org) ↩︎
- EXIF Spec DC-008-Translation-2023-E ↩︎
- CIPA_DC-007-2021_E ↩︎
- ISO/IEC 19566-5:2019 – Information technologies — JPEG systems — Part 5: JPEG universal metadata box format (JUMBF) ↩︎