JPEG文件编/解码详解

JPEG文件编/解码详解(1


JPEG(Joint Photographic Experts Group)是联合图像专家小组的英文缩写。它由国际电话与电报咨询委员会CCITT(The International Telegraph and Telephone Consultative Committee)与国际标准化组织ISO于1986年联合成立的一个小组,负责制定静态数字图像的编码标准。

小组一直致力于标准化工作,开发研制出连续色调、多级灰度、静止图像的数字图像压缩编码方法,即JPEG算法。JPEG算法被确定为国际通用标准,其适用范围广泛,除用于静态图像编码外,还推广到电视图像序列的帧内图像压缩。而用JPEG算法压缩出来的静态图片文件称为JPEG文件,扩展名通常为*.jpg、*.jpe*.jpeg。

JPEG专家组开发了两种基本的压缩算法、两种数据编码方法、四种编码模式。具体如下:

压缩算法:

l 有损的离散余弦变换(Discrete Cosine Transform,DCT);

l 无损的预测技术压缩。

数据编码方法:

l 哈夫曼编码;

l 算术编码;

编码模式:

l 基于DCT顺序模式:编/解码通过一次扫描完成;

l 基于DCT递进模式:编/解码需要多次扫描完成,扫描效果从粗糙到精细,逐级递进;

l 无损模式:基于DPCM,保证解码后完全精确恢复到原图像采样值;

l 层次模式:图像在多个空间多种分辨率进行编码,可以根据需要只对低分辨率数据作解码,放弃高分辨率信息。

在实际应用中,JPEG图像使用的是离散余弦变换、哈夫曼编码、顺序模式。

JPEG压缩编码算法的主要计算步骤如下:

(0) 8*8分块。

(1) 正向离散余弦变换(FDCT)。

(2) 量化(quantization)。

(3) Z字形编码(zigzag scan)。

(4) 使用差分脉冲编码调制(DPCM)对直流系数(DC)进行编码。

(5) 使用行程长度编码(RLE)对交流系数(AC)进行编码。

(6) 熵编码。

笔者在实践过程中查阅了大量的资料,发现大多数书籍资料和网上资料都是从编码角度分析JPEG的编/解码方式,并且都只是介绍编码过程中的主要方法。所以,本文从解码角度详细分析JPEG的编/解码过程,并且加入许多笔者实践过程中遇到的问题和解决方法,希望从另一个角度说明问题,以更好帮助读者结合其他资料解决问题。

不过,介绍解码过程之前,首先要了解JPEG文件中数据的存储格式。

一、JPEG文件格式介绍

JPEG文件使用的数据存储方式有多种。最常用的格式称为JPEG文件交换格式(JPEG File Interchange Format,JFIF)。而JPEG文件大体上可以分成两个部分:标记码(Tag)和压缩数据。

标记码由两个字节构成,其前一个字节是固定值0xFF,后一个字节则根据不同意义有不同数值。在每个标记码之前还可以添加数目不限的无意义的0xFF填充,也就说连续的多个0xFF可以被理解为一个0xFF,并表示一个标记码的开始。而在一个完整的两字节的标记码后,就是该标记码对应的压缩数据流,记录了关于文件的诸种信息。

常用的标记有SOIAPP0DQTSOF0DHTDRISOSEOI

注意,SOI等都是标记的名称。在文件中,标记码是以标记代码形式出现。例如SOI的标记代码为0xFFD8,即在JPEG文件中的如果出现数据0xFFD8,则表示此处为一个SOI标记。

本文附录列出一张完整的JPEG定义的标记表,供读者查阅。这里仅列出几个常用标记的标记代码、占用字节长度和表示的意义。

l         SOI,Start of Image,图像开始

u  标记代码    
                           
2字节     固定值0xFFD8

l       APP0,Application,应用程序保留标记0

u  标记代码    
                           
2字节     固定值0xFFE0

u  包含9个具体字段:
 
① 数据长度   
                    
2字节     ①~⑨9个字段的总长度
                                      
                    
即不包括标记代码,但包括本字段
 
② 标识符       
                    
5字节    固定值0x4A46494600,即字符串“JFIF0”
 
③ 版本号       
                    
2字节    一般是0x0102,表示JFIF的版本号1.2
  
                                   
                    
可能会有其他数值代表其他版本
 
④ X和Y的密度单位   
       1字节    
只有三个值可选
                                                    
       0:无单位;1:点数/英寸;2:点数/厘米
 
⑤ X方向像素密度       
       2字节    
取值范围未知
 
⑥ Y方向像素密度       
       2字节    
取值范围未知  
 
⑦ 缩略图水平像素数目
       1字节    
取值范围未知
 
⑧ 缩略图垂直像素数目
       1字节    
取值范围未知
 
⑨ 缩略图RGB位图     
       长度可能是3的倍数   
       缩略图RGB位图数据

本标记段可以包含图像的一个微缩版本,存为24位的RGB像素。如果没有微缩图像(这种情况更常见),则字段⑦“缩略图水平像素数目”和字段⑧“缩略图垂直像素数目”的值均为0。

l       APPn,Application,应用程序保留标记n,其中n=1~15(任选)

u  标记代码    
                           
2字节     固定值0xFFE1~0xFFF

u  包含2个具体字段:
 
① 数据长度   
      
             
2字节     ①~②2个字段的总长度
                                      
                    
即不包括标记代码,但包括本字段
 
② 详细信息    
       数据长度-2字节   内容不定

例如,Adobe Photoshop生成的JPEG图像中就用了APP1和APP13两个标记段分别存储了一幅图像的副本。

l       DQT,Define Quantization Table,定义量化表

u  标记代码    
                    
2字节    
       固定值0xFFDB

u  包含9个具体字段:
 
① 数据长度   
             
2字节    
       字段①和多个字段②的总长度
                                      
                    
即不包括标记代码,但包括本字段
 
② 量化表
       数据长度-2字节

a)        
精度及量化表ID   1字节    
       高4位:精度,只有两个可选值
                                               
             
