【转载】转载自http://www.cnblogs.com/leaven/archive/2010/04/06/1705846.html
JPEG压缩编码算法的主要计算步骤如下:
(0) 8*8分块。
(1) 正向离散余弦变换(FDCT)。
(2) 量化(quantization)。
(3) Z字形编码(zigzag scan)。
(4) 使用差分脉冲编码调制(DPCM)对直流系数(DC)进行编码。
(5) 使用行程长度编码(RLE)对交流系数(AC)进行编码。
(6) 熵编码。
一、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标记。
SOI,Start of Image,图像开始
标记代码 2字节 固定值0xFFD8
APP0,Application,应用程序保留标记0
标记代码 2字节 固定值0xFFE0
包含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。
APPn,Application,应用程序保留标记n,其中n=1~15(任选)
标记代码 2字节 固定值0xFFE1~0xFFF
包含2个具体字段:
① 数据长度 2字节 ①~②2个字段的总长度
即不包括标记代码,但包括本字段
② 详细信息 数据长度-2字节 内容不定
例如,Adobe Photoshop生成的JPEG图像中就用了APP1和APP13两个标记段分别存储了一幅图像的副本。
DQT,Define Quantization Table,定义量化表
标记代码 2字节 固定值0xFFDB
包含9个具体字段:
① 数据长度 2字节 字段①和多个字段②的总长度
即不包括标记代码,但包括本字段
② 量化表 数据长度-2字节
a)精度及量化表ID 1字节 高4位:精度,只有两个可选值
0:8位;1:16位
低4位:量化表ID,取值范围为0~3
b)表项 (64×(精度+1))字节 例如8位精度的量化表
其表项长度为64×(0+1)=64字节
本标记段中,字段②可以重复出现,表示多个量化表,但最多只能出现4次。
SOF0,Start of Frame,帧图像开始
标记代码 2字节 固定值0xFFC0
包含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次)。
DHT,Difine Huffman Table,定义哈夫曼表
标记代码 2字节 固定值0xFFC4
包含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标记段只有一个哈夫曼表。
DRI,Define Restart Interval,定义差分编码累计复位的间隔
标记代码 2字节 固定值0xFFDD
包含2个具体字段:
①数据长度 2字节 固定值0x0004,①~②两个字段的总长度
即不包括标记代码,但包括本字段
②MCU块的单元中的重新开始间隔
2字节 设其值为n,则表示每n个MCU块就有一个
RSTn标记。第一个标记是RST0,第二个是
RST1等,RST7后再从RST0重复。
如果没有本标记段,或间隔值为0时,就表示不存在重开始间隔和标记RST
SOS,Start of Scan,扫描开始 12字节
标记代码 2字节 固定值0xFFDA
包含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标记表示结束。
EOI,End of Image,图像结束 2字节
标记代码 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,并保留紧接的此数值用于译码。
为了造福大众,我把自己定义的文件头结构体给他家参考一下,欢迎讨论~
1 typedef struct APP0 //应用程序保留标记0 2 { 3 //char value[2]; //标记代码 4 unsigned char d_length[2]; //数据长度 5 unsigned char symbol[5]; //标识符 6 unsigned char version[2]; //版本号 7 unsigned char density; //X和Y的密度单位 8 unsigned char x_density[2]; //X方向像素密度 9 unsigned char y_density[2]; //Y方向像素密度 10 unsigned char x_num; //缩略图水平像素数目 11 unsigned char y_num; //缩略图垂直像素数目 12 unsigned char* RGB_bitmap; //缩略图RGB位图 13 }APP0; APP0 app0; 14 15 typedef struct APPn //应用程序保留标记n,其中n=1~15(任选) 16 { 17 //char value[2]; //标记代码 18 unsigned char d_length[2]; //数据长度 19 unsigned char* info; //详细信息,数据长度-2字节 20 }APPn; APPn appn; 21 22 typedef struct DQT //定义量化表 23 { 24 //char value[2]; //标记代码 25 unsigned char d_length[2]; //数据长度 26 unsigned char* table; //量化表,数据长度-2字节 27 //a.精度及量化表ID 1字节 28 //高4位:精度,只有两个可选值,0:8位;1:16位 29 //低4位:量化表ID,取值范围为0~3 30 //b.表项(64×(精度 + 1))字节 31 //例如8位精度的量化表,其表项长度为64×(0 + 1) = 64字节 32 }DQT; DQT dqt; 33 34 typedef struct SOF0 //帧图像开始 35 { 36 //char value[2]; //标记代码 37 unsigned char d_length[2]; //数据长度 38 unsigned char accuracy; //精度 39 unsigned char height[2]; //图像高度 40 unsigned char width[2]; //图像宽度 41 unsigned char color; //颜色分量数 42 unsigned char* color_info; //颜色分量信息 43 }SOF0; SOF0 sof0; 44 45 typedef struct DHT //定义哈夫曼表 46 { 47 //char value[2]; //标记代码 48 unsigned char d_length[2]; //数据长度 49 unsigned char* H_table; //哈夫曼表,数据长度-2字节 50 //a.表ID和表类型,1字节, 51 //高4位:0:DC直流;1:AC交流 52 //低4位:哈夫曼表ID,注意,DC表和AC表分开编码 53 //b.不同位数的码字数量,16字节 54 //c.编码内容,16个不同位数的码字数量之和(字节) 55 }DHT; DHT dht; 56 57 typedef struct DRI //定义差分编码累计复位的间隔 58 { 59 //char value[2]; //标记代码 60 unsigned char d_length[2]; //数据长度 61 unsigned char interval[2]; //MCU块的单元中的重新开始间隔 62 //设其值为n,则表示每n个MCU块就有一个RSTn标记。 63 }DRI; DRI dri; 64 65 typedef struct SOS //扫描开始,12字节 66 { 67 //char value[2]; //标记代码 68 unsigned char d_length[2]; //数据长度 69 unsigned char color; //颜色分量数 70 unsigned char* color_info; //颜色分量信息 71 unsigned char image_data[3]; //压缩图像数据 72 }SOS; SOS sos;
来源:https://www.cnblogs.com/gungnir2011/p/3615273.html