一、JPEG 图像格式详解
JPEG(Joint Photographic Experts Group,联合图像专家小组)是全球应用最广泛的连续色调静止图像压缩与存储标准,以高效有损压缩、24位真彩色支持为核心优势,覆盖互联网、数字摄影、印刷等多场景。本文基于官方标准与实践细节,从基础定义、识别逻辑、格式结构到编码实例,全面解析JPEG格式。
1.1 JPEG 文件简介
1.1.1 核心定义与标准背景
-
全称:Joint Photographic Experts Group(联合图像专家小组),是由 ISO(国际标准化组织) 与 ITU-T(国际电信联盟电信标准化部门) 联合制定的图像压缩标准。
-
初始目标:通过64Kbps通信线路传输720×576分辨率图像,实现低带宽下的图像高效传输。
-
核心属性:
- 颜色深度:支持 24位真彩色(1600万种颜色),非专业人士难以区分与原始图像的差异;
- 压缩效率:文件大小仅为未压缩格式的 1/10~1/20,压缩比可在 10:1~40:1 间调整(压缩比越高,细节损失越多);
- 局限性:有损压缩(删除的高频细节无法还原),不适合放大显示或高精度印刷(如专业画册)。
1.2 扩展名与格式分类
1.2.1 扩展名区别
JPEG文件的两种扩展名实质无差异,仅因历史命名规则不同:
.jpg:源于DOS系统的8.3文件名规则(最多8个主文件名+3个扩展名),兼容性更广;.jpeg:标准扩展名,符合现代文件命名规范,类似.htm与.html的关系,可相互重命名不影响文件内容。
1.2.2 三种格式类型
| 格式类型 | 核心特点 | 适用场景 |
|---|---|---|
| 标准 JPEG | 需完全加载后显示全貌;压缩比与品质可权衡;仅支持有损压缩;压缩高频信息,保留色彩细节 | 网络图片、普通数字照片 |
| 渐进式 JPEG | 加载时先显轮廓再补细节(“模糊→清晰”过程);用户体验更优,无需等待全量加载 | 网页大图、低网速场景 |
| JPEG2000 | 升级版,压缩率比标准JPEG高30%;支持有损/无损压缩;支持渐进传输与“感兴趣区域(ROI)”(指定区域高压缩品质) | 医疗图像、高精度印刷、无线传输 |
1.2.3 压缩模式
JPEG标准定义4种压缩模式,适配不同场景:
- 顺序式编码(Sequential Encoding):从左到右、从上到下逐行编码,最常用;
- 递增式编码(Progressive Encoding):对应渐进式JPEG,分阶段传输细节;
- 无失真编码(Lossless Encoding):仅支持无损压缩,压缩比低(约2:1),应用较少;
- 阶梯式编码(Hierarchical Encoding):多分辨率层级编码,支持缩放查看,适合遥感图像等。
1.4 JPEG 压缩四步骤
JPEG压缩通过“减少冗余信息”实现高效存储,核心流程分为四步,其中量化步骤是图像失真的主要原因:
步骤1:颜色转换与采样
-
颜色模式转换:JPEG仅支持 YUV颜色模型(Y=亮度,U/Cb=蓝色差,V/Cr=红色差),需先将RGB图像转换为YUV(人眼对亮度更敏感,色差可降低采样率)。
-
数据采样:对色差分量(Cb/Cr)降低采样率,常用比例:
- 2:1:1:每2个Y像素对应1个Cb和1个Cr,数据量压缩为原大小的1/2;
- 4:2:2:每4个Y像素对应2个Cb和2个Cr,数据量压缩为原大小的2/3。
步骤2:DCT 离散余弦变换
-
将转换后的YUV图像分割为 8×8像素矩阵(JPEG最小处理单元);
-
对每个矩阵执行DCT变换,将空间域信号转换为频率域信号:
- 矩阵左上角:低频系数(对应图像整体轮廓,视觉权重高,需保留);
- 矩阵右下角:高频系数(对应图像细节,如纹理、边缘,可优先舍弃);
-
变换后输出浮点数频率系数矩阵。
步骤3:量化处理
-
目的:将浮点数频率系数转换为整数,适配后续整数码本编码;
-
方法:用“量化表”(亮度量化表、色度量化表)对系数做“除法取整”:
- 低频系数:用小量化值(如16、11),保留精度;
- 高频系数:用大量化值(如61、99),牺牲细节;
-
关键:量化是不可逆过程,系数近似误差直接导致图像有损,是JPEG失真的核心来源。
步骤4:编码压缩
通过“两步编码”进一步减少冗余,最终生成压缩数据:
-
行程长度编码(RLE):将8×8矩阵按“之字形”(从低频到高频)排列,连续0值用“长度+0”表示(如“5个连续0”记为“5,0”),大幅减少冗余;
-
熵编码:基于统计特性压缩,JPEG支持两种方式:
- Huffman编码(默认):用短码表示高频出现的符号,长码表示低频符号,无专利限制;
- 算术编码:压缩效率更高(比Huffman高5~10%),但受专利限制,应用较少。
二、如何识别 JPEG 文件
jpg不依赖文件扩展名判断格式(扩展名可手动修改),而是通过验证文件头部固定字节识别JPEG,逻辑简单且可靠。
2.1 识别规则
JPEG协议规定:文件头部固定为 0xFF 0xD8 0xFF(十六进制),读取文件前3字节,若完全匹配则判定为JPEG格式。

