[转载]对图像文件(jpg和tif)中各种属性存放方式的研究
来源:http://blog.sina.com.cn/s/blog_bf9655770102vqhk.html
北京蓝码动力软件科技有限公司 余承飞
2015-05-20
交流QQ: 1931045538
1 研究背景
客户被jpg和tif文件中存在的各种附加属性—Exif属性、XMP属性、TIFF属性搞晕了,这些属性有些时候自相矛盾,特别是分辨率属性,不知道Photoshop会按照那个属性值来进行打印输出和其它处理。因此客户委托我研究一下。
我在网上找了很多资料,通过做一些小实验进行验证,基本上有了一些成果,在本文中记录下来。如有错误,希望大家指正。
2 TIFF中的各种属性
2.1 开始
本来我是先去研究JPEG的,后来发现JPEG里面其实套用了TIFF的数据的表示方法,所以TIFF是JPEG文件存储方式和EXIF的基础,因此我还是先说TIFF,再说JPEG。
用Photoshop打开一个tif文件看看。我选了这么一张:
在Photoshop CS5中选菜单“文件/文件简介”:
选中“高级”选项卡:
我原先以为这个界面显示的是Exif、Tiff、XMP属性,但经过后面的分析,发现这个界面显示的其实都是从XMP信息段中取出来的信息。因此,我在后面又补充了对非PhotoShop产生的tif文件的分析。
客户只关心EXIF属性、TIFF属性、XMP属性,我也就只研究这几项。
2.2 TIFF属性
首先,TIFF属性最重要,不能乱改。EXIF属性和XMP属性其实都可以删了不要。
TIFF文件的格式说明是TIFF6.pdf,可以从我公司网站的下载中心下载:
http://www.xcoolsoft.com/cn/downloads.php
TIFF文件的结构如下图:
TIFF文件首先有一个8个字节的文件头,然后跟着一个IFD,也就是Image File Directory的缩写,可以看做是索引目录。IFD可以有很多个,文件头指出了第一个IFD的位置,在IFD的结尾处指出了下一个IFD的位置,这样一直链接下去。
每个IFD的开始处首先说明了这个IFD有多少个Directory Entry,也就是有多少条目。然后顺序跟着这么多个条目,每个条目占12个字节。
在每个条目的12个字节里面,前两个字节是Tag(标签),标签的数值就决定了这个条目是干什么的。在TIFF6.pdf的最后附录中,列出了TIFF中的所有标签。我们在PhotoShop中看到的TIFF属性,都有相应的标签,如下表所示:
2.3 Exif属性
那么,Exif属性是如何存放的呢? 这要看另一个文档:exifStandard2.pdf。这个文档同样也可以从我公司网站的下载中心下载:
http://www.xcoolsoft.com/cn/downloads.php
Exif属性信息专门放在一个IFD里,称为Exif IFD。它的Tag是34665(8769.H)。下面是如何找到这个IFD的过程。
我们用二进制编辑器打开这个tif文件,找到第一个IFD:
这个文件的编码是低位在前的。第一个IFD的头两个字节是17 00,反过来就是0x0017,也就是说有0x17=23个Entry。上图中每个用红线标出的是每个Entry的标签(Tag),红线标注的最后四个字节是下一个IFD的地址,因为都是0,所以就只有这一个IFD。
请注意里面有一个标签是69 87,反过来是0x8767,就是Exif IFD的Tag。这个Entry的最后四个字节是E0 97 0F 00,也就是0x000F97E0,是Exif IFD的地址,从文件的开头开始计算。我们跳到这个地址看看。
03 00表示有3个Entry。把这些标签解释一下:
与PhotoShop中的属性显示完全一致。
2.4 XMP属性
XMP在各种文件中的存放位置:
XMP插入tif文件的Tag是700,即0x02BC。在我们的这个tif文件中,有BC 02这个Tag,Type=0x0001,即“BYTE”;Count=0x00003D21,Offset=0x00000262。我们跳到这个地址看看。
果然,这个地址开始是文本形式的xml。
3 JPEG 中的各种属性
3.1 开始
我们使用Win7自带一张jpg图片作为研究对象。这个图片的位置如下:
用PhotoShop CS5打开看一下:
我原先以为这个界面显示的是Exif、Tiff、XMP属性,但经过后面的分析,发现这个界面显示的其实都是从XMP信息段中取出来的信息。因此,我在后面又补充了对非PhotoShop产生的jpg文件的分析。
下面我们分析一下这些属性信息都放在哪里了。
3.2 JPEG文件格式
Wiki:
http://en.wikipedia.org/wiki/JPEG
http://en.wikipedia.org/wiki/JPEG_File_Interchange_Format
详细的格式说明,大家有兴趣的话可以去仔细研究一下。下面是我的理解。首先上一张从Wiki截下来的文件结构图:
这里提到了“JFIF”。JFIF大概是一个标准,规定了JPEG文件应该怎么存储。后来又有了Exif标准,也规定了JPEG怎么存储。软件厂商们就把这两个标准综合了一下,先以JFIF为基础,然后把Exif中的扩展的东西加进去,再把ICC Color Profiles加进去。
下面这张图是从Exif标准中截下来的:
有兴趣研究的朋友,建议先看TIFF标准,然后再看Exif标准,这样就容易理解得多了。Exif标准中对很多术语都不解释,例如IFD,完全没有解释,意思是详情参见TIFF标准。
言归正传。JPEG文件开头是SOI,即Start of Image(图像开始),必须是FF D8,规定死的。接下来是一些数据段,首先都是用两个字节(marker)表示这个数据段是什么,如下图:
我们关心的Exif信息是在APPn里面。
对照我们的这个jpg文件看一下:
头两个字节是FF D8没错。
3.2.1 APP0(0xFFE0)
接下来,我们发现marker是FF E0,即APP0。我们看看JFIF 的APP0是什么结构,如下图。
APP0 marker就是FF E0,跟我们的文件对上了。接下来是:
Length=0x0010=16
Iditifier=”JFIF\0” (0x4A46494600)
Version=0x0102 (major version=0x01, minorversion=0x02)
Density units = 0x01 (Pixels per inch)
X density = 0x0060=96
Y density = 0x0060=96
Thumbnail width (tw) = 0x00
Thumbnail height (th) = 0x00
Thumbnail data: 0个字节,不存在。
算下来总长度:2+5+2+1+2+2+1+1=16,与Length相符。
3.2.2 APP14(0xFFEE)
我们继续向下探索这个文件。
FFEE:APP14。怀疑是Adobe信息。
0x000E:怀疑是这段信息的长度。0x000E=14字节。
3.2.3 APP1(0xFFE1)
再向下:
FFE1:APP1。应该是Exif了。
到这里要看Exif标准了。
Length=0x135D=4957
Exif Identifier Code = “Exif\0\0” =0x457869660000 (是应该有两个“\0”吗?没找到证据)
以下开始是TIFF Header。前面我们已经学习过TIFF的格式,这里就很容易看懂了。
Byte Order = 0x4D4D (“MM”), bigendian,高位在前,不用反过来看了。
42: 0x002A TIFF固定标志,必须是这个。
Offset of IFD = 0x00000008,也就是立即开始0thIFD
number of fields = 0x0007,有7个Entries。
Entry 1 |
Tag = 0x0132 (DateTime) Type = 0x0002,(ASCII) Count = 0x00000014 = 20 Value or Offset = 0x00000062 = 98,因为20个字节放不下,这个98是偏移地址,从TIFF头开始算起。TIFF头的起始地址为0x00002E,相加后得0x000090。 |
跳到0x00000090看一下:
刚好是文本形式的拍摄时间。
再向下,第二个Entry:
Entry 2 |
Tag = 0x013B (Artist) Type = 0x0002,(ASCII) Count = 0x00000007 Value Offset = 0x00000076,加上TIFF头偏移0x2E,绝对偏移为0xA4。 |
到0xA4看一下:
是文本“Corbis”。奇怪的是,PhotoShop中的Exif属性中没有这一项。在Windows的属性中可以看到这项,如下图:
再向下:
Entry 3 |
Tag = 0x4746 这个标记在Exif标准中没有找到。查了别的网站,说是“Rating”。 Type = 0x0003,(SHORT) Count = 0x00000001 Value Offset = 0x00040000 = 4 这直接就是数值了,Rating = 4 |
在Windows的属性中可以看到“分级”为4颗星。
再向下:
Entry 4 |
Tag = 0x4749 (RatingPercent) 查到,Exif标准中没有。 Type = 0x0003,(SHORT) Count = 0x00000001 Value Offset = 0x003F0000 = 63,即RatingPercent =63% |
再向下:
Entry 5 |
Tag = 0x9C9D (XPAuthor, ignored by Windows Explorer if Artist exists ) Type = 0x0001,(BYTE) Count = 0x0000000E = 14 Value or Offset = 0x00000000 = 0 偏移地址竟然是0,不可理解。就当废弃了吧。 |
再向下:
Entry 6 |
Tag = 0xEA1C (Padding) Type = 0x0007,(UNDEFINED) Count = 0x0000080C Value or Offset = 0x00000000 = 0 也是无效的偏移 |
最后一个Entry:
Entry 7 |
Tag = 0x8769 (Exif IFD Pointer) Type = 0x0004,(LONG) Count = 0x00000001 Value or Offset = 0x0000007D,这是一个Exif IFD的地址偏移。本来这个IFD就是用来存Exif信息的了,这又出来另一个Exif IFD,好复杂。 |
最后就是指向下一个IFD地址了,4个字节,如下图:
4-byte offset to the next IFD = 0x000000E7,加上TIFF头地址0x2E,绝对地址为0x115。到这个地址看看。
这个地址竟然是奇数的,不是Word对齐的。
Entry数量=0x0005
Entry 1 |
Tag = 0x0103 (Compression) Type = 0x0003,(SHORT) Count = 0x00000001 Value or Offset = 0x00060000, 这就是数值了,Compression=6。 |
查了Exif标准后,发现这个IFD是用来存缩略图的。标准中的解释如下:
向下:
Entry 2 |
Tag = 0x011A (XResolution) Type = 0x0005,(RATIONAL,即两个LONG,第一个是分子,第二是分母) Count = 0x00000001 Value or Offset = 0x00000129, 因为2个LONG在这里放不下,所以这个是offset(偏移)。加上TIFF头地址0x2E,绝对偏移为0x157。 |
到地址0x157看看。如下图:
分子=0x00000048=,分母=0x00000001。即72/1。
向下:
Entry 3 |
Tag = 0x011B (YResolution) Type = 0x0005,(RATIONAL) Count = 0x00000001 Value or Offset = 0x00000131, 加上TIFF头地址0x2E,绝对偏移为0x15F。 |
到地址0x15F看看。如下图:
跟前面一样,也是72/1。
向下:
Entry 4 |
Tag = 0x0201 (JPEGInterchangeFormat,这里表示缩略图的位置) Type = 0x0004,(LONG) Count = 0x00000001 Value or Offset = 0x00000139。加上TIFF头地址0x2E,绝对偏移为0x167。 |
到地址0x167看看。如下图:
FF D8是SOI,FF E0是APP0,明显是一个jpg的特征。这应该是缩略图没错。
向下:
Entry 5 |
Tag = 0x0202 (JPEGInterchangeFormatLength,这里表示缩略图的长度) Type = 0x0004,(LONG) Count = 0x00000001 Value or Offset = 0x0000121C |
接下来是00 00 00 00,表示没有下一个IFD了。
回头再看0th IFD的Exif IFD Pointer,其偏移为0x0000007D,加上TIFF头地址0x2E,绝对偏移为0xAB。到这个地址看看。
Entry 数量=0x0005。
Entry 1 |
Tag = 0x9003 (DateTimeOriginal) Type = 0x0002,(ASCII) Count = 0x00000014=20 Value or Offset = 0x000000BF。加上TIFF头地址0x2E,绝对偏移为0xED。 |
到地址0xED看看。
这是时间日期没错。这跟我们前面看到的Tag=0x0132的时间不一样,那个是2009年的,这个是2008年的。
往下:
Entry 2 |
Tag = 0x9004 (DateTimeDigitized) Type = 0x0002,(ASCII) Count = 0x00000014=20 Value or Offset = 0x000000D3。加上TIFF头地址0x2E,绝对偏移为0x101。 |
到地址0x101看看:
这是数字化的时间。
往下:
Entry 3 |
Tag = 0x9291 (SubSecTimeOriginal) Type = 0x0002,(ASCII) Count = 0x00000003 Value or Offset = 0x35340000=“54\0\0”。表示54亚秒。 |
往下:
Entry 4 |
Tag = 0x9292 (SubSecTimeDigitized) Type = 0x0002,(ASCII) Count = 0x00000003 Value or Offset = 0x35340000=“54\0\0”。表示54亚秒。 |
往下:
Entry 5 |
Tag = 0xEA1C (Padding) 用来垫的,没意义的。 Type = 0x0007,(UNDEFINED) Count = 0x000007B4 Value or Offset = 0x00000000 |
指向下一个IFD的指针为00 00 00 00,说明没有其它了。
这样就把APP1分析完了。
3.2.4 APP12 (0xFFEC)
APP1的长度是0x135D,起始地址是0x26,所以下一段的开始地址是0x135D+0x26=0x1383。到这个地址看看:
这一段是Adobe Photoshop产生的,跟“Save for Web”有关。
3.2.5 APP1 (0xFFE1)
接下来又是一段APP1。
这一段应该就是传说中的XMP基本属性吧。
Length=0x1014
起始地址为0x1398,0x1398+0x1014=0x23AC。跳到这个地址看看。
3.2.6 APP13 (0xFFED)
这段是“Photoshop IRB”,不深究了。
Length = 0x00DC。起始地址0x23AE,0x23AE+0x00DC=0x248A。跳到这个地址。
3.2.7 DQT(0xFFDB)
这段是“Define Quantization Table”,不深究了。
Length=0x0043。起始0x248C,下个地址是0x0043+0x248A=0x24CF。跳到这个地址。
3.2.8 DQT(0xFFDB)
又是一段DQT。
Length=0x0043, 起始地址0x24d1,下一地址0x0043+0x24d1=0x2514。跳到这个地址。
3.2.9 SOF (0xFFC0)
Length=0x0011,起始地址0x2516,下一地址0x0011+0x2516=0x2527。跳到这个地址。
3.2.10 DHT (0xFFC4)
Length=0x001F,起始地址0x2529,下一地址0x001F+0x2529=0x2548。跳到这个地址。
3.2.11 DHT (0xFFC4)
Length=0x00B5,起始地址0x254A,下一地址0x00B5+0x254A=0x25FF。跳到这个地址。
3.2.12 DHT (0xFFC4)
Length=0x001F,起始地址0x2601,下一地址0x0011+0x2601=0x2620。跳到这个地址。
3.2.13 DHT (0xFFC4)
Length=0x00B5,起始地址0x2622,下一地址0x00B5+0x2622=0x26D7。跳到这个地址。
3.2.14 SOS (0xFFDA)
Length=0x000C,起始地址0x26D9,下一地址0x000C+0x26D9=0x26E5。从这开始就是图像数据了。
4 补充分析:非PhotoShop产生的图片
4.1 TIFF
首先我想到的是Windows的画图。
打开画图软件,就用默认的100x100大小,什么也不画,选格式为TIFF存盘。如下图。
关闭画图,分析一下这个tif文件。
因为文件很小,只有998字节,所以翻一页就看完了,里面没有出现XMP文本,是一个“纯”的TIFF。用PhotoShop打开来看看:
奇怪的是,XMP属性就存在了。所以用PhotoShop来看文件的属性是不正确的,我猜PhotoShop是获取了文件中的原有的属性,然后加上了一些默认的Adobe、XMP、都柏林属性,存盘的时候就以XMP信息段的形式存在文件里了。
4.2 JPEG
我想到了用数码相机拍摄的jpg照片。打开一个看看:
翻了一遍,没有发现XMP信息。用PhotoShop打开:
可以看到有Exif属性。
所以,不能用PhotoShop去看文件中是否存在Adobe、XMP等属性,这样不准确。