专栏首页天天P图攻城狮JPEG文件格式解析(一) Exif 与 JFIF

JPEG文件格式解析(一) Exif 与 JFIF

JPEG标准与JPEG文件存储格式

JPEG是联合图象专家组(Joint Picture Expert Group)的英文缩写,是国际标准化组织(ISO)和CCITT联合制定的静态图象的压缩编码标准。

而我们通常说的JPEG指的是以JPEG格式压缩的图片(即文件后缀为.jpeg .jpe )。经过JPEG重新编码的图片,文件压缩率可以达到90%以上,而且图片本身还具有较好的图片质量。这也是JPEG成为目前互联网上被用来存储和传输图片应用最广泛的格式的一个重要原因。

JPEG本身只有描述如何将一个视频/图片转换为字节的数据流(streaming),但并没有说明这些字节如何在任何特定的存储媒体上被封存起来。

这里要对JPEG做一个补充说明,很多人把JPEG标准和JPEG文件格式理解成一个东西。然而实际并不是这样的,JPEG标准主要还是围绕编解码的部分(如DCT变换、量化、哈夫曼树等等),虽然在JPEG标准中也定义了“JPEG Interchange Format (JIF)”的文件存储格式,但是因为Encoder和Decoder完整实现JIF很困难,且JIF标准也存在一些缺陷,因此JIF并没有被推广开来。倒是后来出现的“JPEG File Interchange Format (JFIF)” 和 “Exchange image file Format(Exif)” 等新的存储格式成为了主流。Exif 也好 JFIF 也罢,他们都是遵循 JIF标准的,两者只是在JIF的基础上增加了一些各自的Marker.

  • JPEG是压缩标准,JPEG/JFIF和JPEG/Exif是文件格式标准,不是一个概念,需要注意区分。
  • JPEG/Exif文件格式标准是Camera产业联合会发布,主要用于摄像设备上,摄像产业把Exif作为行业的元数据(metadata)交换格式
  • JPEG/JFIF文件格式标准是为了方便JPEG压缩图像在广泛的平台和应用间以最小的存储空间代价进行交换而设计的,它不包含JPEG/TIFF标准任何高级特性。

JPEG 文件结构

相比于BMP文件结构,JPEG文件结构要复杂得多。由于Exif和JFIF格式的都是遵循JIF的标准,在存储格式上沿袭了统一的 JPEG Marker + Compressed Data 的方式。整个文件根据不同的Marker划分成不同的标记段。每个Marker的长度为固定的 2 Byte.

Marker名称

Marker内容

说明

SOI

0xFFD8

Start Of Image

SOF0

0xFFC0

Start Of Frame 0

SOF2

0xFFC2

Start of Frame 2

DHT

0xFFC4

Define Huffman Table(s)

DQT

0xFFDB

Define Quantization Table(s)

DRI

0xFFDD

Define Restart Interval

SOS

0xFFDA

Start of Scan

RST0~RST7

0xFFD0 ~ 0xFFD7

Restart

APP0~APP15

0xFFE0 ~ 0xFFEF

Application-sepcific

COM

0xFFFE

Comment

EOI

0xFFD9

End of Image

上面这张表列举了JPEG 的主要 Marker。文件按照Marker的划分成不同的标记段,每个标记段结构轮廓一致,如下图所示。Detail Data 部分的结构根据不同的Marker的定义而进行不同的细分。

[ JPEG数据段结构 ]

SOI (0xFFD8) 和 EOI (0xFFD9) 作为JPEG文件的起止标志,不参照上图的数据划分。 所有的JPEG文件的头两个字节一定是 0xFFD8 最后两个字节是 0xFFD9

下图展示了JPEG两种文件结构的基本样式

[ Exif文件结构与JFIF文件结构比较 ]

可以看出两者基本上是一致的,最大的差异还是APP1APP0以及他们的扩展标记段APP2JFXX-APP0

JFIF-APP0 标记段

[ JFIF APP0标记段结构 ]

JFIF的结构相对比较简单,从APP0标记码起始地址偏移18个字节后,即可得到对应的缩略图数据数据的地址,这里是图像数据是未压缩过的,这与BMP位图的图像数据格式是一致的。

Exif APP1 标记段