注意:需文件扩展名为图像类型(如 .jpg .jpeg),才能被文件管理器或图像应用识别并打开。
2.2 代码实现
可以通过下面的代码来读取图片文件,查看是否是jpg压缩的
// 定义JPEG头部固定字节(FF D8 FF)
static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
static const size_t HEADER_SIZE = sizeof(gHeader);
static bool is_jpeg(SkStream* stream) {
char buffer[HEADER_SIZE];
// 从数据源读取前3个字节
size_t len = stream->read(buffer, HEADER_SIZE);
if (len != HEADER_SIZE) {
return false; // 读取不足3字节,非JPEG
}
// 字节完全匹配则为JPEG
if (memcmp(buffer, gHeader, HEADER_SIZE)) {
return false;
}
return true;
}
三、JPEG 图片格式详解
JPEG文件以“段(Segment)”为基本存储单元,段的数量和长度不固定,只要包含必须段即可正常解码。每个段由“标识+类型+长度+内容”组成,结构统一且可扩展。
3.1 段的通用结构
所有JPEG段遵循以下结构(段间的FF填充字节需忽略):

| 字段 | 字节数 | 说明 |
|---|---|---|
| 段标识 | 1 | 固定为0xFF,表示新段开始; |
| 段类型 | 1 | 标记码(如D8=SOI、E0=APP0),区分段功能; |
| 段长度 | 2 | 表示段内容+自身的总字节数(不含段标识和段类型),采用Motorola格式(高位在前); |
| 段内容 | ≤65533 | 段的具体数据(如量化表、图像尺寸),长度由“段长度”字段决定; |
关键细节:JPEG中所有多字节数据(宽高、长度等)均采用Motorola格式(高位在前),与Intel格式(低位在前)相反。
3.2 段类型汇总
JPEG标准定义30种段类型,仅10种需被所有程序识别(必须段+常用可选段),其余可忽略。核心段类型如下:
| 段名称 | 标记码 | 类型 | 核心作用 |
|---|---|---|---|
| SOI | D8 | 必须段 | 文件头,标识JPEG开始(仅FF D8两字节) |
| EOI | D9 | 必须段 | 文件尾,标识JPEG结束(仅FF D9两字节) |
| SOF0 | C0 | 必须段 | 帧开始,存储图像基本信息(宽高、组件数量) |
| DQT | DB | 必须段 | 定义量化表(亮度+色度各1个) |
| DHT | C4 | 必须段 | 定义Huffman表(亮度DC/AC、色度DC/AC) |
| SOS | DA | 必须段 | 扫描行开始,指向压缩图像数据 |
| APP0 | E0 | 必须段 | 图像识别信息(交换格式、像素密度、缩略图) |
| DRI | DD | 可选段 | 定义重新开始间隔(复位Huffman解码) |
| COM | FE | 可选段 | 存储注释(如作者、拍摄时间) |
| SOF1 | C1 | 可选段 | 与SOF0功能一致,适配特殊编码场景 |
3.3 必须段详解
3.3.1 SOI(文件头)
- 标记码:
D8; - 结构:仅2字节,无长度和内容;
- 数据:
FF D8(JPEG文件的起始标识,不可缺少); - 示例:二进制文件开头必为
FF D8,对应十六进制00000000h: FF D8 ...。