0:8位;1:16位
                                        
       低4位:量化表ID,取值范围为0~3

b)       
表项       (64×(精度+1))字节             
例如8位精度的量化表
                                               
其表项长度为64×(0+1)=64字节

本标记段中,字段②可以重复出现,表示多个量化表,但最多只能出现4次。

l       SOF0,Start of Frame,帧图像开始

u  标记代码    
             
2字节     固定值0xFFC0

u  包含9个具体字段:
 
① 数据长度   
       2字节    
①~⑥六个字段的总长度
                                      
       即不包括标记代码,但包括本字段
 
② 精度         
       1字节    
每个数据样本的位数
                                      
       通常是8位,一般软件都不支持 12位和16位
 
③ 图像高度   
       2字节    
图像高度(单位:像素),如果不支持 DNL 就必须 >0
 
④ 图像宽度   
       2字节    
图像宽度(单位:像素),如果不支持 DNL 就必须 >0
 
⑤ 颜色分量数
       1字节    
只有3个数值可选
                                      
       1:灰度图;3:YCrCb或YIQ;4:CMYK
                                      
       而JFIF中使用YCrCb,故这里颜色分量数恒为3
 
⑥颜色分量信息      颜色分量数×3字节(通常为9字节)

a)        
颜色分量ID  
             
1字节

b)       
水平/垂直采样因子     
1字节    
       高4位:水平采样因子
                                               
       低4位:垂直采样因子
                                                      
(曾经看到某资料把这两者调转了)

c)        量化表                        
1字节    
       当前分量使用的量化表的ID

本标记段中,字段⑥应该重复出现,有多少个颜色分量(字段⑤),就出现多少次(一般为3次)。

l       DHT,Difine Huffman Table,定义哈夫曼表

u  标记代码    
                           
2字节    
       固定值0xFFC4

u  包含2个具体字段:
 ①数据长度
                           
2字节    
       字段①和多个字段②的总长度
                                      
                    
       即不包括标记代码,但包括本字段
 ② 哈夫曼表      
       数据长度-2字节

a)      
表ID和表类型    
       1字节    
       高4位:类型,只有两个值可选
                                               
             
       0:DC直流;1:AC交流
                                         
             
低4位:哈夫曼表ID,
                                                      
             
注意,DC表和AC表分开编码

