-------------
8. 一个数据单元 Y 的解码 (其余类同)
--------------------------------
在整个图片解码的开始, 你需要先初始化 DC 值为 0.
1) 先解码 DC:
a) 取得一个 Huffman 码 (使用 Huffman DC 表)
b) Huffman解码, 看看后面的数据位数 N
c) 取得 N 位, 计算 Diff 值
d) DC + = Diff
e) 写入 DC 值: " vector[0]=DC "
2) 解码 63 个 AC:
------- 循环处理每个 AC 直到 EOB 或者处理到 64 个 AC
a) 取得一个 Huffman 码 (使用 Huffman AC 表)
b) Huffman 解码, 得到 (前面 0 数量, 组号)
[记住: 如果是(0,0) 就是 EOB 了]
c) 取得 N 位(组号) 计算 AC
d) 写入相应数量的 0
e) 接下来写入 AC
-----------------
下一步的解码
------------
上一步我们得到了 64 个矢量. 下面我们还需要做一些解码工作:
1) 反量化 64 个矢量 : "for (i=0;i<=63;i++) vector
2) 重排列 64 个矢量到 8x8 的块中
3) 对 8x8 的块作 IDCT
对 8x8 块的 (Y,Cb,Cr) 重复上面的操作 [Huffman 解码, 步骤 1), 2), 3)]
4) 将所有的 8bit 数加上 128
5) 转换 YCbCr 到 RGB
9. JPG 文件(Byte 级)里怎样组织图片信息
-----------------------------------
注意 JPEG/JFIF 文件格式使用 Motorola 格式, 而不是 Intel 格式, 就是说, 如果
是一个字的话, 高字节在前, 低字节在后.
JPG 文件是由一个个段 (segments) 构成的. 每个段长度 <=65535. 每个段从一个标
记字开始. 标记字都是 0xff 打头的, 以非 0 字节和 0xFF 结束. 例如 'FFDA' ,
'FFC4', 'FFC0'. 每个标记有它特定意义, 这是由第2字节指明的. 例如, SOS (Start
Of 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 = 'FFD9'
JPG 文件都以 FFD9 结束
RSTi = FFDi ( i = 0..7) [ RST0 = FFD0, RST7=FFD7]
= 复位标记
通常穿插在数据流里, 我想是担心 JPG 解码出问题吧(应该配合 DRI 使用). RST 将
Huffman 的解码数据流复位. DC 也重新从 0 开始计
(SOS --- RST0 --- RST1 -- RST2 --...
...-- RST6 --- RST7 -- RST0 --...)
-------
10. 标记
-------
下面是必须处理的标记
SOF0 = Start Of Frame 0 = FFC0
SOS = Start Of Scan = FFDA
APP0 = it's the marker used to identify a JPG file which uses the JFIF
specification = FFE0
COM = Comment = FFFE
DNL = Define Number of Lines = FFDC
DRI = Define Restart Interval = FFDD
DQT = Define Quantization Table = FFDB
DHT = Define Huffman Table = FFC4
11. JPG 文件中 Haffman 表的储存
-----------------------------
JPEG 里定义了一张表来描述 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 代码等价数字. 我
不多解释, 这需要你先了解 Canonical Huffman Code. 这里只举一个例子:
Haffman 表的表头是 0,2,3,1,1,1,0,1,0,0,0,0,0,0,0,0
就是说长度为 1 的代码没有
长度为 2 的代码为 00
01
长度为 3 的代码是 100
101
110
长度为 4 的代码是 1110
长度为 5 的代码是 11110
长度为 6 的代码是 111110
长度为 7 的代码没有 (如果有一个的话应该是 1111110)
长度为 8 的代码是 11111100
.....
后面都没有了.
如果表下面的数据是
45 57 29 17 23 25 34 28
就是说
45 = 00
57 = 01
29 = 100
17 = 101
23 = 110
等等...
使用 Canonical Huffman Code 的好处在于可以很简洁的重建对应关系表.
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_x<=HCr;counter_x++)
{ 对 Cr 的 Data Unit 编码 }
按我上面的例子: (HY=2, VY=2 ; HCb=VCb =1, HCr,VCr=1) 就是这样一个次序
YDU,YDU,YDU,YDU,CbDU,CrDU
这些就描述了一块 16x16 的图形. 16x16 = (Hmax*8 x Vmax*8) 这里 Hmax=HY=2
Vmax=VY=2
一个 (Hmax*8,Vmax*8) 的块被称作 MCU (Minimun Coded Unix) 前面例子中一个
MCU = YDU,YDU,YDU,YDU,CbDU,CrDU
如果 HY =1, VY=1
HCb=1, VCb=1
HCr=1, VCr=1
这样 (Hmax=1,Vmax=1), MCU 只有 8x8 大, MCU = YDU,CbDU,CrDU
对于灰度 JPG, MCU 只有一个 DU (MCU = YDU)
JPG 文件里, 图象的每个组成部分的采样系数定义在 SOF0 (FFC0) 标记后
13. 简单说一下 JPG 文件的解码
-------------------------
解码程序先从 JPG 文件中读出采样系数, 这样就知道了 MCU 的大小, 算出整个图象
有几个 MCU. 解码程序再循环逐个对 MCU 解码, 一直到检查到 EOI 标记. 对于每个
MCU, 按正规的次序解出每个 DU, 然后组合, 转换成 (R,G,B) 就 OK 了
附:JPEG 文件格式
~~~~~~~~~~~~~~~~
- 文件头 (2 bytes): $ff, $d8 (SOI) (JPEG 文件标识)
- 任意数量的段 , 见后面
- 文件结束 (2 bytes): $ff, $d9 (EOI)
段的格式:
~~~~~~~~~
- header (4 bytes):
$ff 段标识
n 段的类型 (1 byte)
sh, sl 该段长度, 包括这两个字节, 但是不包括前面的 $ff 和 n.
注意: 长度不是 intel 次序, 而是 Motorola 的, 高字节在前,
低字节在后!
- 该段的内容, 最多 65533 字节
注意:
- 有一些无参数的段 (下面那些前面注明星号的)
这些段没有长度描述 (而且没有内容), 只有 $ff 和类型字节.
- 段之间无论有多少 $ff 都是合法的, 必须被忽略掉.
段的类型:
~~~~~~~~~
*TEM = $01 可以忽略掉
SOF0 = $c0 帧开始 (baseline JPEG), 细节附后
SOF1 = $c1 dito
SOF2 = $c2 通常不支持
SOF3 = $c3 通常不支持
SOF5 = $c5 通常不支持
SOF6 = $c6 通常不支持
SOF7 = $c7 通常不支持
SOF9 = $c9 arithmetic 编码(Huffman 的一种扩展算法), 通常不支持
SOF10 = $ca 通常不支持
SOF11 = $cb 通常不支持
SOF13 = $cd 通常不支持
SOF14 = $ce 通常不支持
SOF14 = $ce 通常不支持
SOF15 = $cf 通常不支持
DHT = $c4 定义 Huffman Table, 细节附后
JPG = $c8 未定义/保留 (引起解码错误)
DAC = $cc 定义 Arithmetic Table, 通常不支持
*RST0 = $d0 RSTn 用于 resync, 通常被忽略
*RST1 = $d1
*RST2 = $d2
*RST3 = $d3
*RST4 = $d4
*RST5 = $d5
*RST6 = $d6
*RST7 = $d7
SOI = $d8 图片开始
EOI = $d9 图片结束
SOS = $da 扫描行开始, 细节附后
DQT = $db 定义 Quantization Table, 细节附后
DNL = $dc 通常不支持, 忽略
DRI = $dd 定义重新开始间隔, 细节附后
DHP = $de 忽略 (跳过)
EXP = $df 忽略 (跳过)
APP0 = $e0 JFIF APP0 segment marker (细节略)
APP15 = $ef 忽略
JPG0 = $f0 忽略 (跳过)
JPG13 = $fd 忽略 (跳过)
COM = $fe 注释, 细节附后
其它的段类型都保留必须跳过
SOF0: Start Of Frame 0:
~~~~~~~~~~~~~~~~~~~~~~~
- $ff, $c0 (SOF0)
- 长度 (高字节, 低字节), 8+components*3
- 数据精度 (1 byte) 每个样本位数, 通常是 8 (大多数软件不支持 12 和 16)
- 图片高度 (高字节, 低字节), 如果不支持 DNL 就必须 >0
- 图片宽度 (高字节, 低字节), 如果不支持 DNL 就必须 >0
- components 数量(1 byte), 灰度图是 1, YCbCr/YIQ 彩色图是 3, CMYK 彩色图
是 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 table
bit 5..7: 必须是 0
- 16 bytes: 长度是 1..16 代码的符号数. 这 16 个数的和应该 <=256
- n bytes: 一个包含了按递增次序代码长度排列的符号表
(n = 代码总数)
备注:
- 一个单独的 DHT 段可以包含多个 HT, 每个都有自己的信息字节
COM: 注释:
~~~~~~~~~~
- $ff, $fe (COM)
- 注释长度 (高字节, 低字节) = L+2
- 注释为长度为 L 的字符流
SOS: Start Of Scan:
~~~~~~~~~~~~~~~~~~~
- $ff, $da (SOS)
- 长度 (高字节, 低字节), 必须是 6+2*(扫描行内组件的数量)
- 扫描行内组件的数量 (1 byte), 必须 >= 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 段.
没有评论:
发表评论