3.3.2 APP0(图像识别信息)
- 标记码:
E0; - 核心作用:定义JPEG交换格式(如JFIF)、像素密度、缩略图信息;
- 详细结构:
| 字段 | 字节数 | 说明 |
|---|---|---|
| 段标识 | 1 | FF |
| 段类型 | 1 | E0 |
| 段长度 | 2 | 无缩略图时为0010(16字节);有缩略图时为16+3n(n=缩略图像素总数) |
| 交换格式 | 5 | 4A 46 49 46 00(“JFIF”的ASCII码,JPEG默认交换格式) |
| 主/次版本号 | 各1 | 如01 01(主版本1,次版本1) |
| 密度单位 | 1 | 0=无单位,1=点数/英寸,2=点数/厘米 |
| X/Y像素密度 | 各2 | 如00 60(96点/英寸,高位在前) |
| 缩略图X/Y像素 | 各1 | 00表示无缩略图 |
| RGB缩略图 | 3×n | 仅当缩略图尺寸>0时存在(24位真彩,3字节/像素) |
- 示例:某JPEG的APP0段数据为
FF E0 00 10 4A 46 49 46 00 01 01 01 00 60 00 60 00 00,表示无缩略图、96点/英寸密度。

3.3.3 DQT(定义量化表)
- 标记码:
DB; - 核心作用:存储亮度和色度量化表,控制压缩失真程度;
- 详细结构:
| 字段 | 字节数 | 说明 |
|---|---|---|
| 段标识 | 1 | FF |
| 段类型 | 1 | DB |
| 段长度 | 2 | 单个量化表时为00 43(67字节)=3(段长度+QT信息)+64(8bit量化表) |
| QT信息 | 1 | 0-3位=QT号(0=亮度,1=色度);4-7位=QT精度(0=8bit,1=16bit) |
| QT量化表 | n | n=64×精度字节数(8bit时64字节,16bit时128字节) |
- 关键:JPEG通常包含2个DQT段(亮度+色度),量化表值越小,图像越清晰(失真越少)。

3.3.4 SOF0(图像基本信息)
- 标记码:
C0; - 核心作用:存储图像宽高、颜色组件数量等关键信息,解码必备;
- 详细结构:
| 字段 | 字节数 | 说明 |
|---|---|---|
| 段标识 | 1 | FF |
| 段类型 | 1 | C0 |
| 段长度 | 2 | 8 + 3×组件数量(如3个组件时为00 11=17字节) |
| 样本精度 | 1 | 通常为08(8bit/样本,主流软件不支持12/16bit) |
| 图片高度 | 2 | 如01 E0=480像素(高位在前) |
| 图片宽度 | 2 | 如01 40=320像素(高位在前) |
| 组件数量 | 1 | 1=灰度图,3=YCbCr彩色图(主流),4=CMYK彩色图 |
| 组件ID | 1 | 1=Y,2=Cb,3=Cr |
| 采样系数 | 1 | 0-3位=垂直采样系数,4-7位=水平采样系数(如22=垂直2、水平2) |
| 量化表号 | 1 | 对应DQT中的QT号(0=亮度表,1=色度表) |
- 示例:某彩色JPEG的SOF0段组件信息为
01 22 00(Y组件,采样系数2×2,用亮度表0)、02 11 01(Cb组件,采样系数1×1,用色度表1)、03 11 01(Cr组件,同Cb),对应YUV422采样格式。

