LV01-图像-03-图片格式-03-03-JPEG协议介绍

本文主要是图片格式——JPEG协议介绍的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
PC端开发环境 Windows Windows11
Ubuntu Ubuntu20.04.6的64位版本
VMware® Workstation 17 Pro 17.0.0 build-20800274
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
Win32DiskImager Win32DiskImager v1.0
Linux开发板环境 Linux开发板 正点原子 i.MX6ULL Linux 阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
点击查看本文参考资料
点击查看相关文件下载
------

这一节的笔记我们来学习JPEG文件的格式。

一、JPEGsnoop

1. 简介

JPEGsnoop是一个详细的JPEG图像解码器和分析工具。它报告所有图像元数据,甚至可以帮助识别图像是否被编辑过。它有以下特点(这里我就没翻译了):

  • Decode JPEG, AVI (MJPG), PSD images
  • MCU analysis with detailed decode
  • Extract embedded JPEG images
  • Detect edited images through compression signature analysis
  • Report all image metadata (EXIF)
  • Batch file processing
  • Compression signature detection
  • Recover corrupt JPEG image data
  • No installation required

官方Github仓库应该是这个:GitHub - ImpulseAdventure/JPEGsnoop: JPEGsnoop: JPEG decoder and detailed analysis

2. 下载与安装

我们下载一下安装包,可以直接从发布版本中找:Releases · ImpulseAdventure/JPEGsnoop (github.com)

image-20240725074043718

我们直接下载第一个好了,下载后解压,可以得到这样一个文件:

image-20240725074226505

这个软件是免安装的,直接双击打开即可。

image-20240725074328368

我们打开一个jpeg文件看一下,我们这里就用之前自己生成的jpg文件,像素点少,后面可以更好的查看数据:

format-30x13

打开后如下图所示:

image-20240725075107599

二、JPEG文件中段的概念

1. 如何识别JPEG文件?

我们平时是靠.jpg.jpeg等后缀名来识别,看到这个后缀,我们就认为是一张JPEG图片,但是还是会有一些图片是没有后缀的,依然能被识别,这是怎么做的呢?

其实很简单,就是判断前面3个字节是什么,如果发现是FF D8 FF开始,那就认为它是JEPG图片。JPG文件是由一段段的数据构成的组成的(segment),段的多少和长度并不是一定的。只要包含了足够的信息,该JPEG文件就能够被打开。

2. 段结构

上面了解到JPEG 文件的格式是分为一个一个的段来存储的,段的多少和长度并不是一定的。只要包含了足够的信息,该JPEG文件就能够被打开,呈现给我们。接下来我们就开始学习一下段的相关基础知识。

JPEG文件的每个段都一定包含两部分:一个是段的标识,它由两个字节构成:第一个字节是十六进制0xFF,第二个字节对于不同的段,这个值是不同的。紧接着的两个字节存放的是这个段的长度(除了前面的两个字节0xFF和0xXX,X表示不确定。他们是不算到段的长度中的)。

注意:这个长度的表示方法是按照高位在前,低位在后的,与 Intel 的表示方法不同。比方说一个段的长度是0x12AB,那么它会按照0x12,0xAB的顺序存储。但是如果按照Intel的方式:高位在后,低位在前的方式会存储成0xAB,0x12,而这样的存储方法对于JPEG是不对的。这样的话如果一个程序不认识JPEG文件某个段,它就可以读取后两个字节,得到这个段的长度,并跳过忽略它。

段的一般结构如下:

image-20240726074235919
1
2
3
4
5
6
7
8
-----------------------------------------------------------------
名称 字节数 数据 说明
-----------------------------------------------------------------
段标识 1 FF 每个新段的开始标识
段类型 1 类型编码(称作“标记码”)
段长度 2 包括段内容和段长度本身,不包括段标识和段类型
段内容 ≤65533字节
————————————————

①JPG 文件中所有关于宽度高度长度间隔这一类数据,凡是>1字节的,均采用Motorola格式,即:高位在前,低位在后。

②有些段没有长度描述也没有内容,只有段标识和段类型。文件头和文件尾均属于这种段。

③段与段之间无论有多少FF都是合法的,这些FF称为“填充字节”,必须被忽略掉。

3. 段的标识与类型

image-20240726074235919

