65.9K
CodeProject 正在变化。 阅读更多。
Home

理解和读取 Exif 数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (11投票s)

2009年12月14日

CPOL

6分钟阅读

viewsIcon

154853

downloadIcon

5372

本文介绍了如何从 JPEG 图像中的 Exif 数据中检索有价值的图像信息,并提供了一个 Silverlight 类库,可以在客户端完全执行此任务。

引言

Silverlight 是开发富 Web 应用程序的绝佳环境。使用 Silverlight,您可以轻松地在客户端执行昂贵的任务,从而降低传统 Web 应用程序所需的带宽和处理能力的成本。

本文介绍了如何从 JPEG 图像中的 Exif 数据中检索有价值的图像信息,并提供了一个 Silverlight 类库,可以在客户端完全执行此任务。

注意:不幸的是,对于 Silverlight 开发人员来说,他们可以使用的沙盒 .NET Framework 并未公开 System.Drawing.Imaging 命名空间中实现的 GDI+ 包装器 - 至少在撰写本文时是这样。这些包装器包含了从图像元数据中检索各种信息所需的所有工具,所以如果它们可用,就不要浪费时间重新发明轮子。

相关工作

Simon McKenzie 撰写了一篇关于同一主题的精彩文章:ExifLib - A Fast Exif Data Extractor for .NET 2.0。这些库甚至有相同的名称……世界真小。我没有看过他的代码,但文章称他也没有使用 System.Drawing.Imaging 方法 - 所以它应该可以在 Silverlight 中正常工作,而无需进行重大修改。

本文的不同之处在于,它解释了 Jpeg 和 Exif 格式的基础知识,并且是专门为 Silverlight 编写的。

Jpeg 文件格式

理解 Jpeg 文件的整体结构对于提取 Exif 数据很重要。Jpeg 图像由两个字节标记分隔。第一个标记字节始终是 0xFF,而第二个字节标识其后的内容。例如,强制性的“图像起始标记”或 SOI,由数字 0xD8 定义。这就是为什么每个 Jpeg 图像都以序列 0xFF 0xD8 开头。除了 SOI 标记之外,每个标记后面都有两个字节指定该标记部分数据的长度(以字节为单位)。如果我们的目标是从 Jpeg 图像中提取 Exif 数据,那么我们要寻找 Exif 标记:0xE1。

Exif 格式

在指示 Exif 部分长度的两个字节之后,找到 ASCII 字符串“Exif”,后跟两个零字节。接下来的两个字节指示 Exif 目录中存储的数据的字节序。如果找到 II,表示使用 Intel 顺序,则字节顺序为小端。如果找到 MM,表示使用 Motorola 顺序,则字节顺序为大端。此后读取的任何原始数据都必须考虑 Exif 字节顺序。

Exif 数据存储在目录结构中:图像文件目录 (Image File Directory) 或 IFD 结构。前四个字节存储目录的长度(以字节为单位)。然后,用两个字节表示该目录中找到的条目数,即标签数。每个 Exif 条目首先由其标签标识符(两个字节)、然后是其格式(两个字节)以及最后是其组件计数(四个字节)定义。Exif 格式定义如下:

  1. 无符号字节(每组件 1 字节)
  2. ASCII 字符串(每组件 1 字节)
  3. 无符号短整型(每组件 2 字节)
  4. 无符号长整型(每组件 4 字节)
  5. 无符号有理数(每组件 8 字节,4 字节用于分子,4 字节用于分母)
  6. 有符号字节
  7. 未定义(每组件 1 字节)
  8. 有符号短整型
  9. 有符号长整型
  10. 有符号有理数
  11. 单精度浮点型(每组件 4 字节)
  12. 双精度浮点型(每组件 8 字节)

如果 Exif 条目的总字节数超过四个字节,则接下来的四个字节表示 Exif 部分内的偏移量,从中可以找到实际的条目数据。这样,通过跳过 12 个字节就可以遍历 Exif 条目列表。

IFD 可以嵌套在根 Exif 目录之下。标签号 0x8769 定义了一个新的 Exif 目录的开始。其他 IFD 标签也用于将标签号分组到类别中。例如,0x8825 是 GPS IFD 的标签号。在 IFD 的末尾,可以定义另一个 IFD,从而创建链表。这种链接由目录条目列表结束后的接下来的四个字节定义。如果该整数是一个落在 Exif 部分边界内的偏移量,它就定义了下一个 IFD 的起始点。

缩略图数据

一台相机创建的 1000 万像素图像在存储卡上会生成一个 2-7 MB 的 Jpeg 文件,具体取决于图像。在主内存中存储一个原始的 1000 万像素彩色图像至少需要 28 MB。在内存中保留数百张这样的巨型图像并不太实际,尤其是当你的唯一目标是显示图像的缩略图时。即使有内存可用,解码 Jpeg 图像也是一项非常耗时的操作,而且解码数百张图像今天可能需要几分钟。同样,这也不太实际。

那么,你的数码相机如何在不到一秒钟的时间内预览十张或更多的 10 MP 照片呢?当然是使用缩略图。缩略图是完整尺寸图像的一个较小版本,用于预览图像,而无需解码或将其存储在内存中。Exif 格式包含缩略图数据的条目,因此数码相机在创建 Jpeg 图像时,通常也会创建同一图像的 160x120 版本,并将其存储在图像的 Exif 部分。这种图像可以非常快速地解码,并且只需要不到 60K 的内存。

Exif 条目还包含其他非常有用的信息,例如拍摄图像的日期、相机型号、用户评论、曝光时间、相机闪光灯是否使用过,甚至在最近型号或手机中包含 GPS 坐标。

ExifLib

Exif_Data/class-diagram.png

ExifLib 是一个小型类库,可用于快速有效地解析给定 Jpeg 流中的 Exif 数据。ExifReader.ReadJpeg 静态方法以 System.IO.FileInfo 实例作为输入,并返回一个新的 JpegInfo 对象。如果 JpegInfo.IsValid 字段设置为 True,则该对象现在填充了 Jpeg 流中所有可用的 Exif 数据。

请注意,并非所有已知的 Exif 标签都包含在此库中。如果需要提取更多信息,可以修改 ExifTag 类以及相应的标签枚举。

ExifSL

Exif_Data/ExifSL.png

为了测试和演示 ExifLib 的功能,我编写了一个简单的 Silverlight 应用程序,您可以加载和预览 Jpeg 图像,还可以显示一些提取的 Exif 信息。当找不到 Exif 缩略图时,它会回退使用 BitmapImage.SetSource 方法加载整个图像。只需单击“添加图像”按钮并选择一些 Jpeg 图像即可开始。

结论

理解和使用图像文件格式和元数据是任何图像处理应用程序的第一步。希望这个小工具能为您节省一些时间。

参考文献

  1. JPEG - 维基百科
  2. JPEG.org
  3. EXIF.org
  4. Exif 文件格式
  5. Exif Jpeg 头信息修改工具
© . All rights reserved.