[ Exif APP1 标记段结构 ]

相比JFIF的结构,Exif APP1标记段的内容就复杂多了。下面我们就对APP1标记段的信息进行详细的说明。

  1. APP1的标记码为固定值 0xFFE1
  2. APP1 Data 的头两个字节表示APP1 Data的长度
  3. 标识符占用 6 个字节,固定内容为 0x45 78 69 66 00 00,表示 “Exif”字符串
TIFF Header 的结构

[ TIFF Header 结构 ]

TIFF Header 一共8个字节

头两个字节表示 Byte align

  • II 表示数字存储遵循 intel 的字节序,即小端存储
  • MM 表示数据存储遵循 Motorola 的字节序,即大端存储

不同的存储字节序的选择主要是因为不同厂商的不同的数码产品的差异引起。大部分的数码相机使用 Intel 的字节序,也有些奇葩的产品的,比如Sony 的大部分产品都是使用Intel字节序的,唯独D700. (求D700心理阴影面积) 重要:字节序直接影响到数据内容,所以在解析Exif数据前必须检查文件的Byte align

中间两个字节表示 Tag Mark,是固定值,

  • 如果使用 Intel 字节序,则对应的存储值为 0x2a00
  • 如果使用Motorola 的字节序,则存储值为 0x002a

最后四个字节表示到 IFD0(Image File Directory)的偏移。

  • 该偏移的起始位置为 TIFFHeader 的起始位置
  • APP1 Data段中的 IFD0,Exif SubIFD,IFD1 中的偏移,也都是以 TIFFHeader的起始位置为基准
Image File Directory 结构

根据 TIFF Header 的后四个字节,我们可以找到第一个 IFD(Image File Directory)。IFD的数据结构如下表所示。每个IFD结构中存在多个Directory Entry,每个Entry记录着图片的一条属性信息,比如拍摄时间、拍摄机器、图片尺寸等等。

[ Image File Directory 结构示意图 ]

IFD结构的头两个字节存储 entry 的个数,如上表中 directory entry 的个数为 9个

每一个 Directory Entry 的长度固定为12个字节,分为4个部分

  • 头两个字节TTTT对应的内容为 Exif Tag
  • 次两个字节FFFF对应的内容为 Directory Entry存储内容的类型(Component Type)
  • 后面四个字节NNNNNNNN 存储的是 Directory Entry 对应的Component的数量
  • 最后的四个字节DDDDDDDD 存储的可能是Entry对应的值,Entry对应的值的长度超过四个字节,那么DDDDDDDD存储的是对应的值的偏移地址(该偏移寻址的基址也是TIFFHEADER的起始位置)。

[ Component Type信息对照表 ]

因为 Directory Entry 只有12个字节,用于数据存储的只有最后的4字节,无法存储过长的字符串或总长度超过4字节的数据信息。所以对于总长度超过4字节的信息,实际存储在IFD的Data area 中,在Directory Entry的最后四个字节中存储该信息的偏移地址。

总长度计算公式 总长度 = Component 数量 * 每个Component的字节数

Component的数量在 Directory的第三部分NNNNNNNN 中存储; 每个Component的字节数根据Directory Entry 第二部分的值Component Type,并结合「Component Type信息对照表」可以查询到。

Thumbnail image 部分

Exif格式的缩略图存储有主要有两种类型,一种为是以JPEG形式的存储的,一种是以TIFF的形式存储的,TIFF又分为RGB和YCbCr两种形式,严格的说一共是三种形式。其中JPEG形式和RGB的TIFF格式可以直接查看,而YCbCr的TIFF格式需要进行颜色空间的转换后才能正常查看。

缩略图的信息存储在 APP Data标记段的最后部分,缩略图的存储格式、起始地址和缩略图长度是由IFD1部分中Directory Entry的值来决定。

缩略图的存储格式由IFD1中的Exif Tag0x0103(含义为Compression) 的Directory Entry的值来决定

StripOffset表示缩略图偏移位置;

StripByteCounts表示缩略图长度;

PhotometricInterpretation = 1时,是RGB形式的TIFF格式存储;

PhotometricInterpretation = 6时,是YCbCr形式的TIFF格式存储;

  • Compression = 6. 缩略图是以JPEG格式存储。