它由两个字节构成:第一个字节是段标识0xFF,第二个字节是段类型(对于不同的段是不同的),具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
段类型   表示符号      说明
——————————————————————————————————
SOI D8 文件头(必须)

APP0 E0 定义交换格式和图像识别信息(非必需)
APP1 E1 同上
.... .... ....
APPn En 同上

DQT DB 定义量化表(必须)

SOF0 C0 图像基本信息
SOF1 C1 同上
.... .... ....
SOFn Cn 同上

DHT C4 定义 Huffman 表(霍夫曼表)
DRI DD 定义重新开始间隔(非必需)
SOS DA 扫描行开始
COM FE 注释(非必需)
EOI D9 文件尾
......

段类型大概有30种,但上面那些是必须被所有程序识别的,其它的类型都可以忽略。

常见的段:

  1. SOI(必须):SOI段定义了文件头。该段仅有两个字节:FF D8,这两个字节代表了JPEG文件的开始。
  2. APP0, APP1, APP2….(必须):APP0段定义了交换格式和图像识别信息。包含了一些像素和略缩图相关的信息。
  3. DQT(必须):DQT段定义了量化表。JPEG文件一般有两个DQT段,为亮度定义1个, 为色度定义1个。
  4. SOF0, SOF1, SOF2….(必须):SOF段定义了图像基本信息。包含了一些图片长宽高、组件等信息。
  5. DHT(必须) :DHT段定义了Huffman表,即霍夫曼编码表。
  6. DRI(非必须):DRI段定义了重新开始间隔,很多JPEG文件没有这个段。
  7. SOS(必须):SOS段定义了扫描行开始,紧接着SOS段后的就是一个个扫描行(压缩的图像数据)。
  8. COM(非必须):COM段定义了注释段,有的JPEG文件没有这个段。
  9. EOI(必须):EOI段定义了文件尾,该段仅有两个字节:FF D9,这两个字节代表了JPEG文件的结束。

4. 段的长度

image-20240726074235919

紧接着的两个字节存放的是这个段的长度(除了前面的两个字节0xFF和0xXX,X表示不确定。他们是不算到段的长度中的)。其作用是这样的话如果一个程序不认识JPEG文件某个段,它就可以读取后两个字节,得到这个段的长度,并跳过忽略它。

三、一般有哪些段?

1. SOI——文件头

1.1 基本结构

JPEG文件的开始2个字节都是FF D8这是JPEG协议规定的:

1
2
3
4
5
6
-----------------
名称 字节数 值
-----------------
段标识 1 FF
段类型 1 D8
-----------------

1.2 实例分析

我们可以打开之前准备的那个format-30x13.jpg文件看一下:

image-20240726074942254

我们可以使用JPEGsnoop工具打开图片看一下:

image-20240727093445827

2. APP0——图像识别信息

2.1 基本结构

APP0的基本结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
--------------------------------------------------------------------------
名称 字节数 值 说明
--------------------------------------------------------------------------
段标识 1 FF
段类型 1 E0
段长度 2 00 10 如果有RGB缩略图就=16+3n

(以下为段内容)
交换格式 5 4A46494600 “JFIF”的ASCII码
主版本号 1
次版本号 1
密度单位 1 0=无单位;1=点数/英寸;2=点数/厘米
X像素密度 2 水平方向的密度
Y像素密度 2 垂直方向的密度
缩略图X像素 1 缩略图水平像素数目
缩略图Y像素 1 缩略图垂直像素数目

(如果“缩略图X像素”和“缩略图Y像素”的值均>0,那么才有下面的数据)
RGB缩略图 3×n n=缩略图像素总数=缩略图X像素×缩略图Y像素
--------------------------------------------------------------------------

(1)JFIF是JPEG File Interchange Forma的缩写,即JPEG文件交换格式,另外还有TIFF等格式,很少用。

(2)“如果有RGB缩略图就=16+3n”是什么意思呢?比如说“缩略图X像素”和“缩略图Y像素”的值均为48,就表示有一个48×48像素的缩略图(n=48×48),缩略图是24位真彩位图,用3个字节来表示一个像素,所以共占用3n个字节。但大多数JPG文件都没有这个“鸡肋”缩略图。

2.2 实例分析

image-20240727091512156

(1)图像识别信息头:2个字节FF E0。

