资源描述
JPEG图像格式详解JPEG压缩简介1. 色彩模型JPEG的图片使用的是 YCrCb颜色模型,而不是计算机上最常用的RGB.关于色彩模型, 这里不多阐述. 只是说明, YCrCb 模型更适合图形压缩. 因为人眼对图片上 的亮度 Y 的变化远比色度 C 的变化敏感. 我们完全可以每个点保存一个 8bit 的亮 度值, 每 2x2 个点保存一个 Cr Cb 值, 而图象在肉眼中的感觉不会起太大的变化. 所以,原来用RGB模型,4个点需要4x3=12字节.而现在仅需要 4+2=6字节;平 均每个点占12bit.当然JPEG格式里允许每个点的C值都记录下来; 不过MPEG里都是按 12bit 一个点来存放的, 我们简写为 YUV12.R G B - Y Cb Cr 转换(R,G,B都是 8bitunsigned)|Y| 0.2990.5870.114 |R|0| Cb |=|- 0.1687- 0.33130.5 | * |G|+ |128| Cr | 0.5- 0.4187- 0.0813|B|128|Y = 0.299*R + 0.587*G + 0.114*B(亮度)Cb = - 0.1687*R - 0.3313*G + 0.5 *B + 128Cr =0.5 *R - 0.4187*G - 0.0813*B + 128 Y,Cb,Cr - R,G,B 转换+ 1.402*(Cr-128)- 0.71414*(Cr-128)R= YG= Y - 0.34414*(Cb-128)B = Y + 1.772*(Cb-128)一般, C 值 (包括 Cb Cr) 应该是一个有符号的数字, 但这里被处理过了, 方法 是加上了 128. JPEG 里的数据都是无符号 8bit 的.2. DCT ( 离散余弦变换)JPEG里,要对数据压缩, 先要做一次 DCT变换.DCT变换的原理,涉及到数学 知识, 这里我们不必深究. 反正和傅立叶变换 (学过高数的都知道) 是差不多了. 经过 这个变换,就把图片里点和点间的规律呈现出来了,更方便压缩.JPEG里是对每8x8个点为一个单位处理的. 所以如果原始图片的长宽不是 8 的倍数, 都需要先补成 8 的倍数, 好一块块的处理. 另外, 记得刚才我说的 Cr Cb 都是 2x2 记录一次吗? 所 以大多数情况, 是要补成 16x16 的整数块 . 按从左到右, 从上到下的次序排列 (和我 们写字的次序一样).JPEG里是对 Y Cr Cb分别做 DCT变换的.这里进行 DCT变换 的 Y, Cr, Cb 值的范围都是 -128127. (Y 被减去 128)JPEG编码时使用的是面给出公式:Forward DCT (FDCT) 解码时使用的Inverse DCT(IDCT)FDCT:772*x+12*y+1F(u,v) =alpha(u)*alpha(v)*sum sum f(x,y)* cos ( *u*PI)* cos( *v*PI)x=0 y=01616u,v = 0,1,.,71/sqrt(8)(u=0)alpha(u)=1/2(u!=0)IDCT:772*x+12*y+1f(x,y) =sumsum alpha(u)*alpha(v)*F(u,v)*cos( *u*PI)* cos (-*v*PI)u=0v=01616x,y=0,1.7这个步骤很花时间, 另外有种 AA&N 优化算法, 大家可以去 inet 自己找一下.在Intel 主页上可以找到 AA&NIDCT的MMX优化代码.(Intel主页上的代码,输入数据为 12.4 的定点数, 输入矩阵需要转置 90 度)3. 重排列DCT结果DCT将一个8x8的数组变换成另一个8x8的数组.但是内存里所有数据都是线形存放的, 如果我们一行行的存放这 64 个数字, 每行的结尾的点和下行开始的点就 没有什么关系,所以JPEG规定按如下次序整理64个数字.0, 1, 5, 6,14,15,27,28,2, 4, 7,13,16,26,29,42,3, 8,12,17,25,30,41,43,9,11,18,24,31,40,44,53,10,19,23,32,39,45,52,54,20.22.33.38.46.51.55.60,21.34.37.47.50.56.59.61,35,36,48,49,57,58,62,63这样数列里的相邻点在图片上也是相邻的了.4. 量化对于前面得到的 64 个空间频率振幅值, 我们将对它们作幅度分层量化操作 . 方 法就是分别除以量化表里对应值并四舍五入.for (i = 0 ; i (5,10111)-30 - (5,00001)-8 - (4,0111)1 - (1,1)前面的那串数字就变成了:(0,6), 111001 ; (0,6), 101101 ; (4,5), 10111; (1,5), 00001; (0,4) , 0111 ; (2,1), 1 ; (0,0)括号里的数值正好合成一个字节. 后面被编码的数字表示范围是-32767.32767.合成的字节里, 高 4 位是前续 0 的个数, 低 4 位描述了后面数字的位数.继续刚才的例子, 如果 06 的 huffman 编码为 111000 ( 06 对应 111000 为查表所得. jpeg 文件里保存了压缩时所产生的 huffman 表, 将 0255 这 256 个 8 bits 定长数字,对应成 116 bits的不定长数值.出现频率高的数字小于 8bits, 频率低的大于 8bits,从而使整个的数据长度降低,关于huffman 压缩算法, 请查阅相关资料 )69= (4,5)-1111111110011001 ( 注: 69=4*16+5=0x45 )21= (1,5)-111111101104= (0,4)-101133= (2,1)-110110= EOB= (0,0)- 1010那么最后对于前面的例子表示的 写入 JPG 文件中就是这样的:63 个系数 ( 记得我们将第一个跳过了吗 ?) 按位流111000 111001 1110001011 0111 11011 1101101 1111111110011001101010111 11111110110 000017. DC 的编码记得刚才我们跳过了每组 64 个数据的第一个吧, DC 就是指的这个数字 (后面 63个简称 AC)代入前面的FDCT公式可以得到c(0,0)77DC = F(0,0)= * sum sum f(x,y) * cos 0 * cos 0 其中 c(0,0)= 1/24x=0 y=0177一 *sum sum f(x,y)8x=0 y=0即一块图象样本的平均值. 就是说, 它包含了原始 8x8 图象块里的很多能量. ( 通常 会得到一个很大的数值)JPEG的作者指出连续块的DC率之间有很紧密的联系,因此他们决定对 8x8块的DC 值的差别进行编码. (Y, Cb, Cr 分别有自己的 DC)Diff = DC(i) - DC(i-1)所以这一块的 DC(i) 就是: DC(i) = DC(i-1)+ DiffJPG 从 0 开始对 DC 编码, 所以 DC(0)=0. 然后再将当前 Diff 值加在上一个值上得 到当前值.下面再来看看上面那个例子: (记住我们保存的 DC是和上一块 DC的差值 Diff)例如上面例子中, Diff 是 -511, 就编码成(9, 000000000)如果 9 的 Huffman 编码是 1111110 (在 JPG 文件中, 一般有两个 Huffman 表,个是DC用,一个是 AC用)那么在JPG文件中,DC的2进制表示为1111110 000000000它将放在 63 个 AC 的前面, 上面上个例子的最终 BIT 流如下:1111110 000000000 111000 111001 111000 101101 1111111110011001 10111 11111110110 00001 1011 0111 11011 1 1010解码过程简述8. 一个数据单元 Y 的解码 ( 其余类同)在整个图片解码的开始,你需要先初始化 DC值为0.1) 先解码 DC:a) 取得一个 Huffman 码 ( 使用 Huffman DC 表)b) Huffman 解码, 看看后面的数据位数 Nc) 取得 N 位, 计算 Diff 值d) DC + = Diffe) 写入 DC 值: vector0=DC 2) 解码 63 个 AC: 循环处理每个 AC 直到 EOB 或者处理到 64 个 ACa) 取得一个 Huffman 码 ( 使用 Huffman AC 表)b) Huffman 解码, 得到 ( 前面 0 数量, 组号) 记住: 如果是 (0,0) 就是 EOB 了c) 取得 N 位( 组号) 计算 ACd) 写入相应数量的 0e) 接下来写入 AC下一步的解码上一步我们得到了 64 个矢量. 下面我们还需要做一些解码工作:1) 反量化 64 个矢量 : for (i=0;i=63;i+)vectori*=quanti(注意防止溢出)2) 重排列 64 个矢量到 8x8 的块中3) 对 8x8 的块作 IDCT对 8x8 块的 (Y,Cb,Cr) 重复上面的操作 Huffman 解码, 步骤 1), 2), 3)4) 将所有的 8bit 数加上 1285) 转换 YCbCr 到 RGB9. JPG文件(Byte级)里怎样组织图片信息注意 JPEG/JFIF 文件格式使用 Motorola 格式, 而不是 Intel 格式, 就是说, 如果 是一个字的话, 高字节在前, 低字节在后.JPG 文件是由一个个段 (segments) 构成的. 每个段长度 =65535. 每个段从一个标 记字开始. 标记字都是 0xff 打头的, 以非 0 字节和 0xFF 结束. 例如 FFDA , FFC4,FFC0. 每个标记有它特定意义, 这是由第 2 字节指明的. 例如, SOS (StartOf Scan = FFDA) 指明了你应该开始解码. 另一个标记 DQT (Define Quantization Table = 0xFFDB) 就是说它后面有 64 字节的 quantization 表在处理 JPG 文件时, 如果你碰到一个 0xFF, 而它后面的字节不是 0, 并且这个字节 没有意义. 那么你遇到的 0xFF 字节必须被忽略. (一些 JPG 里, 常用用 0xFF 做某 些填充用途) 如果你在做 huffman 编码时碰巧产生了一个 0xFF, 那么就用 0xFF 0x00 代替. 就是说在 jpeg 图形解码时碰到 FF00 就把它当作 FF 处理.另外在 huffman 编码区域结束时, 碰到几个 bit 没有用的时候, 应该用 1 去填充. 然后后面跟 FF.面是几个重要的标记SOI = Start Of Image = FFD8 这个标记只在文件开始出现一次EOI = End Of Image = FFD9JPG 文件都以 FFD9 结束RSTi = FFDi ( i = 复位标记0.7) RST0 = FFD0, RST7=FFD7通常穿插在数据流里,我想是担心 JPG解码出问题吧(应该配合 DRI使用).RST将Huffman 的解码数据流复位. DC 也重新从 0 开始计(SOS - RST0- RST1- RST2 -.- RST6 - RST7- RST0 -.)10. 标记11. JPG 文件中Haffman 表的储存面是必须处理的标记SOF0=StartOfFrame0 = FFC0SOS =StartOfScan= FFDAAPP0=itsthemarkerused to identifya JPG file which uses the JFIFspecification = FFE0COM =Comment= FFFEDNL =DefineNumber of Lines= FFDCDRI =DefineRestart Interval= FFDDDQT =DefineQuantization Table= FFDBDHT =DefineHuffman Table= FFC4JPEG里定义了一张表来描述Haffman树.定义在 DHT标记后面. 注意:Haffman代码的长度限制在 16bit 内.一般一个 JPG文件里会有 2类Haffman表:一个用于 DC 一个用于 AC (实际有 4 个表, 亮度的 DC,AC 两个, 色度的 DC,AC 两个) 这张表是这样保存的:1) 16 字节:第 i 字节表示了 i 位长的 Huffman 代码的个数 (i= 1 到 16) 2) 这表的长度 ( 字节数) = 这 16 个数字之和现在你可以想象这张表怎么存放的吧? 对应字节就是对应 Haffman 代码等价数字. 我 不多解释, 这需要你先了解 Haffman 算法. 这里只举一个例子:Haffman 表的表头是 0,2,3,1,1,1,0,1,0,0,0,0,0,0,0,0就是说长度为 1 的代码没有长度为 2 的代码为0001长度为3的代码是100101110长度为4的代码是1110长度为5的代码是11110长度为6的代码是111110长度为7的代码没有 ( 如果有一个的话应该是1111110)长度为8的代码是11111100后面都没有了.如果表下面的数据是45 57 29 17 23 25 34 28 就是说45 = 0057 = 0129 = 10017 = 10123 = 110如果你懂 Haffman 编码, 这些不难理解12. 采样系数下面讲解的都是真彩JPG的解码,灰度JPG的解码很简单,因为图形中只有亮度信息. 而彩色图形由 (Y, Cr, Cb) 构成, 前面提到过, Y 通常是每点采样一次, 而 Cr, Cb 一般是 2x2 点采样一次, 当然也有的 JPG 是逐点采样, 或者每两点采样 ( 横向 两点, 纵向一点) 采样系数均被定义成对比最高采样系数的相对值.一般情况(即:Y逐点采样,Cr Cb每2x2点一次)下:Y有最高的采样率, 横向采 样系数 HY=2 纵向采样系数 VY=2; Cb 的横向采样系数 HCb=1, 纵向采样系数 VCb=1; 同样 HCr=1, VCr=1在 Jpeg 里, 8x8 个原始数据, 经过 RLE, Huffman 编码后的一串数据流称为一个 Data Unit (DU) JPG 里按 DU 为单位的编码次序如下:1)for (counter_y=1;counter_y=VY;counter_y+) for (counter_x=1;counter_x=HY;counter_x+) 对 Y 的 Data Unit 编码 2)for (counter_y=1;counter_y=VCb ;counter_y+) for (counter_x=1;counter_x=HCb;counter_x+) 对 Cb 的 Data Unit 编码 3)for (counter_y=1;counter_y=VCr;counter_y+) for (counter_x=1;counter_x0- 图片宽度 ( 高字节, 低字节 ), 如果不支持 DNL 就必须 0- components 数量 (1 byte), 灰度图是 1, YCbCr/YIQ 彩色图是 3, 是4- 每个 component: 3 bytes- component id (1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q)- 采样系数 (bit 0-3 vert., 4-7 hor.)- quantization table 号DRI: Define Restart Interval:- $ff, $dd (DRI)- 长度 ( 高字节, 低字节 ), 必须是 4-MCU块的单元中的重新开始间隔(高字节,低字节),意思是说,每n个MCU块就有一个 RSTn标记.第一个标记是 RST0,然后是RST1等,RST7后再从RST0重复DQT: Define Quantization Table:- $ff, $db (DQT)- 长度 ( 高字节, 低字节)- QT 信息 (1 byte):bit 0.3:QT 号(0.3,否则错误)bit 4.7:QT 精度, 0 = 8 bit, 否则 16 bit- n 字节的 QT, n = 64*( 精度+1) 备注:-一个单独的 DQT段可以包含多个 QT,每个都有自己的信息字节- 当精度 =1 (16 bit), 每个字都是高位在前低位在后DAC: Define Arithmetic Table:法律原因, 现在的软件不支持 arithmetic 编码. 不能生产使用 arithmetic 编码的 JPEG 文件DHT: Define Huffman Table:- $ff, $c4 (DHT)- 长度 ( 高字节, 低字节)- HT 信息 (1 byte):bit 0.3: HT 号 (0.3, 否则错误)bit 4 : HT 类型, 0 = DC table, 1 = AC tablebit 5.7: 必须是 0- 16 bytes: 长度是 1.16 代码的符号数. 这 16 个数的和应该 = 1 , =4 ( 否则是错的) 通常是 3- 每个组件: 2 bytes- component id (1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q), 见 SOF0- 使用的 Huffman 表:- bit 0.3: AC table (0.3)- bit 4.7: DC table (0.3)- 忽略 3 bytes (?)备注:-图片数据(一个个扫描行) 紧接着SOS段.
展开阅读全文