起始的偏移地址由IFD1段的Exif Tag0x0201(JpegIFOffset)的决定;

缩略图的长度由IFD1中的Exif Tag0x0202(JpegIFByteCount)来决定。

  • Compression = 1. 缩略图是以为TIFF格式存储的. 这里涉及到的Entry有三个Exif Tag分别是
    • 0x0111(StripOffset) 表示缩略图偏移位置;
    • 0x0117(StripByteCounts) 表示缩略图长度;
    • 0x0106(PhotometricInterpretation).值为1时是RGB形式的TIFF格式存储;值为6时,是YCbCr形式的TIFF格式存储;
Exif IFD的树形结构

上面已经将Exif APP1结构的做了逐一介绍,下面我们绘制成了一张图来展示Exif IFD的树形结构

[ Exif IFD树形结构 ]

  • 从图上可以看出IFD0~IFDn根据 Offset to next IFD 来指向下一个 IFD,形成了一个单链表。在解析该链表时,我们只要根据 “Offset to next IFD”是否为0x00000000来判断当前节点是不是最后一个节点。
  • IFD0中的Exif Tag为0x8769(EXIF_OFFSET)的Entry的值为 Exif Sub IFD的偏移地址。
  • Exif Sub IFD中的Exif Tag为0xa005(InteropOffset)的Entry值为 Interoperability IFD的偏移地址。
  • Exif Tag 的含义列表可以访问https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html 查询

查看 Exif 信息

比较过 JFIF 格式APP0 和 Exif APP1的结构后,Exif的结构明显比JFIF的要复杂,但是也因此具有更大的灵活度,可以存储更多的信息(可以访问上面Exif Tag列表的链接,就可以感受到Exif信息庞大),这也是当前越来越多的数码产品使用Exif来存储的图片的原因之一。JFIF APP0的结构信息是线性顺序排列的,解析相对简单,这里我们主要以Exif APP1的结构即解析为例展开说明。查看Exif信息的工具有很多,一般系统自带的工具就可以查看。

Mac 自带的图片查看器

打开图片后点击菜单栏 工具-> 显示检查器 -> 选择 Exif 即可

[ Mac自带的图片查看其查看Exif信息 ]

使用 Exiftool 来查看Exif信息

ExifTool是Phil Harvey以Perl写成的免费开源软件,可读写及处理图像、视频及音频的metadata,例如Exif、IPTC、XMP、JFIF、GeoTIFF、ICC Profile。它是跨平台的,可作为命令列或Perl函式库使用。

exiftool 的安装

$ brew install exiftool

exiftool 的使用

$ exiftool image_42x42.jpg

输出部分信息如下

实验

exiftool 会将解析完的信息都展示出来,中间的解析过程都是不可见的,为了更好的理解Exif的结构,我写了一些代码来提取APP1标记段的内容,并初步解析了含义。 实验的图片是以一张手机拍摄的图片,原始尺寸为 5480x4110。

[ 原始图片的截图 ]

由于尺寸太大不便于文件格式的分析,所以通过Photoshop对图片进行裁剪并缩小为 42x42的小图片(该操作并不会影响图片本身的Exif信息)。

[ 实验素材图 ]

使用vim打开该文件,并转换成十六进制的格式查看。根据前面介绍的APP1的格式,可以对文件做如下划分

[ 十六进制的图片数据 ]

上图绿色和橙色高亮的部分是 Directory Entry的数据段,做了颜色标注之后,方便理解。蓝色高亮部分0000 03b4 是IFD的Offset to next IFD信息,该部分往后便是 Data Area of IFD0部分;这里已经可以看到一些字符串信息,如手机型号,拍摄时间,图像处理软件等

[ IFD0 信息提取 ]

根据 Exif Offset 可以获取到 Exif Sub IFD 的信息如下

[ Exif Sub IFD 信息提取 ]

根据IFD0Offset to next IFD 可以读取 IFD1 的信息如下

[ IFD1 信息提取 ]