(2)段长度:00 10 也就是16个字节。

(3)交换格式5个字节:4A 46 49 46 00,此处代表‘JFIF’,一般我们用的都是JFIF的jpeg交换格式,但是也有TFIF的jpeg交换格式,如果camera sensor直接输出的是jpeg图片,那么在其sensor寄存器可以设置使用JFIF还是TFIF。

(4)主版本号和次版本号一共2个字节: 01 01 说明主版本号和此版本号都为1。

(5)单位密度1个字节:01 表示此处使用的是点数/英寸。

(6)X像素密度2个字节:00 a8表示水平像素密度是168。

(7)Y像素密度2个字节:00 a8表示垂直像素密度是168。

(8)缩略图X像素:00 表示没有缩略图。

(9)缩略图Y像素:00 表示没有缩略图。

(10)RGB缩略图:此处没有缩略图,所以是空的。

可以拿JPEGsnoop工具打开验证一下:

image-20240727093516502

3. DQT——定义量化表

3.1 基本结构

DQT,Define Quantization Table量化表:

1
2
3
4
5
6
7
8
9
10
11
--------------------------------------------------------------------------
名称 字节数 值 说明
--------------------------------------------------------------------------
段标识 1 FF
段类型 1 DB
段长度 2 43 其值=3+n(当只有一个QT时)

(以下为段内容)
QT信息 1 [0:3]位:QT号 [4:7]位:QT精度(0=8bit,1字节;否则=16bit,2字节)
QT n n=64×QT精度的字节数
--------------------------------------------------------------------------

(1)JPEG文件一般有2个DQT段,为Y值(亮度)定义1个, 为C值(色度)定义1个。

(2)一个DQT段可以包含多个QT, 每个都有自己的信息字节。

3.2 编码质量控制

使用不同的量化表可以对JPEG的编码质量进行控制:

image-20240729075256730

3.3 实例分析

image-20240727104257900

可以看到这里有两个量化表,此处有2个DQT数据,第一个是亮度的,第二个是色度的。以第一个亮度的为例:

(1)定义量化表的头2个字节:FF DB

(2)段长度2个字节:00 43就是3(段长度2个字节,QT信息1个字节)+QT量化表的长度,此处QT量化表的长度是64

(3)QT信息1个字节:以第一个量化表为例子00中0-3位是QT号,4-7位QT精度,此处是0,所以精度是8bit,即1个字节。以第二个量化表为例子 01就表示QT号是1,精度是8bit。

(4)QT量化表:这个的长度是根据QT信息确定的,上面QT精度为8bit,所以此处是64×1 = 64个字节
我们使用前面的JPEGsnoop工具打开看一下:

image-20240727093046246

4. SOF0——图像基本信息

4.1 基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
--------------------------------------------------------------------------
名称 字节数 值 说明
--------------------------------------------------------------------------
段标识 1 FF
段类型 1 C0
段长度 2 其值=8+组件数量×3

(以下为段内容)
样本精度 1 8 每个样本位数(大多数软件不支持12和16)
图片高度 2
图片宽度 2
组件数量 1 3 1=灰度图,3=YCbCr/YIQ 彩色图,4=CMYK 彩色图

(以下每个组件占用3字节)
组件 ID 1 1=Y, 2=Cb, 3=Cr, 4=I, 5=Q
采样系数 1 0-3位:垂直采样系数,4-7位:水平采样系数
量化表号 1
---------------------------------------------------------------------------

(1)JPEG大都采用YCrCb色彩模型(Y表示亮度,Cr红色分量,Cb表示蓝色分量),所以组件数量一般=3

(2)样本就是单个像素的颜色分量,也可理解为一个样本就是一个组件.

(3)采样系数是实际采样方式与最高采样系数之比,而最高采样系数一般=0.5(分数表示为1/2)。比如说,垂直采样系数=2,那么2×0.5=1,表示实际采样方式是每个点采一个样,也就是逐点采样;如果垂直采样系数=1,那么:1×0.5=0.5(分数表示为1/2),表示每2个点采一个样

4.2 实例分析

image-20240727105201634

(1)图像基本信息的:2个字节FF C0

(2)段长度2个字节:00 11 就是 17 = 8 + 3*3,说明组件数量有3个。

