JPEG
泄密最狠的一个JPEG 文件把元数据放在文件头和图像数据之间的 APPn 段里。下面就是各部分都住着什么,以及 PicScrub 分别会怎么处理。
会被清除的东西
- EXIF GPS 坐标、相机品牌和型号、拍摄时间、曝光参数
- XMP 编辑历史、用过的软件、评分
- IPTC / Photoshop 图注、署名、关键词
- 注释和内嵌缩略图 原图的一个微缩副本,可能在你裁剪、修图之后还活着
你可以保留的东西
- 图像数据 永远不动,绝不重新编码
- ICC 色彩配置文件 可选,让颜色保持准确
- 方向信息 可选,让竖拍照片不歪
- 版权声明 可选,给想署名又不想暴露坐标的摄影师
文件结构概览
如果你用十六进制编辑器打开过 JPEG,就会发现它是由一个个被标记分隔开的段组成的。每个标记都以 0xFF 开头,后面跟一个字节,说明这是什么类型的段。这个设计让 JPEG 相对好解析: 只要扫描 FF 字节,再看看它后面跟着什么就行。
对隐私来说有意思的地方在于,所有元数据(EXIF、GPS、拍摄时间)都住在这些段里, 和真正的图像数据完全分开。所以我们才能清除元数据,而不碰任何像素、不做任何重新压缩。
JPEG 文件结构
JPEG 文件头示例
| Offset | Hex | ASCII | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0000 | FFD8FFE000104A464946000101000048 | ......JFIF.....H | |||||||||||||||
| 0010 | 00480000FFE112344578696600000000 | .H.....4Exif.. | |||||||||||||||
标记的结构
FF xx:标记(xx 表示类型)LL LL:长度(2 字节,大端序,把自己也算进去)Data...:段数据(长度 - 2 字节)
JPEG 标记完整速查表
| 标记 | 名称 | 说明 |
|---|---|---|
| FF D8 | SOI | 图像开始 |
| FF D9 | EOI | 图像结束 |
| FF C0 | SOF0 | 帧开始(基线 DCT) |
| FF C2 | SOF2 | 帧开始(渐进式 DCT) |
| FF C4 | DHT | 定义霍夫曼表 |
| FF DB | DQT | 定义量化表 |
| FF DD | DRI | 定义重启间隔 |
| FF DA | SOS | 扫描开始(后面就是图像数据) |
| FF FE | COM | 注释(元数据) |
| FF E0 | APP0 | JFIF 标记 |
| FF E1 | APP1 | EXIF / XMP 标记 |
| FF E2 | APP2 | ICC 配置文件 / MPF |
| FF ED | APP13 | IPTC / Photoshop |
| FF EE | APP14 | Adobe 色彩变换 |
金色 = 元数据(会被清除),无底色 = 必需的结构
JFIF 与 EXIF
有件事让很多人犯迷糊:JPEG 文件里常常同时装着两套不同的元数据标准。 JFIF 出现得更早,管的是分辨率之类的基础信息。后来相机开始往里塞 EXIF 数据, 记录详细的拍摄参数。今天大多数 JPEG 两套都有,所以你会在文件开头附近同时看到 APP0 和 APP1 标记。
APP0:JFIF
JPEG File Interchange Format,记录图片的基础信息。
- • 版本号
- • 像素密度(DPI)
- • 宽高比
- • 可选的缩略图
APP1:EXIF
Exchangeable Image File Format,相机写入的元数据。
- • 相机品牌和型号
- • GPS 坐标
- • 时间戳
- • 曝光参数
APP 标记详解
JPEG 规范预留了 16 个“应用”标记(APP0 到 APP15),让厂商自由发挥。 这些年下来,不同的组织各自占了不同的标记来放自己的元数据格式。 结果自然有点乱,但好在是一团有文档可查的乱。
麻烦之处在于,同一个标记根据内容不同,含义也不同。比如 APP1 既可能装 EXIF, 也可能装 XMP,你得看看里面的标识符字符串,才知道自己面对的是哪一种。
APP0 (0xFFE0):JFIF
标识符:JFIF\0。包含版本、密度单位、X/Y 密度, 以及可选的内嵌缩略图。一般保留它是安全的。
APP1 (0xFFE1):EXIF
标识符:Exif\0\0。里面是一套完整的 TIFF 结构, 用 IFD 条目存放相机信息、GPS 数据、时间戳、缩略图和 maker notes。 隐私上最需要提防的部分。
APP1 (0xFFE1):XMP
标识符: http://ns.adobe.com/xap/1.0/\0。Adobe 制定的基于 XML 的 元数据标准,可能包含编辑历史、软件信息和扩展的 EXIF 数据。
XMP 命名空间
xmp: http://ns.adobe.com/xap/1.0/xmpMM: http://ns.adobe.com/xap/1.0/mm/dc: http://purl.org/dc/elements/1.1/photoshop: http://ns.adobe.com/photoshop/1.0/tiff: http://ns.adobe.com/tiff/1.0/exif: http://ns.adobe.com/exif/1.0/APP1 (0xFFE1):扩展 XMP
标识符: http://ns.adobe.com/xmp/extension/\0。当 XMP 数据超过 65KB 时, 会被拆到多个 APP1 段里。
APP2 (0xFFE2):MPF
标识符:MPF\0。Multi-Picture Format,用于立体图像和全景图。
APP2 (0xFFE2):ICC 配置文件
标识符:ICC_PROFILE\0。色彩管理数据,可能被切分到多个 APP2 标记里。 通常保留它是安全的。
APP13 (0xFFED):IPTC/Photoshop
标识符:Photoshop 3.0\0。存放 IPTC-IIM 数据, 包括图注、关键词、版权和创作者信息,可能带有作者身份。
APP14 (0xFFEE):Adobe
标识符:Adobe\0。存放色彩变换标志(RGB/CMYK), 对颜色能否正确渲染很重要。
EXIF 的结构
有意思(也有点怪)的地方来了:EXIF 数据其实是一个嵌在 JPEG 里的微型 TIFF 文件。 是的,真的。它有自己的文件头、自己的字节序,并用 TIFF 的 IFD(Image File Directory) 结构来存放标签。
在“Exif”标识符之后,你首先看到的是 TIFF 文件头,它说明数据是小端序(Intel,“II”) 还是大端序(Motorola,“MM”)。这一步弄错,后面所有数字都会变成垃圾, 所以任何解析器都会先检查它。
TIFF 文件头(8 字节)
| Offset | Hex | ASCII | |||||||
|---|---|---|---|---|---|---|---|---|---|
| 0000 | 49492A0008000000 | II*..... | |||||||
II = Intel(小端序),MM = Motorola(大端序)
IFD 条目的结构(12 字节)
主要的 IFD 条目
0x010F:Make0x0110:Model0x0132:DateTime0x8769:ExifIFDPointer0x8825:GPSInfoIFDPointer0x927C:MakerNotePicScrub 如何处理 JPEG
思路相当直接:我们一个标记一个标记地读完整个文件,保留显示图像必需的那些, 跳过装着元数据的那些。因为压缩过的图像数据我们从来不碰,所以画质零损失。
关键在于 JPEG 基于标记的结构让这件事变得简单。我们不需要理解 DCT 系数, 也不需要看懂霍夫曼表,只要分清哪些段是元数据、哪些不是就够了。
校验文件头
确认文件开头有 SOI 标记(FF D8)
解析标记
按顺序读取每个标记,取出类型和长度
识别元数据
检查 APP 段里的标识符(EXIF、XMP、IPTC 等)
选择性复制
把非元数据的段复制到输出里,跳过元数据段
会保留什么
- • SOI/EOI 标记(必需)
- • DQT(量化表)
- • SOF(帧头)
- • DHT(霍夫曼表)
- • SOS 和扫描数据(图像本身)
- • 可选:ICC 色彩配置文件(可配置)
会清除什么
- • APP1 EXIF(相机、GPS、时间戳)
- • APP1 XMP(编辑元数据)
- • APP13 IPTC(图注、作者)
- • COM 标记(注释)
- • EXIF 缩略图
EXIF 标签速查表
就隐私而言,EXIF 标签并不是一视同仁的。有些标签(比如曝光参数)相当无害, 另一些(比如序列号和机主姓名)却能唯一地指认出你或你的器材。 下面这些是我们认定的高风险标签:
| 标签 ID | 名称 | 隐私风险 |
|---|---|---|
| 0x9003 | DateTimeOriginal | 高 |
| 0x9004 | DateTimeDigitized | 高 |
| 0x9010 | OffsetTime | 中 |
| 0x9286 | UserComment | 高 |
| 0x927C | MakerNote | 高 |
| 0xA420 | ImageUniqueID | 高 |
| 0xA430 | CameraOwnerName | 高 |
| 0xA431 | BodySerialNumber | 高 |
| 0xA434 | LensModel | 中 |
| 0xA435 | LensSerialNumber | 高 |
| 0xFDE8 | OwnerName | 高 |
| 0xFDE9 | SerialNumber | 高 |
查看所有相机参数标签(不涉及身份)
0x829A ExposureTime0x829D FNumber0x8822 ExposureProgram0x8827 ISOSpeedRatings0x9201 ShutterSpeedValue0x9202 ApertureValue0x9203 BrightnessValue0x9204 ExposureBiasValue0x9207 MeteringMode0x9208 LightSource0x9209 Flash0x920A FocalLength0xA001 ColorSpace0xA002 PixelXDimension0xA003 PixelYDimension0xA402 ExposureMode0xA403 WhiteBalance0xA405 FocalLengthIn35mmFilm0xA406 SceneCaptureTypeGPS 标签速查表
GPS 数据住在自己的子 IFD 里,由主 IFD 中的 0x8825 标签指向它。 只要这个指针存在,你的图片里就有位置数据。现在的智能手机在这件事上格外积极, 精度往往足以指出你当时具体站在哪儿。
| 标签 ID | 名称 | 说明 |
|---|---|---|
| 0x0000 | GPSVersionID | GPS 标签版本 |
| 0x0001 | GPSLatitudeRef | N 或 S |
| 0x0002 | GPSLatitude | 度、分、秒 |
| 0x0003 | GPSLongitudeRef | E 或 W |
| 0x0004 | GPSLongitude | 度、分、秒 |
| 0x0005 | GPSAltitudeRef | 海平面以上或以下 |
| 0x0006 | GPSAltitude | 米 |
| 0x0007 | GPSTimeStamp | UTC 时间 |
| 0x001D | GPSDateStamp | UTC 日期 |
| 0x0012 | GPSMapDatum | 所用的大地测量基准 |
| 0x001B | GPSProcessingMethod | GPS/CELLID/WLAN/MANUAL |
| 0x001C | GPSAreaInformation | 地点名称 |
IPTC 标签速查表
IPTC 当初是为通讯社设计的,用来在图片里嵌入图注、署名和版权信息。 它存放在 APP13 里,标识符是“Photoshop 3.0”(没错,Adobe 把它接了过去)。 你在 Lightroom 或 Photoshop 里填过的版权信息和关键词,最后就落在这里。
0x0250 By-line(作者)0x0255 By-line Title(作者职务)0x025A City(城市)0x025C Sub-location(具体地点)0x025F Province/State(省或州)0x0265 Country Name(国家)0x0274 Copyright Notice(版权声明)0x0276 Contact(联系方式)0x0237 Date Created(创建日期)0x023C Time Created(创建时间)0x0278 Caption/Abstract(图注)0x0219 Keywords(关键词)