JPEG编码(一)
JPEG编码介绍。
转自:http://blog.chinaunix.net/uid-20451980-id-1945156.html
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,并表示一个标记码的开始。而在一个完整的两字节的标记码后,就是该标记码对应的压缩数据流,记录了关于文件的诸种信息。
常用的标记有SOI、APP0、DQT、SOF0、DHT、DRI、SOS、EOI。
注意,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位码字。
在准备好所有的图片信息后,就可以对图片数据进行解码了。
JPEG编码(一)的更多相关文章
- 形成一个zigzag数组(JPEG编码里取像素数据的排列顺序)
面试例题:输入n,求一个nXn矩阵,规定矩阵沿45度递增,形成一个zigzag数组(JPEG编码里取像素数据的排列顺序),请问如何用C++实现? (中国台湾著名硬件公司2007年11月面试题)(自程序 ...
- CUDA JPEG编码
基于英伟达的jpegNPP工程,分离实现独立的JPEG压缩. 由于原工程是直接把解码时的jpeg图片的信息直接作为编码时的信息,所以在做独立的JPEG编码时,需要自己来填充各种信息. 1.JPEG编码 ...
- JPEG编码(二)
来自CSDN评论区http://bbs.csdn.net/topics/190980 1. 色彩模型 JPEG 的图片使用的是 YCrCb 颜色模型, 而不是计算机上最常用的 RGB. 关于色彩模型, ...
- JPEG 编码
WIN8. DNJXJ-7XBW8-2378T-X22TX-BKG7J 模板:类的宏,泛型,甜饼切割机 类模板:泛型类: 函数模板:泛型函数 STL standard template Library ...
- Atitit 视频编码与动画原理attilax总结
Atitit 视频编码与动画原理attilax总结 1.1. 第一步:实现有损图像压缩和解压1 1.2. 接着将其量化,所谓量化,就是信号采样的步长,1 1.3. 第二步:实现宏块误差计算2 1.4. ...
- [数据结构与算法]哈夫曼(Huffman)树与哈夫曼编码
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- dm8148 jpeg编解码器测试
测试过程: 1)于A8将jpeg传送到videoM3解码,然后,videoM3编码.在编译jpeg图像传输到A8,主要是测试jpeg编码的图像需要多少时间: 1000w像素: 编码时间:43ms. ...
- JPEG流封装AVI视频
前言:前几天工作任务,要把JPEG流封装为AVI视频,就找了些AVI文件结构资料和示例代码研究了下,现将学习总结及最终完成的可用代码分享出来,由于本人也是现学现用,如有不恰当或错误之处,欢迎提出! 1 ...
- 嵌入式 视频编码(H264)
这几天在编写视频录制模块,所以,闲暇之余,又粗粗的整理了一下,主要是API,以备不时之用 摄像头获取的模拟信号通过经芯片处理(我们使用的是CX25825),将模拟信号转成数字信号,产生标准的IT ...
随机推荐
- [POI2015][bzoj4383] Pustynia [线段树优化建图+拓扑排序]
题面 bzoj权限题传送门 luogu传送门 思路 首先,这个题目显然可以从所有小的点往大的连边,然后如果没环就一定可行,从起点(入读为0)开始构造就好了 但是问题来了,如果每个都连的话,本题中边数是 ...
- 全网把Map中的hash()分析的最透彻的文章,别无二家。
你知道HashMap中hash方法的具体实现吗?你知道HashTable.ConcurrentHashMap中hash方法的实现以及原因吗?你知道为什么要这么实现吗?你知道为什么JDK 7和JDK 8 ...
- 2017 多校4 Wavel Sequence
2017 多校4 Wavel Sequence 题意: Formally, he defines a sequence \(a_1,a_2,...,a_n\) as ''wavel'' if and ...
- CSS实现放大镜/狙击镜效果
图片放大,这是一个比较容易的效果了.当然,今天说的可不是简简单单的在一个框里放大,而是一个圆.就像放大镜或是狙击镜那样,只有圆圈里的放大,圈外的当然还是原来的图片.这是不是很不可思议? 当然不是.做过 ...
- Linux命令(IT)
ls 查看当前目录下文件 cd 指定目录 sftp zygf@xxx.xxx.xxx.xxx 进行登录zygf用户 sftp命令行登录过程: ① sftp xxx.xxx.xxx.xxx ...
- Three Garlands~Educational Codeforces Round 35
C. Three Garlands time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...
- PHP AES128加密解密
<?php /** * Class AES */ class AES { public static function encrypt($input, $key) { $size = mcryp ...
- 接下来打算写一下visual stuido 2013使用git进行远端管理。
虽然我有了vs的账号,也vs2013开始已经可以进行远端的账户管理了,可是vs的版控毕竟有些依赖vs,想想还是用git吧 今天把这个环境的整套都弄地基本熟了.记录一下,算是一个小结.开始搭建系统框架
- cocos2d-iphone心得
源码下载地址: http://code.google.com/p/cocos2d-iphone/downloads/list https://github.com/cocos2d/cocos2d-ip ...
- BufferedInputStream&BufferedOutputStream
使用字符缓冲区相关实现copy文件: public static void main(String[] args) { //创建文件对象指定要拷贝的文件路径(源文件),文件须存在,测试用例不做判断 F ...