(3)样本精度1个字节:08,每个样本的信息是8bit。

(4)样本高度2个字节:00 0d = 13,图片高度与实际一致。

(5)样本宽度2个字节:00 1e = 30,图片宽度与实际一致。

(6)组件数量1个字节:03 代表YCbCr 彩色图,有3个组件分别是Y、Cb、Cr。

(7)每个组件占用3个字节:第一个字节是组件ID,第二个字节是采样系数,第三个字节是量化表号。此处是:

01 22 00 表示 Y组件,垂直采样系数和水平采样系数都是2,量化表号是0;02 11 01表示 Cb组件,垂直采样系数和水平采样系数都是1,量化表号是1;03 11 01表示 Cr组件,垂直采样系数和水平采样系数都是1,量化表号是1。此处可知此处Y采样是逐点采样,CbCr都是隔点采样,这就是标准的YUV422的数据。

image-20240727105617732

5. DHT——定义huffman表

5.1 基本结构

1
2
3
4
5
6
7
8
9
10
11
12
--------------------------------------------------------------------------
名称 字节数 值 说明
--------------------------------------------------------------------------
段标识 1 FF
段类型 1 C4
段长度 2 其值=19+n(当只有一个HT表时)

(以下为段内容)
HT信息 1 [0:3]位:HT号; [4]位:HT类型, 0=DC表,1=AC表; [5:7]位:必须=0
HT位表 16 这16个数的和应该≤256,分别表示1-16bit位数码字的数量
HT值表 n n=表头16个数的和
--------------------------------------------------------------------------

(1)JPEG文件里有2类Haffman 表:一类用于DC(直流量),一类用于AC(交流量)。一般有4个表:亮度的DC和AC,色度的DC和AC。最多可有6个。

(2)一个DHT 段可以包含多个HT表, 每个都有自己的信息字节

(3)HT表是一个按递增次序代码长度排列的符号表。

5.2 实例分析

image-20240728093412783

(1)定义Huffman表头2个字节:FF C4

(2)段长度2个字节:以①为例子 00 1F就是31 = 19(段长度2个字节+HT信息1个字节+HT位表16个字节) + 12(这个数代表HT表有12个字节)

(3)HT信息1个字节:[0:3]位是HT号,[4]位是HT类型(0=DC表,1=AC表),[5:]7位必须为0。①中 00 表示 HT号是0,DC表;②中 10 表示 HT号是0,AC表;以③为例子 01 表示 HT号是1,DC表;④中 11表示 HT号是1,AC表。此处一共有4个Haffman 表,亮度DC Haffman 表,亮度AC Haffman 表,色度DC Haffman 表,色度AC Haffman 表

(4)HT位表16个字节:这16个数字值和小于等于256。①中 00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 共16个字节,加起来是12(此处和段长度是相匹配的),说明HT表有12个字节。

(5)HT值表:以①为例子00 01 02 03 04 05 06 07 08 09 0A 0B。

我们使用JPEGsnoop工具打开看一下,

image-20240728093813473

6. SOS——扫描行开始

6.1 基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--------------------------------------------------------------------------
名称 字节数 值 说明
--------------------------------------------------------------------------
段标识 1 FF
段类型 1 DA
段长度 2 000C 其值=6+2×扫描行内组件数量

(以下为段内容)
扫描行内组件数量 1 3 必须≥1,≤4(否则错误),通常=3

(以下每个组件占用2字节)
组件ID 1 1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q
Huffman表号 1 [0:3]位:AC表号 (其值=0...3); [4:7]位:DC表号(其值=0...3)
剩余3个字节 3 最后3个字节用途不明,忽略
--------------------------------------------------------------------------

紧接SOS段后的是压缩的图像数据(一个个扫描行),数据存放顺序是从左到右、从上到下。

6.2 实例分析

image-20240728095450833

(1)扫描行开始的头,2个字节:FF DA

(2)段长度2个字节:00 0C 就是 12 = 6(2个字节的扫描行开始头+1个字节扫描行内组件数量+3个字节的剩余位) + 2×3(扫描行内组件数量,每个组件2个字节)

(3)扫描行内组件数量1个字节:03 代表组件数量数3

