JPEG(Joint Photographic Experts Group)是一种广泛使用的有损图像压缩格式。
一、文件结构总览
JPEG 文件以SOI(Start of Image)标记开始,以EOI(End of Image)标记结束。中间由一系列标记段(Marker Segment)组成。
SOI 起始标记 → 各类参数段(APP0/APP1/DQT/DHT/SOF/SOS...) → 压缩图像数据 → EOI 结束标记 ┌─────────────────────────────────────────┐ │ SOI (0xFFD8) 2 bytes │ ├─────────────────────────────────────────┤ │ APP0 / APPn 标记段 可变长度 │ ├─────────────────────────────────────────┤ │ DQT (量化表) 可变长度 │ ├─────────────────────────────────────────┤ │ SOF (帧头) 可变长度 │ ├─────────────────────────────────────────┤ │ DHT (哈夫曼表) 可变长度 │ ├─────────────────────────────────────────┤ │ SOS (扫描开始) + 图像数据 可变长度 │ ├─────────────────────────────────────────┤ │ EOI (0xFFD9) 2 bytes │ └─────────────────────────────────────────┘1.1 核心标记
所有标记均以0xFF开头,后跟一个标记字节。
| 标记 | 十六进制 | 名称 | 说明 |
|---|---|---|---|
| SOI | FF D8 | Start of Image | 文件起始标记 |
| SOF0 | FF C0 | Start of Frame (Baseline DCT) | 基线帧起始 |
| SOF2 | FF C2 | Start of Frame (Progressive DCT) | 渐进帧起始 |
| DHT | FF C4 | Define Huffman Tables | 定义哈夫曼表 |
| DQT | FF DB | Define Quantization Tables | 定义量化表 |
| DRI | FF DD | Define Restart Interval | 定义重启间隔 |
| SOS | FF DA | Start of Scan | 扫描开始 |
| RSTn | FF D0-D7 | Restart | 重启标记(n=0~7) |
| APPn | FF E0-EF | Application-specific | 应用数据段 |
| COM | FF FE | Comment | 注释段 |
| DNL | FF DC | Define Number of Lines | 定义行数 |
| EOI | FF D9 | End of Image | 文件结束标记 |
二、核心标记解析
2.1 SOI与EOI
文件开发和结束标记,仅仅作为标记无实际内容
SOI: FF D8 // 仅2字节,无长度字段
EOI: FF D9 // 仅2字节,无长度字段
2.2 APP0 段(JFIF 标记)
字节偏移 内容 长度 0-1 FF E0 2 bytes (标记) 2-3 length 2 bytes (段长度,含此2字节) 4-8 "JFIF\0" 5 bytes (标识符) 9 version_major 1 byte 10 version_minor 1 byte 11 units 1 byte (0=无单位, 1=dpi, 2=dpc) 12-13 Xdensity 2 bytes 14-15 Ydensity 2 bytes 16 Xthumb 1 byte (缩略图宽度) 17 Ythumb 1 byte (缩略图高度) 18-... thumbnail_data 3*X*Y bytes- 2B 标记
FF E0 - 2B 段长 L
- 5B 标识符
JFIF\x00 - 1B 主版本号,1B 次版本号(如 1.02)
- 1B 密度单位:0 = 无单位,1 = 像素 / 英寸,2 = 像素 / 厘米
- 2B X 密度,2B Y 密度(分辨率 DPI)
- 1B 缩略图宽,1B 缩略图高
- 剩余字节:RGB 缩略图原始像素(宽 × 高 ×3 字节,无压缩)
2.3 DQT 段(量化表)
0-1 FF DB 标记 2-3 length 段长度 4 Pq:Tq 1 byte - Pq (高4位): 精度 0=8bit, 1=16bit - Tq (低4位): 表号 0~3 5-... table_data 64 bytes (8bit) 或 128 bytes (16bit) (按 Zig-Zag 顺序)压缩核心,8×8 量化矩阵,决定画质高低。
FF DB标记- 2B 段长
- 1B 表编号 + 精度:bit0~3 = 表 ID (0/1),bit7=0=8bit 量化,1=16bit
- 64B 量化矩阵(Z 字形排列 8×8)
Zig-Zag 顺序(64个 DCT 系数的读取顺序):
[ 0] [ 1] [ 5] [ 6] [14] [15] [27] [28]
[ 2] [ 4] [ 7] [13] [16] [26] [29] [42]
[ 3] [ 8] [12] [17] [25] [30] [41] [43]
[ 9] [11] [18] [24] [31] [40] [44] [53]
[10] [19] [23] [32] [39] [45] [52] [54]
[20] [22] [33] [38] [46] [51] [55] [60]
[21] [34] [37] [47] [50] [56] [59] [61]
[35] [36] [48] [49] [57] [58] [62] [63]
2.4 SOF0 段(帧起始)
0-1 FF C0 标记 2-3 length 段长度 4 precision 1 byte (通常=8) 5-6 height 2 bytes 7-8 width 2 bytes 9 num_components 1 byte (1=灰度, 3=YCbCr, 4=CMYK) 10-... components 3 bytes × num_components - C_i (component ID) - H_i:V_i (水平:垂直采样因子, 各4位) - T_q_i (使用的量化表号)定义图像尺寸、通道、采样格式,解码必须参数:
FF C0- 2B 段长
- 1B 采样精度(通常 8 位)
- 2B 图像高度 H(大端)
- 2B 图像宽度 W(大端)
- 1B 通道数量:1 = 灰度图,3=YCbCr 彩色
- 每组通道参数(3 通道循环 3 次):
- 1B 通道 ID:1=Y 亮度,2=Cb 蓝色差,3=Cr 红色差
- 高 4 位:垂直采样因子 V;低 4 位:水平采样因子 H 例:
0x22= H=2 V=2 → Y4:2:0 标准采样
2.5 DHT 段(哈夫曼表)
0-1 FF C4 标记 2-3 length 段长度 4 Tc:Th 1 byte - Tc (高4位): 类型 0=DC, 1=AC - Th (低4位): 表号 0~3 5-20 L[1..16] 16 bytes (各长度码字的数量) 21-... V[..] ΣL bytes (按长度分组的符号值)存储熵编码用的霍夫曼码表,分 DC(直流低频)、AC(交流高频)表,亮度 / 色度各一套。
FF C4- 2B 段长
- 1B 参数:bit4=0 (DC)/1 (AC),bit0~3 = 表 ID (0/1)
- 16B:每个值代表码字长度对应码数(1 位码多少个、2 位码多少个…)
- 后续 N 字节:霍夫曼码字对应符号表
2.6 SOS 段(扫描开始)
0-1 FF DA 标记 2-3 length 段长度 4 num_components 1 byte (扫描中的分量数) 5-... components 2 bytes × num_components - Cs (component selector) - Td:Ta (DC表:AC表, 各4位) N+0 Ss 1 byte (系数起始位置) N+1 Se 1 byte (系数结束位置) N+2 Ah:Al 1 byte (高位:低位比特位置) ┌───────────────────────────────┐ │ 压缩图像数据(熵编码流) │ │ (紧接在 SOS 段之后) │ └───────────────────────────────┘参数配置完成后,该段结束元数据,后面就是压缩二进制像素流:
FF DA- 2B 段长
- 1B 通道数 N
- N 组通道参数:通道 ID + (DC 表 ID<<4 | AC 表 ID)
- 3B 固定参数(基线 JPEG 恒为
00 3F 00)
三、编码流程
原始图像 (RGB)
↓
颜色空间转换 → YCbCr
↓
色度子采样 (4:4:4 / 4:2:2 / 4:2:0)
↓
分块 (8×8 宏块)
↓
电平偏移 (减去 128)
↓
DCT 变换 (离散余弦变换)
↓
量化 (除以量化表系数)
↓
Zig-Zag 扫描 → 一维序列
↓
DC 系数差分编码 (DPCM)
↓
AC 系数游程编码 (RLE)
↓
哈夫曼编码 (熵编码)
↓
字节填充 / 标记插入
↓
JPEG 文件
DC 系数编码
对相邻块的 DC 系数取差值(DPCM),然后:
- 确定差值的类别(SIZE:所需比特数)
- 用哈夫曼编码编码 SIZE
- 附加 SIZE 位的幅度值
AC 系数编码
对 63 个 AC 系数按 Zig-Zag 顺序处理,对每组 (RUN, SIZE/LEVEL):
- RUN:连续零的个数 (0~15)
- SIZE:非零值所需比特数
- LEVEL:非零值的幅度
- 特殊标记:
EOB (00):块结束(后续全零)ZRL (F0):16 个连续零
- (RUN, SIZE) 组合用哈夫曼编码,LEVEL 用附加位