b)     
不同位数的码字数量    16字节

c)      编码内容       16个不同位数的码字数量之和(字节)

本标记段中,字段②可以重复出现(一般4次),也可以致出现1次。例如,Adobe Photoshop 生成的JPEG图片文件中只有1个DHT标记段,里边包含了4个哈夫曼表;而Macromedia Fireworks生成的JPEG图片文件则有4个DHT标记段,每个DHT标记段只有一个哈夫曼表。

l       DRI,Define Restart Interval,定义差分编码累计复位的间隔

u  标记代码                                
2字节     固定值0xFFDD

u  包含2个具体字段:
 ①数据长度                            
2字节     固定值0x0004,①~②两个字段的总长度
                                                           
即不包括标记代码,但包括本字段
 ②MCU块的单元中的重新开始间隔
                                             
2字节     设其值为n,则表示每n个MCU块就有一个
  
                                                       
RSTn标记。第一个标记是RST0,第二个是
                                                           
RST1等,RST7后再从RST0重复。

如果没有本标记段,或间隔值为0时,就表示不存在重开始间隔和标记RST

l       SOS,Start of Scan,扫描开始 12字节

u  标记代码                         
2字节     固定值0xFFDA

u  包含2个具体字段:
 ①数据长度                     
2字节     ①~④两个字段的总长度
                               
                    
即不包括标记代码,但包括本字段
 ②颜色分量数                
1字节     应该和SOF中的字段⑤的值相同,即:
                                                    
1:灰度图是;3: YCrCb或YIQ;4:CMYK。

而JFIF中使用YCrCb,故这里颜色分量数恒为3
 
 ③颜色分量信息
       
a) 颜色分量ID
          1字节
       
b) 直流/交流系数表号 1字节     高4位:直流分量使用的哈夫曼树编号
                                                       
低4位:交流分量使用的哈夫曼树编号

④ 压缩图像数据
       
a)谱选择开始      
             
1字节     固定值0x00
       
b)谱选择结束      
             
1字节     固定值0x3F
       
c)谱选择                    
       1字节    
在基本JPEG中总为00

本标记段中,字段③应该重复出现,有多少个颜色分量(字段②),就出现多少次(一般为3次)。本段结束后,紧接着就是真正的图像信息了。图像信息直至遇到一个标记代码就自动结束,一般就是以EOI标记表示结束。

l        EOI,End of Image,图像结束 2字节

u  标记代码                  
2字节     固定值0xFFD9

这里补充说明一下,由于在JPEG文件中0xFF具有标志性的意思,所以在压缩数据流(真正的图像信息)中出现0xFF,就需要作特别处理。具体方法是,在数据0xFF后添加一个没有意义的0x00。换句话说,如果在图像数据流中遇到0xFF,应该检测其紧接着的字符,如果是

1)0x00,则表示0xFF是图像流的组成部分,需要进行译码;

2)0xD9,则与0xFF组成标记EOI,则图像流结束,同时图像文件结束;

3)0xD0~0xD7,则组成RSTn标记,则要忽视整个RSTn标记,即不对当前0xFF和紧接的0xDn两个字节进行译码,并按RST标记的规则调整译码变量;

3)0xFF,则忽视当前0xFF,对后一个0xFF再作判断;

4)其他数值,则忽视当前0xFF,并保留紧接的此数值用于译码。

二、       
JPEG解码过程详解

 

下面来详细讲述JPEG文件的解码过程。

1.读入文件的相关信息

按照上述的JPEG文件数据存储方式,把要解码的文件的相关信息一一读出,为接下来的解码工作做好准备。参考方法是,设计一系列的结构体对应各个标记,并存储标记内表示的信息。其中图像长宽、多个量化表和哈夫曼表、水平/垂直采样因子等多项信息比较重要。以下给出读取过程中的两个问题。

1)整个文件的大体结构

JFIF格式的JPEG文件(*.jpg)的一般顺序为:

SOI(0xFFD8)

APP0(0xFFE0)

[APPn(0xFFEn)]可选

DQT(0xFFDB)

SOF0(0xFFC0)

DHT(0xFFC4)

SOS(0xFFDA)

压缩数据