3.3.5 DHT(定义Huffman表)
- 标记码:
C4; - 核心作用:存储Huffman表(DC表用于直流量,AC表用于交流量),编码压缩必备;
- 详细结构:
| 字段 | 字节数 | 说明 |
|---|---|---|
| 段标识 | 1 | FF |
| 段类型 | 1 | C4 |
| 段长度 | 2 | 19 + n(n=HT值表长度,19=2+1+16:段长度+HT信息+HT位表) |
| HT信息 | 1 | 0-3位=HT号;4位=HT类型(0=DC表,1=AC表);5-7位=0(保留) |
| HT位表 | 16 | 16个字节,分别表示“1位码~16位码”的符号数量,和≤256 |
| HT值表 | n | n=HT位表16个字节的和,存储Huffman符号(按码长递增排列) |
- 关键:JPEG需4个Huffman表(亮度DC、亮度AC、色度DC、色度AC),确保编码和解码对应。

3.3.6 SOS(扫描行开始)
- 标记码:
DA; - 核心作用:标识压缩图像数据的起始,关联Huffman表与颜色组件;
- 详细结构:
| 字段 | 字节数 | 说明 |
|---|---|---|
| 段标识 | 1 | FF |
| 段类型 | 1 | DA |
| 段长度 | 2 | 6 + 2×组件数量(如3个组件时为00 0C=12字节) |
| 组件数量 | 1 | 通常为3(YCbCr彩色图),1~4之间 |
| 组件ID | 1 | 1=Y,2=Cb,3=Cr |
| Huffman表号 | 1 | 0-3位=AC表号,4-7位=DC表号(如00=AC表0、DC表0) |
| 剩余3字节 | 3 | 用途不明,解码时可忽略 |
- 关键:SOS段后直接跟随压缩的图像数据(扫描行数据),按“左到右、上到下”顺序存储。

3.3.7 EOI(文件尾)
- 标记码:
D9; - 结构:仅2字节,无长度和内容;
- 数据:
FF D9(JPEG文件的结束标识,不可缺少); - 示例:二进制文件末尾必为
FF D9,对应十六进制00007bf0h: ... FF D9。
实际可以省略,比如下面的jpg就没有:

