JPEG结构及解码过程详解
0. 本文概述
本文是笔者在研究JPEG编码格式及解码过程中的笔记,整合了许多网上的文章与参考文献(链接于文末).
同时本文也是为了应付USTC计网实验写一篇博客的要求
本文将介绍JPEG的格式(包括常用JPEG标识段及其结构),并详细介绍解码基线离散余弦变换编码JPEG(baseline DCT JPEG)与渐进式离散余弦变换编码JPEG(Progressive DCT Huffman Coding JPEG)的过程,这是两种最常用的JPEG编码格式,其他编码格式由于过于少见,许多格式转换软件甚至不支持它们.对于本文所介绍的两种JPEG编码格式,前者具有最广泛的兼容性,而后者在解码过程中具有从模糊到清晰的过程,加载速度较快,因此是大多数网站与应用的首选JPEG格式.
另外,对于本文并未提到或介绍不太详细的内容,你可以阅读由**国际电信联盟(ITU-T)与国际标准化组织(ISO)**制定的参考文献[1]详细了解
1.JPEG基本格式
除被压缩的图片数据外,JPEG文件由许多以标记(Marker)开头的段(Segment)组成,常见的如SOI,APPn,DHT,DQT,SOFn,SOS,EOI等等.标记长度为2字节,且第一个字节总是0xFF.对于大多数段而言,紧跟在标记后的两字节就是该段的长度(大端格式,长度包含自身但不包括标记).我们将在下文中详细地介绍它们的大致顺序与结构.
对于JPEG中的数据均为大端格式,在压缩数据流处也是先读取字节高位
1.1 SOI与EOI
JPEG文件总是以标记SOI(Start of Image)开始,并以EOI(End of Image)标记结束.这两个段除标记单独出现外没有其他的数据,在SOI与EOI之间的所有内容称为帧(Frame).
SOI结构:
| Name | Length(Bytes) | Description |
|---|---|---|
| SOI | 2 | 0xFFD8 |
EOI结构:
| Name | Length(Bytes) | Description |
|---|---|---|
| SOI | 2 | 0xFFD9 |
1.2 APPn与COM
APPn是为应用程序保留的段,当n=0时,该段一般用来保存24位RGB像素的缩略图,当n=1~15时,为应用程序保存一些特定数据.对于JPEG解码来说,这些段没有必要的作用,可以通过读取标记后的两个字节来跳过.
COM为图片的注释段,也没有实际意义,可以跳过.
APP0结构:
| Name | Length(Bytes) | Description |
|---|---|---|
| APPn(n=0~15) | 2 | 0xFFE0~0xFFEF |
| 段长度 | … | * |
| …(data) | … | 缩略图或保留数据 |
COM结构:
| Name | Length(Bytes) | Description |
|---|---|---|
| COM | 2 | 0xFFFE |
| 段长度 | … | * |
| …(Comments) | … | 注释 |
1.3 DQT
DQT即定义扫描量化表(Define Quantization Table).JPEG格式在压缩时以一个颜色分量的8*8数据作为一个数据单元(data unit),进行DCT变换后再量化处理.扫描量化表储存了对于8*8个数据进行量化的具体系数.
一个DQT段可以定义多个量化表,也可以在多个DQT段中分别定义.对于一般JPEG,它最多只能同时定义4个DQT段,但是允许在不同的扫描(scan)前重新定义.
DQT结构:
| Name | Length(Bytes) | Description |
|---|---|---|
| DQT | 2 | 0xFFDB |
| DQT长度 | … | * |
| DQT表1: | ||
| 量化精度与量化表id | 1 | 高4位:精度 	0:8bit 1:16bit 低4位:量化表id |
| 64个量化表系数 | 64*(1+精度) | * |
| (更多DQT表…) | * | * |
当读取完一个DQT表发现未达到该DQT段的长度时,提示接下来还有DQT表.
1.4 DHT
DHT即定义霍夫曼表(Define Huffman Table).该表定义了压缩过程中用到的霍夫曼表.需要注意的是,JPEG压缩使用范式霍夫曼编码来压缩数据,这种压缩方法使得DHT段中只需要记录各编码长度所含的编码条目数,与被编码的原数据(1字节),就可以建立编码的霍夫曼树.
可参考文献[3]
类似DQT,DHT段也可以定义多个霍夫曼表,也可以在多个DHT段中分别定义.同样DHT也只能定义4个段,但允许重新定义.
DHT结构:
| Name | Length(Bytes) | Description |
|---|---|---|
| DHT | 2 | 0xFFC4 |
| DHT长度 | … | * |
| DHT表1: | ||
| 表id与编码数据类型 | 1 | 高4位:编码数据类型 	0:DC(直流) 1:AC(交流) 低4位:霍夫曼表id |
| 1~16编码长度所含的编码条目数 | 16 | * |
| 被编码的原数据 | 以上16个数值和 | 按照顺序的原数据字节 |
| (更多DHT表…) | * | * |
当读取完一个DHT表发现未达到该DHT段的长度时,提示接下来还有DHT表.
1.5 SOFn
SOFn,即帧图像开始段(Start of Frame).n代表图像所用的编码格式,例如对于本文所研究的两种编码格式:基线DCT格式为SOF0,而渐进式DCT格式为SOF2.该段记录了图像的基本信息,如长宽、数据精度、颜色分量数等.SOF段必须在任何扫描开始前定义.
SOFn结构:
| Name | Length(Bytes) | Description |
|---|---|---|
| SOFn | 2 | 0xFFC0+n |
| SOF长度 | … | * |
| 图像数据精度 | 1 | 8/12/16位,大多数软件仅支持8位精度 |
| 图像高度 | 2 | Y向像素数量 |
| 图像宽度 | 2 | X向像素数量 |
| 颜色分量数 | 1 | 1(灰度图)/3(YCrCb)/4(CMYK) |
| 颜色分量信息 | (颜色分量数)*3 | 颜色ID(1B):从1计数 采样因子(1B):高4位水平采样因子,低4位垂直采样因子 量化表ID:该颜色分量所使用量化表ID |
采样因子与在扫描中规定的*最小编码单元(Minumum Coded Unit,MCU)*的大小有关,我们之后详细叙述这个问题
1.6 DRI与RSTn
DRI即定义复位间隔(Define Restart Interval),除标记与描述长度的数据外,仅有一个2字节数据(设为n),它描述了一个扫描每读取n个MCU后就应该重置(包括重置DC差分编码与字节重新对齐等),并读入2字节长的复位标记RSTn,并继续进行扫描读取,其中RSTn的n从0到7循环.
DRI与RSTn相关的段用于分隔压缩数据,以确保一块数据出错时不会影响下一块数据.DRI与配合的RSTn并不是必需的,且同样也可以在每次扫描前重新定义复位间隔.
DRI结构:
| Name | Length(Bytes) | Description |
|---|---|---|
| DRI | 2 | 0xFFDD |
| DRI长度 | 4 | * |
| 读取MCU复位间隔n | 2 | 每在扫描中读取n个块就重置,并读取复位标记 |
RSTn结构:
| Name | Length(Bytes) | Description |
|---|---|---|
| RSTn | 2 | 0xFFD0+n,总应该从0开始0~7循环 |
1.7 SOS
SOS段即扫描开始段(Start of Scan).本段后接一次扫描(即对图片压缩数据流的读取),因此定义了扫描的具体参数.一次扫描应该具备的参数有:扫描中参与的颜色分量数、各颜色分量所使用的霍夫曼表表号(包含直流分量的表号与交流分量的表号)、扫描的(经DCT变换后的)频谱段起始与逐次逼近系数(前一次/本次).
SOS(即扫描)可以有多次,对渐进式JPEG来说尤其如此,而基线式JPEG也可以有多次扫描,不过总是扫描全部频率,且不会有逐次逼近.换言之,对基线JPEG的扫描后三个字节只能是0x00 0x63 0x00,其多次扫描只是针对不同的颜色分量.
SOS结构:
| Name | Length(Bytes) | Description |
|---|---|---|
| SOS | 2 | 0xFFDD |
| SOS长度 | … | * |
| 该扫描所包含颜色分量数 | 1 | 1(灰度图)/3(YCrCb)/4(CMYK) |
| 颜色分量信息 | (颜色分量数)*2 | 颜色ID(1B):从1计数 霍夫曼表表号:高4位DC表号,低4位AC表号 |
| 扫描频谱起始 | 1 | 扫描每一个数据单元(data unit)经DCT变换后的频段的开始 |
| 扫描频谱结尾 | 1 | 扫描每一个数据单元(data unit)经DCT变换后的频段的结尾 |
| 逐次逼近系数 | 1 | 仅针对渐进式编码定义:高4位为前一次(该频段)扫描的该系数(初值0),低四位为本次扫描的系数 |
在SOS之后就是压缩的数据流,JPEG格式定义:若扫描中出现了段标记的开头字节0xFF,必须在之后添加额外的0x00以表示0xFF是数据流的一部分.若扫描结束时不在对齐的字节上,则该字节剩余比特均为1,假如这恰好构成了一个0xFF,同样也要在结尾添加0x00.
扫描若有多个颜色分量,我们称之为交叉的(interleaved),否则称为非交叉的(non-interleaved).是否交叉扫描会对MCU大小的计算造成影响,具体是:若扫描非交叉,则只有单个颜色分量被扫描,MCU大小就是数据单元(data unit)的大小(8*8),否则需要根据扫描中所有颜色分量的采样系数计算MCU大小,这一点我们在下一节会详细介绍.
对于逐次逼近系数,若被定义且前一次扫描的系数不为0,则在渐进式JPEG中,本次扫描的系数只能为 上一次系数-1。
1.8本节总结
以上就是基线与渐进式JPEG中常见的标记与段,但是仅仅根据这些信息无法得出JPEG具体的解码方法.我们将在下面两节分别详细介绍基线JPEG与渐进式JPEG的编解码过程.
2 基线(Baseline)JPEG解码方法
待更
3 渐进式(Progressive)JPEG解码方法
待更
(以下为未整理的废稿)
1 | //以下是在一次 Refined AC Scan 中,对一块 8 * 8 块进行操作的伪代码: |
0.5 test images
下面是zigzag变换的顺序:
END
参考文献:
[1]itu-t81.pdf
[2]JPEG编码过程详解
[3]JPEG中的范式霍夫曼编码
[4]JPEG编解码过程详解
[5]参考书:Compressed Image File Formats JPEG, PNG, GIF, XBM, BMP (John Miano)