EOI(0xFFD9)

2)字的高低位问题

JPEG文件格式中,一个字(16位)的存储使用的是 Motorola 格式, 而不是 Intel 格式。也就是说, 一个字的高字节(高8位)在数据流的前面, 低字节(低8位)在数据流的后面,与平时习惯的Intel格式不一样。.

3)读出哈夫曼表数据

a)理论说明

在标记段DHT内,包含了一个或者多个的哈夫曼表。对于单一个哈夫曼表,应该包括了三部分:

l 哈夫曼表ID和表类型
这个字节的值为一般只有四个0x00、0x01、0x10、0x11。
0x00表示DC直流0号表;
0x01表示DC直流1号表;
0x10表示AC交流0号表;
0x11表示AC交流1号表。

l 不同位数的码字数量

JPEG文件的哈夫曼编码只能是1~16位。这个字段的16个字节分别表示1~16位的编码码字在哈夫曼树中的个数。

l 编码内容

这个字段记录了哈夫曼树中各个叶子结点的权。所以,上一字段(不同位数的码字数量)的16个数值之和就应该是本字段的长度,也就是哈夫曼树中叶子结点个数。

b)举例说明

以下面一段哈夫曼表数据举例说明(数据全部以16进制表示):

11  00 
02  02  00  05  01  06  01  00 
00  00  00  00  00  00  00
00 
01  11  02  21  03  31  41  12 
51   61 71 81 91 22 13  32

红色部分(第1字节)为哈夫曼表ID和表类型,其值0x11表示此部分数据描述的是AC交流1号表。

蓝色部分(2~17字节)为不同位数的码字的数量。这16个数值实际意义为:没有1位和4位的哈夫曼码字;2位和3位的码字各有2个;5位码字有5个;6位和8位码字各有1个;7位码字各有6个;没有9位或以上的码字。

绿色部分(18~34字节)为编码内容。由蓝色部分数据知道,此哈夫曼树有0+2+2+0+5+1+6+1=17个叶子结点,即本字段应该有17个字节。这段数据表示17个叶子结点按从小到大排列,其权值依次为0、1、11、2、21、3、31、41……

4)建立哈夫曼树

a)理论说明

在读出哈夫曼表的数据后,就要建立哈夫曼树。具体方法为:

1)第一个码字必定为0。
如果第一个码字位数为1,则码字为0;
如果第一个码字位数为2,则码字为00;
如此类推。

2)从第二个码字开始,
如果它和它前面的码字位数相同,则当前码字为它前面的码字加1;
如果它的位数比它前面的码字位数大,则当前码字是前面的码字加1后再在后边添若干个0,直至满足位数长度为止。

b)举例说明

继续以上边的例子说明问题。

n          
由于没有1位的码字,所以第一个码字的位数为2,即码字为00;

n          
由于2位的码字有两个,所以第二个码字位数仍为2,即码字为00+1=01;

n          
第三个码字为3位,比第二个码字长1位,所以第三个码字为:01+1=10,然后再添1个“0”,得100;

n          
……

如此类推,最后得到这个哈夫曼树如下:


序号


码字长度


码字


权值


1


2


00


0x00


2


2


01


0x01


3


3


100


0x11


4


3


101


0x02


5


5


11000


0x21


6


5


11001


0x03


7


5


11010


0x31


8


5


11011


0x41


9


5


11100


0x12


10


6


111010


0x51


11


7


1110110


0x61


12


7


1110111


0x71


13


7


1111000


0x81


14


7


1111001


0x91


15


7


1111010


0x22


16


7


1111011


0x13


17


8


11111000


0x32

特别注意的是,如果中间有某个位数的码字缺失,例如没有4位码字,则应该在3位码字加1后,添加“00”补足5位,形成下一个5位码字。

在准备好所有的图片信息后,就可以对图片数据进行解码了。

时间: 2024-12-12 20:58:02

JPEG文件编/解码详解的相关文章

atitit.mp4 视频文件多媒体格式结构详解

