JPEG格式研究——(2)JPEG文件格式

news/2024/11/16 5:35:21/文章来源:https://www.cnblogs.com/Ryan1202/p/18397454

JPEG文件除了图像数据之外,还保存了与图片相关的各种信息,这些信息通过不同类型的TAG存储在文件中。

TAG

JPEG通过TAG标记压缩书记之外的信息。所有的TAG都包含一个TAG类型,TAG类型大小为两个字节,位于一个TAG的最前面。TAG类型的第一个字节一定为0xFF

以下是部分常见的TAG类型

TAG类型 数值 备注
SOF0 ~ SOF3
SOF5 ~ SOF7
SOF8 ~ SOF11
SOF13 ~ SOF15
FF C0 ~ FF C3
FF C5 ~ FF C7
FF C8 ~ FF CB
FF CD ~ FF CF
不同的编码模式对应不同的SOF,详见JPEG标准Table B.1。本文以常见的SOF0为研究对象
DHT FF C4 Define Huffman table(s),定义了解码所需的哈夫曼表
RSTm FF D0 ~ FF D7 遇到时重置DC系数,数字会递增,具体含义不清楚
SOI FF D8 Start of Image,表示图像文件的开始
EOI FF D9 End of Image,表示图像文件结束
SOS FF DA Start of scan,表示这一个TAG后就是压缩的图像数据,记录了DHT、DQT与图像不同部分的对应关系
DQT FF DB Define quantization table(s),定义了解码所需的量化表
APP0 ~ APP15 FF E0 ~ FF EF 应用保存的图片相关信息(如相机信息等)

JPEG文件中各种数据的分布

压缩数据中也是存在TAG的,虽然大部分TAG都在文件开头,但是也有少部分是例外。如EOI就在文件的末尾,RSTm会出现在压缩数据当中。

那么问题来了,如果压缩数据中有一个字节本身就是0xFF怎么办?JPEG的做法是在0xFF后面再加一个字节0x00,用于表示这不是一个TAG。

因为不知道解码需要哪些TAG,我在尝试写JPEG解码器的时候耗费了大量的时间在研究TAG上。总结出对于常见的JPEG图片解码需要的TAG:

1.SOI和EOI:用于确定文件的开头和结尾

2.DQT和DHT:保存了解码时需要用到的哈夫曼表和量化表

3.SOS:保存了图片不同部分的需要用哪个哈夫曼表

4.SOF:图片的长和宽、采样精度等、使用哪个量化表都保存在SOF中

5.RST:部分图片存在RST,遇到RST时要重置DC系数才能得到正确的图像

哈夫曼表与量化表

这里有个小坑,我原先一直以为DQT和DHT都是一个TAG对应一个表,后来发现一个TAG可以不只一个表

哈夫曼表

哈夫曼表的存储格式如下:

DHT格式

名称 长度(bit) 备注
$Lh$ 16 表示这一个TAG的长度(包括TAG类型的两个字节)
$Tc$ 4 Table class,0=DC表,1=AC表
$Th$ 4 Huffman table destination identifier,表示该哈夫曼表的id
$L_i$ 8 表示这一长度的编码个数
$V_{i,j}$ 8 表示编码前的原始数据

一个DHT中的哈夫曼表个数可以通过长度Lh算出:

$$
Lh = 2 + \sum_{t=1}^{n} (17+m_t)
$$

其中

$$
m_t=\sum_{1}^{16} L_i
$$

除了TAG类型和Lh一个TAG只有一个外,其余的都是每个哈夫曼表都有的。

Th之后是一个长度为16字节的数组,分别对应长度从1bit到16bit的编码个数。

再之后存的是各个编码对应的原始数据(以字节为单位)。JPEG采用的范式哈夫曼编码,可以这些信息推导出数据编码前后的对应关系。

量化表

DQT的结构与DHT结构相似,比DHT还稍微简单一些
DQT格式

名称 长度(bit) 备注
Lq 16 与Lh意义相同,表示这一TAG的长度
Pq 4 量化表的精度,0=8bit,1=16bit
Pq 4 量化表的id
Qk 8 量化表中的数据

量化表大小固定为8x8,也就是一个表有64个数,DQT长度与量化表个数也有类似的关系:

$$
Pq = 2 + \sum_{t=1}^n (65 + 64 \times Pq(t))
$$

SOF

SOF(Start of Frame) TAG的结构如下:

SOF格式

名称 长度(bit) 备注
Lf 16 这一TAG的长度
P 8 采样精度
Y 16 图片的高度
X 16 图片的宽度
Nf 4 Component的个数

Component的结构如下:

名称 长度(bit) 备注
Ci 8 Compoenent的id
Hi 4 水平缩放因子
Vi 4 垂直缩放因子
Tqi 8 对应的量化表id

根据我的理解,这里的Component个数相当于色度分量的个数,比如RGB和YUV都是3,灰度图像则是1.

SOS

SOS格式

名称 长度(bit) 备注
Ls 16 这一TAG的长度
Ns 8 一个scan内的component数量
Ss 8 没用
Se 8 没用
Ah 4 没用
Al 4 没用

Scan中还描述了这一Scan内不同Component中哈夫曼表和量化表的对应关系:

名称 长度(bit) 备注
Csi 8 通过id选择Component
Tdi 4 通过id选择DC哈夫曼表
Tai 4 通过id选择AC哈夫曼表

到这里JPEG解码所需要的几个重要TAG的结构就介绍完了,接下来就做好准备工作就可以开始解码了。


参考资料

JPEG解码系列博客:多媒体-编解码 - 随笔分类 - OnlyTime_唯有时光 - 博客园 (cnblogs.com)

JPEG标准:Microsoft Word - T081E.DOC (w3.org)

一个Rust写的JPEG解码器:MROS/jpeg_tutorial: 跟我寫 JPEG 解碼器 (Write a JPEG decoder with me) (github.com)

友情链接

我学习过程中写的JPEG图片查看器:Ryan1202/my-tiny-jpeg-viewer: A Tiny Jpeg Viewer (github.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/792317.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【python】一篇搞懂多解释器与虚拟环境

一.同一PC上,python同时支持多个版本的解释器共存1.1:安装不同版本python解释器(参考百度资料)# python安装后,解释器目录介绍 C:\python39- python.exe [解释器]-Scripts- pip.exe- pip3.exe- pip3.9.exe- Lib- re.py- random.py 内置模块- site-packages- requests …

LeeCode-104. 二叉树的最大深度

要求给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 如下图所示的二叉树最大深度为5.解题思路与94题类似,采用递归调用遍历子节点。在基本结构中,节点的最大深度等于根深度(1)加上左右较大深度,左右较大的深…

# LeeCode-104. 二叉树的最大深度

要求给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 如下图所示的二叉树最大深度为5.解题思路与94题类似,采用递归调用遍历子节点。在基本结构中,节点的最大深度等于根深度(1)加上左右较大深度,左右较大的深…

遥感技术在环境监测中的应用:揭秘地球变化的天眼

当我们仰望星空,探索宇宙的奥秘时,别忘了脚下的这片土地同样蕴藏着无数未解之谜。遥感技术,这个听起来似乎遥不可及的名字,其实正是我们透视地球环境变化的“天眼”。今天将带大家一探遥感技术如何在环境监测中大显身手,帮助我们更精准地监测和评估这个星球的每一次呼吸与…

c#程序反编译

工具ilspy https://nchc.dl.sourceforge.net/project/ilspy.mirror/v9.0-preview2/ILSpy_binaries_9.0.0.7660-preview2-arm64.zip?viasf=1 visualstudio反编译方法将需要反编译的程序拖入ilspy 选择vs版本导出项目 右键项目→保存代码vs打开项目文件即可添加库方法反编译库,…

LeeCode-94. 二叉树的中序遍历

基本概念二叉树二叉树的结构如上图所示,由一系列左-中-右节点组成的树状数据结构,其基本结构如下所示,由一个中间节点向左右分叉成两个节点,故称二叉树。中序遍历看二叉树基本的结构左-中-右三个节点,中间为Root,左边为Left,右边为Right。按顺序排列的话有C(3,2)=6种,其…

C#自定义控件—仪表盘

C#用户控件之仪表盘 如何让温度、湿度、压力等有量程的监控值如仪表盘(DashBoard)一样显示?思路(GDI绘图): 定义属性:(仪表盘的半径、颜色、间隙;刻度圆的半径、颜色、字体;指针的颜色、占比;文本的字体、占比;) 绘制图形:(半圆、刻度、指针、中心、文本)定义属…

Linkedlist源码详解

介绍 LinkedList同时实现了List接口和Deque接口,也就是说它既可以看作一个顺序容器,又可以看作一个队列(Queue),同时又可以看作一个栈(Stack)。这样看来,LinkedList简直就是个全能冠军。当你需要使用栈或者队列时,可以考虑使用LinkedList,一方面是因为Java官方已经声明不…

创建一个SpringBoot项目,实现简单的CRUD功能和分页查询

背景 本博文主要是创建了一个新的SpringBoot项目,实现基本的增删改查,分页查询,带条件的分页查询功能。是方便初学者学习后端项目的一个比较清晰明了的实践代码,读者可根据博文,从自己动手创建一个新的SpringBoot项目,到使用PostMan测试基本请求,完完全全实践一遍,写出…

每天5分钟复习OpenStack(十五)Ceph与Bcache结合

上一章我们成功部署了bcache,这一章我们将Ceph与Bcache结合来使用,使用Bcache来为ceph的数据盘提速。1 ceph 架构 一个标准的ceph集群可能是如下的架构,SSD/NVME 存储元数据,而SATA盘存储数据。这样的架构下,物理介质的SATA盘读写速率上限决定了存储集群Ceph的上限(木桶效…

青岛-烟台-威海攻略

烟台 朝阳街-烟台山-所城里朝阳街 漫心酒店外小熊烟台山 烟台开埠陈列馆 烟台京剧艺术馆 冰心纪念馆 烟台地标 烟台山灯塔 旗袍博物馆所城里 宣化城墙金沙滩-滨海中路-渔人码头金沙滩城市展示中心 新城北街养马岛 可以在岛上租电驴 需要一整天酒店推荐烟台站附近 Day1 芝罘湾广…