可以看到IFD1Offset to next IFD0x0000 0000,表示该节点为最后一个IFD节点,利用其中的Compression,JPEG IF Offset,JPEG IF Byte Count 信息将对应位置的内容读出来,并单独写入文件即实现了从 Exif中提取缩略图的功能。下图exifThumbnail_001.jpg是按照这里说的方法从Exif中提取出的缩略图。

[ 从Exif中提取的缩略图信息 ]

这是两个文件的大小

-rw-r--r--@ 1 shaoling  staff     13429 10 12 17:49 image_42x42.jpg
-rw-r--r--@ 1 shaoling  staff      1034 10 25 18:42 exifThumbnail_001.jpg

获取的缩略图exifThumbnail_001.jpg在展示效果上与实验图片image_42x42.jpg 在视觉效果上并没有肉眼可见的差距。

实验图片image_42x42.jpg 大小为 13429字节,提取出的缩略图文件exifThumbnail.jpg大小只有 1034字节;可见实验图片包含了很多其他的信息。这里我们可以使用exiv2这个工具来帮助分析。

$ exiv2 -pS image_42x42.jpg
$ exiv2 -pS exifThumbnail_001.jpg

得到如下结果

可以看到image_42x42.jpg中APP段的信息(APP1 + APP13 + APP1 + APP2 + APP14)长度就12205字节,占了文件大小的 90.89%。

汇总成表格后可以明显的看到两者的差异主要是因为APP段的应用数据差异导致的,与图像本身相关的数据两者的差异并不大。

Tips: 使用 exiftool 来获取exif的缩略图的方法

$ exiftool -b -ThumbnailImage image_42x42.jpg > thumbnail.jpg

参考文献

  1. https://zh.wikipedia.org/wiki/JPEG
  2. https://www.fileformat.info/format/bmp/egff.htm
  3. http://lad.dsc.ufcg.edu.br/multimidia/jpegmarker.pdf
  4. https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
  5. https://www.sno.phy.queensu.ca/~phil/exiftool/
  6. https://www.media.mit.edu/pia/Research/deepview/exif.html

作者简介:shaoling, 天天P图AND工程师


文章后记: 天天P图是由腾讯公司开发的业内领先的图像处理,相机美拍的APP。欢迎扫码或搜索关注我们的微信公众号:“天天P图攻城狮”,那上面将陆续公开分享我们的技术实践,期待一起交流学习!

本文分享自微信公众号 - 天天P图攻城狮(ttpic_dev),作者:shaoling

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-03-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • iOS减包实战:Compress PNG Files作用分析

    减包这个词大家应该都不陌生,在减包过程中,图片资源的优化这项应该是必经之路了。

    天天P图攻城狮
  • Android基础:Fragment,看这篇就够了

    Fragment 作为 Android 最基本,最重要的基础概念之一。本文从为什么出现 Fragment 开始,介绍了相关的方方面面。

    天天P图攻城狮
  • 例说 Constraint Layout(三)—— 性能测评

    在各种页面设计下,提升有多有少,但 CL 的性能确实是最佳的!

    天天P图攻城狮
  • 声明式服务调用-Feign

    博主在昨天的文章中,提及到项目中选用Eureka作为服务的注册中心,那么今天就应该是开始进行服务的调用, 即认证平台调用数据服务。在之前的文章中我都是很模糊的描...

    每天学Java
  • 谈谈桌面应用开发

    前几年最流行的应该是移动端应用开发,桌面应用开发是否已经是一份古老的职业。我们公司去年将移动端和PC端开发合并到一个技术通道,叫做客户端开发,我也逐渐承担起一些...

    congduan
  • Spark如何读取一些大数据集到本地机器上

    我是攻城师
  • 从MapX到MapXtreme2004[11]-坐标概论

            坐标的问题是Mapxtreme中最郁闷的问题,前几天在这上面耗了很多时间,没有搞定,今天又是不得不钻研,还好,小有心得。         1、...

    用户1075292
  • ​OpenGL 学习系列---坐标系统

    在前面绘制基本图形中,遇到了很明显的问题,圆形不像圆形,正多边形不像正多边形?就像下面图形一样:

    glumes
  • 小技巧,把Markdown文本发布到微信公众号文章

    俺踏月色而来
  • DD驱动登陆 tim QQ TGP 脚本必懂

    cxt084

扫码关注云+社区

领取腾讯云代金券