atitit.mp4 视频文件多媒体格式结构详解 1. 一.基本概念1 2. MP4文件概述2 3. mp4是由一个个“box”组成的,2 4. 典型简化mp43 5. Fragments5 6. ref6 7. 具体列表6 8. Ref29 MP4文件格式详解(ISO-14496-12/14) Author:Pirate Leo Email:[email protected] 1. 一.基本概念 1. 文件,由许多Box和FullBox组成. 2. Box,每个Box由Header和Data组

python处理word文件:win32com用法详解

目标:用python处理doc文件 方法:引入win32com模块 ************************************************************************** 一.安装 ************************************************************************** 首先要先下载安装win32com模块(起先在linux下装不成功,后在windows下面成功了...) 下载地址:http

协议分析 - DHCP协议解码详解

协议分析 - DHCP协议解码详解 [DHCP协议简介] DHCP,全称是 Dynamic Host Configuration Protocol﹐中文名为动态主机配置协议,它的前身是 BOOTP,它工作在OSI的应用层,是一种帮助计算机从指定的DHCP服务器获取它们的配置信息的自举协议. DHCP使用客户端/服务器模式,请求配置信息的计算机叫做DHCP客户端,而提供信息的叫做DHCP的服务器.DHCP为客户端分配地址的方法有三种:手工配置.自动配置.动态配置. DHCP最重要的功能就是动态分配

Struts2之struts2文件上传详解

一.学习案例:通过在uploadfile.jsp页面填写完表单,提交后跳转到success.jsp页面,然后验证upload包下上传文件是否成功. 二.案例分析:struts2文件上传并不是表面上看的只需简单配置就可以上传文件.实际是分为两步的.1.struts2首先将客户端上传的文件保存到struts.multipart.saveDir键所指定的目录,如果该键所对应的目录不存在,就会保存到javax.servlet.context.tempdir环境变量所指定的目录中.2.Action中所定义

Android AndroidManifest 清单文件以及权限详解

每个Android应用都需要一个名为AndroidManifest.xml的程序清单文件,这个清单文件名是固定的并且放在每个Android应用的根目录下.它定义了该应用对于Android系统来说一些非常重要的信息.Android系统需要这些信息才能正常运行该应用.Android程序清单文件主要具有下面作用: ·        它给应用程序Java包命名,这个包名作为应用程序唯一标识符. ·        它描述了应用程序中的每个程序组件-Activity,Service,Broadcast Re

spark读写压缩文件API使用详解

最近研究了下Spark如何读写压缩格式的文件,主要有如下三种方式,这里以lzo方式压缩为例     /*******************old hadoop api*************************/     val confHadoop = new JobConf     confHadoop.set("mapred.output.compress", "true")     confHadoop.set("mapred.output

JSch - Java实现的SFTP(文件上传详解篇) 转

JSch是Java Secure Channel的缩写.JSch是一个SSH2的纯Java实现.它允许你连接到一个SSH服务器,并且可以使用端口转发,X11转发,文件传输等,当然你也可以集成它的功能到你自己的应用程序. 本文只介绍如何使用JSch实现的SFTP功能. SFTP是Secure File Transfer Protocol的缩写,安全文件传送协议.可以为传输文件提供一种安全的加密方法.SFTP 为 SSH的一部份,是一种传输文件到服务器的安全方式.SFTP是使用加密传输认证信息和传输

discuz门户首页-header文件模板语法详解和注释

header文件引用了跟多通用模板,所以整个文章会很长,现在比较忙,注释工作会不定期进行 首先开下门户首页的文件 portal里面的index.htm <!--{template common/header}--> //引用common文件下的header.htm这个也是引用模板文件的一种常用方法 <style id="diy_style" type="text/css"></style> //没什么用 <div class

Unix Shell_Oracle EBS基于主机文件Host开发详解(案例)

2014-06-20 BaoXinjian 一.摘要 Oracle 并发程式中Host Type的可执行程式,它的作用是用于调用Unix Shell去执行某些需求 个人觉得Oracle EBS中引入Host去调用unix shell其弥补了很多PLSQL类型程式无法做的某些功能,以unix shell的语法结构直接对服务器进行操作 写host并发程式时,需要较强的Bash语法知识,个人不做DBA,只了解一部分,所以就不具体介绍了,只说明一下Oracle EBS开发Unix Shell时需要注意的