3.4 可选段详解
3.4.1 COM(注释)
- 标记码:
FE; - 作用:存储自定义注释(如作者、拍摄设备、时间);
- 结构:段长度=“注释字符字节数+2”,段内容为UTF-8编码的注释文本;
- 示例:若注释为“2024-01-01拍摄”,段长度=10(注释字节数)+2=12,对应
FF FE 00 0C 32 30 32 34 2D 30 31 2D 30 31 E6 8B 8D E6 91 87。
3.4.2 DRI(定义重新开始间隔)
- 标记码:
DD; - 作用:每隔n个MCU(最小编码单元,通常为8×8或16×16)块插入RST标记,复位Huffman解码状态,便于错误恢复;
- 结构:段长度固定为4,段内容为2字节的“开始间隔n”;
- RST标记:共8个(RST0~RST7),标记码为
FF D0~FF D7,穿插在数据流中,非独立段。
四、JPEG 压缩编码
JPEG压缩数据以“8×8矩阵”为单位,核心是DC系数(矩阵平均值)和AC系数(矩阵其他值)的编码,排列顺序为“亮度DC→亮度AC→色度DC→色度AC→色度DC→色度AC”。
4.1 DC/AC 系数定义
- DC系数:8×8矩阵的平均值,反映区域整体亮度/色差,编码时存储与前一矩阵的差值(差值编码,减少冗余);
- AC系数:8×8矩阵中除DC外的63个值,反映区域细节,按“之字形”排列后用RLE+Huffman编码。
4.2 8×8 色块编码计算
4.2.1 红色色块编码
红色色块的8×8二进制数据为:
E2 E8 A2 8A F9 93 F7 13 → 二进制:11100010 11101000 10100010 10001010 11111001 10010011 11110111 00010011
1. 计算Y亮度分量
- DC亮度:
1110(码长4,对应DC差值码表类别6)→ 后续6位001011(最高位0为负数,取反得110100=-52); - AC亮度:
1010(查表“0/0”,表示后续AC系数全为0,即红色块亮度均匀)。
2. 计算Cb色差分量
- DC色差:
10(类别2)→ 后续2位10=2; - AC色差:
00(后续系数全为0)。
3. 计算Cr色差分量
- DC色差:
10(类别2)→ 后续2位00=-3; - AC色差:
1010(0/3,值111=7)、11001(0/5,值10010)等,后续无数据则补0。
4.2.2 黑色色块编码
黑色色块的8×8二进制数据为:
F9 FE 8A 28 A0 0F → 二进制:11111001 11111110 10001010 00101000 10100000 00001111
1. 计算Y亮度分量
- DC亮度:
111110(类别8)→ 后续8位01111111(亮度均匀); - AC亮度:
10 10(后续系数全为0,黑色块亮度无细节)。
2. 计算色差分量
- DC色差:
00(类别0,差值为0); - AC色差:
1010(0/3,值001=-6)、01(0/1,值0=-1),后续补0,编码完成后剩余位补1。
4.3 标准量化表
JPEG定义默认的亮度和色度量化表,用于控制压缩失真,代码如下:
亮度量化表(std_luminance_quant_tbl)
static const unsigned int std_luminance_quant_tbl[64] = {
16, 11, 10, 16, 24, 40, 51, 61,
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68, 109, 103, 77,
24, 35, 55, 64, 81, 104, 113, 92,
49, 64, 78, 87, 103, 121, 120, 101,
72, 92, 95, 98, 112, 100, 103, 99
};
色差量化表(std_chrominance_quant_tbl)
static const unsigned int std_chrominance_quant_tbl[64] = {
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
};
4.4 关键码表
4.4.1 DC差值码表(亮度/色度)
| 类别(Category) | 亮度码长 | 亮度码字 | 色度码长 | 色度码字 |
|---|---|---|---|---|
| 0 | 2 | 00 | 2 | 00 |
| 1 | 3 | 010 | 2 | 01 |
| 2 | 3 | 011 | 2 | 10 |
| 3 | 3 | 100 | 3 | 110 |
| 4 | 3 | 101 | 4 | 1110 |
| 5 | 3 | 110 | 5 | 11110 |
| 6 | 4 | 1110 | 6 | 111110 |
| 7 | 5 | 11110 | 7 | 1111110 |
| 8 | 6 | 111110 | 8 | 11111110 |
| 9 | 7 | 1111110 | 9 | 111111110 |
| 10 | 8 | 11111110 | 10 | 1111111110 |
4.4.2 亮度AC码表(部分核心条目)
| Run/Size(连续0数/码长) | 码长 | 码字 |
|---|---|---|
| 0/0(EOB,结束) | 4 | 1010 |
| 0/1 | 2 | 00 |
| 0/2 | 2 | 01 |
| 0/3 | 3 | 100 |
| 0/4 | 4 | 1011 |
| 1/1 | 4 | 1100 |
| 1/2 | 5 | 11011 |
| 2/1 | 5 | 11100 |
6127

被折叠的 条评论
为什么被折叠?



