PicScrub logoPicScrub
返回格式列表

HEIC 格式

High Efficiency Image Container

ISOBMFF 容器

如果你摆弄过 MP4 视频文件,那 HEIC 会让你觉得眼熟。苹果选了 ISO 基础媒体文件格式(ISOBMFF)作为 HEIC 的容器, 也就是说它的血统更接近视频格式,而不是 JPEG、PNG 这类传统图片格式。

这个格式由一层层嵌套的“box”(也叫 atom)组成,每个 box 有一个 4 字符的类型码。 这个设计出人意料地优雅:既能表达复杂的数据层级, 摸清基础之后又相当好解析。

HEIC 文件结构

ftyp
meta
mdat
ftyp
24 字节
文件类型和兼容品牌
meta
1000 字节
元数据容器
mdat
长度可变
媒体数据(HEVC)

HEIC 文件头(ftyp box)

box 大小(24)
box 类型(ftyp)
主品牌(heic)
次版本号
兼容品牌(mif1)
兼容品牌(heic)
OffsetHexASCII
0000
000000186674797068656963
....ftypheic
000C
000000006D69663168656963
....mif1heic

box 的结构

大小类型数据
4 字节(大端序)4 字节大小 - 8

如果大小 = 1,类型后面会跟一个 8 字节的扩展大小;如果大小 = 0, 这个 box 一直延伸到文件末尾。

几个关键的 box

HEIC 文件里的 box 层级可以嵌得很深。JPEG 的元数据放在可预测的偏移位置上, 而 HEIC 把元数据埋在好几层嵌套结构里。下面是主要部件的组合方式。

ftyp:文件类型

文件里的第一个 box,包含主品牌和兼容品牌,是识别格式的必要信息。

兼容品牌: heic heix hevc hevx heim heis hevm hevs mif1 msf1

meta:元数据容器

装着所有元数据,包括项目位置、属性和它们之间的关联关系。 EXIF/XMP 数据就住在这里。

meta(元数据容器)
├── hdlr  (处理器引用)
├── pitm  (主项目 ID)
├── iloc  (项目位置 → 指向 mdat 的偏移量)
│   └── [item_id, offset, length] 条目
├── iinf  (项目信息 → 用来找到 'Exif' 项)
│   └── 带项目类型的 infe 条目
├── iprp  (项目属性)
│   ├── ipco (属性容器)
│   │   ├── hvcC  (HEVC 解码器配置)
│   │   ├── ispe  (图像尺寸)
│   │   ├── colr  (ICC 配置文件,偏移 +12)
│   │   ├── Exif  (TIFF 格式的 EXIF)
│   │   └── pixi  (像素信息)
│   └── ipma (属性关联)
└── iref  (项目引用)

mdat:媒体数据

存放真正经过 HEVC 编码的图像数据。meta 里的 iloc box 会给出指向 mdat 的偏移量。

HEIC 里的元数据

如果你习惯了 JPEG 的 APP 标记,HEIC 的做法会很不一样。 元数据不是以顺序排列的标记待在文件开头,而是作为“项目属性”存在 meta box 里。 真正的 EXIF 数据依然是熟悉的 TIFF 格式,但要找到它, 得先在 box 层级里绕上一圈。

EXIF 属性

以“Exif”类型存放在 ipco(项目属性容器)里, 内容是标准的 TIFF 格式 EXIF 数据,和 JPEG 里的一样。

XMP 属性

基于 XML 的元数据,以“mime”类型属性存放, content-type 为“application/rdf+xml”。

ICC 配置文件

色彩配置文件存放在类型为“prof”的“colr”box 里。

偏移量为什么这么要紧

iloc box 里存着指向 mdat 的精确字节偏移量。一旦某个 box 的大小变了 (比如因为删除元数据),这些偏移量就必须重新计算,否则文件根本读不出来。

无损匿名化

HEIC 的棘手之处就在这儿。这个格式当初是为高效存储视频和图像设计的, 不是为了方便改动元数据。

难点在哪

HEIC 基于偏移量的结构让“删字节”这件事变得很危险。 iloc box 里存着指向 mdat 的精确字节偏移量, 哪怕只删掉一个 box,这些偏移量就全错了,图片也就打不开了。

用零覆盖的做法

PicScrub 不去删除元数据 box,而是把它们的内容用零覆盖掉, 同时保持 box 的结构和大小不变。

之前:[Exif][GPS 数据: 37.7749, -122.4194][相机: iPhone]
之后:  [Exif][0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00...]

为什么选择用零覆盖?

  • • 文件保持完整:所有偏移量依然有效
  • • 不重新压缩:图像数据分毫不动
  • • 元数据被摧毁:零里面不含任何信息
  • • 能恢复吗?:不能,被覆盖的数据已经没了

PicScrub 如何处理 HEIC

1

解析 box 结构

递归解析 ftyp、meta,找出所有 box

2

定位元数据

在 ipco 容器里找到 Exif 和 XMP 属性

3

把元数据内容清零

用零覆盖元数据 box 的内容

4

保持结构不变

box 头、大小和所有偏移量原样不动

关于文件大小

因为 PicScrub 用的是零覆盖而不是删除,输出文件的体积会和输入一样。 元数据占的空间还在,只是被填满了零。

输入: 2.5 MB(含 50KB EXIF)
输出: 2.5 MB(50KB 的零)

HEIC、HEIF 与 AVIF

这几个名字很容易混,这里理一理:HEIF 是容器格式, 而 HEIC 和 AVIF 说的是容器里的图像用哪种编解码器压缩。

格式容器编解码器谁在用
HEICHEIFHEVC (H.265)苹果设备
HEIFHEIF多种通用容器
AVIFHEIFAV1Web(免版税)

三者用的都是同一套 ISOBMFF/HEIF 容器结构, 所以 PicScrub 的做法对它们全都适用。