(4)每个组件占用2个字节:第一个字节是组件ID(1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q);第二个字节[0:3]位AC表号,[4:7]位DC表号。表号的值是0-3。01 00 表示 Y组件,AC表号是0,DC表号是0;02 11表示 Cb组件,AC表号是1,DC表号是1;03 11 表示 Cr组件,AC表号是1,DC表号是1。此处跟DHT(定义Huffman表)的HT信息是一致的。

image-20240728101513739

7. EOI——文件尾

7.1 基本结构

1
2
3
4
5
6
------------------
名称 字节数 值
------------------
段标识 1 FF
段类型 1 D9
------------------

7.2 实例分析

image-20240728101645437

这个就没啥好说的了,就在文件结尾。

image-20240728101719062

8. DRI——定义重新开始间隔

8.1 基本结构

DRI, Define Restart Interval,复位的间隔:

1
2
3
4
5
6
7
8
9
10
--------------------------------------------------------------------------
名称 字节数 值 说明
--------------------------------------------------------------------------
段标识 1 FF
段类型 1 DD
段长度 2 4

(以下为段内容)
开始间隔 2 n 复位标记的间隔距离
---------------------------------------------------------------------------

(1)开始间隔表示在压缩数据流中,每隔n个MCU 块就有一个RST标记,RST标记将Huffman 的解码数据流复位,DC也重新从0开始,因此,RST标记是一种复位标记。(表示每隔多少个MCU 重置一次差分编码值,一组MCU称为一个slice,可独立解码, 多个slice之间通过RSTn进行分割)。

(2)RST 标记是一种特殊的段,它只具有段标识和段类型(长度=2字节),但它不是独立的段,只能穿插在数据流中(文件头和文件尾段也只有段标识和段类型,却都是独立的段)。

(3)RST标记共有8个(RST0-RST7),从RST0起开始使用,然后是RST1….直至RST7,再从RST0重复。

(4)RST标记的标识码是 FFD0-FFD7,对应 RST0-RST7。

说明:这个不是必须段,很多JPEG都没有。

8.2 实例分析

之前用的jpg图片没有这个信息,网上找了个,但是只有截图,没见原图,这里参考一下好啦:

image-20240728102229429

四、JPEG标准与文件格式

我们重新看一下JPEG的概念:JPEG是联合图象专家组(Joint Picture Expert Group)的英文缩写,是国际标准化组织(ISO)和CCITT联合制定的静态图象的压缩编码标准

我们通常说的JPEG指的是以JPEG格式压缩的图片(即文件后缀为.jpeg .jpe )。经过JPEG重新编码的图片,文件压缩率可以达到90%以上,而且图片本身还具有较好的图片质量。这也是JPEG成为目前互联网上被用来存储和传输图片应用最广泛的格式的一个重要原因。

JPEG本身只有描述如何将一个视频/图片转换为字节的数据流(streaming),但并没有说明这些字节如何在任何特定的存储媒体上被封存起来。

很多人会把JPEG标准和JPEG文件格式理解成一个东西。然而实际并不是这样的,JPEG标准主要还是围绕编解码的部分(如DCT变换、量化、哈夫曼树等等),虽然在JPEG标准中也定义了“JPEG Interchange Format (JIF)”的文件存储格式,但是因为Encoder和Decoder完整实现 JIF 很困难,且 JIF 标准也存在一些缺陷,因此JIF并没有被推广开来。倒是后来出现的“JPEG File Interchange Format (JFIF)” 和 “**Exchange image file Format(**Exif)” 等新的存储格式成为了主流。Exif 也好 JFIF 也罢,他们都是遵循 JIF 标准的,两者只是在 JIF 的基础上增加了一些各自的Marker。

  • JPEG是压缩标准,JPEG/JFIF和JPEG/Exif是文件格式标准,不是一个概念,需要注意区分。
  • JPEG/Exif文件格式标准是Camera产业联合会发布,主要用于摄像设备上,摄像产业把Exif作为行业的元数据(metadata)交换格式
  • JPEG/JFIF文件格式标准是为了方便JPEG压缩图像在广泛的平台和应用间以最小的存储空间代价进行交换而设计的,它不包含JPEG/TIFF标准任何高级特性。

上面学习JPEG中段的概念的时候,实例使用的就是一张符合JFIF格式标准的jpg图片。

五、多Slice拼接

image-20240730073730286