1. 实验说明
前提:
JPEG的图像数据用Pspad16进制打开为一大堆16进制数据,这对数据实际分为两大部分,Marker(标记0x FFxx)与数据.其中标记为2个byte.
根据上课理解以及课后阅读规格书以及实际读取,归纳出常用的Marker有这几种:SOI,APP0,DQT,SOF0,DHT,EOI等
下面一边对JPEG数据解读一边解释相关Marker意义以及数据的读取.
例图:
使用Pspad打开图像所得16进制数据截图如下:
2. 基本Marker介绍
a. FFD8:SOI(Start of Image)
表示:为图像的开始
b. FFE0:APP0
表示:应用程序的保留标记/数据
长度:之后紧接着的2个byte为APP0段的数据长度(从长度开始包括长度的2个byte)
l 这里由于我们不关注APP0的信息,因此直接读取长度后根据长度跳过此段.
长度:0010 + 0003(当前位置) = 0013
直接找到0013的位置.继续读取
c. FFDB:DQT (Define Quantization Table)
表示:定义量化表
内容:
① 长度
两个byte紧接着FFDB,表示定义量化表的长度(包括自身)
② 量化表
a) 精度 & 量化表ID : 占一个byte
精度: 前四位,两个选择,0代表精度为8位,1代表16位.
量化表ID: 后四位, 取值为0~3
b) 表内容:占 64*(精度+1)个字节
l 可以看到例图中有两个FFDB Marker,读取内容具体在程式解释时介绍.
同样地跳过量化表的内容之后,读到了009D的位置,接下来就看到FFC0的Marker
d. FFC0:SOF0 (Start of Frame)
表示:帧图像开始
内容:
① 长度
2个byte接近着Marker之后表示本段的长度(包括自身)
② 精度
1个byte,每个数据样本的位数
③ 图像高度
2个byte,表示图像高度(单位:像素)
④ 图像宽度
2个byte,表示图像宽度(单位:像素)
⑤ 颜色分量数
1个byte,只有 3个数值可选
a) 1:灰度图
b) 3:YcbCr或者 YIQ
c) 4:CMYK
⑥ 颜色分量信息
长度:颜色分量数×3 byte
a) 颜色分量ID, 长1 byte
b) 水平/垂直采样因子
1个byte
高 4位:水平采样因子; 低4位:垂直采样因子
c) 量化表
1个byte,表示当前分量使用的量化表的 ID
l 照着说明读取本例图的数据可得:
FFC0 0011 08 0122 01B6 03 01 11 00 02 11 01 03 11 01
0011: 数据长度为17
08: 每个数据样本的位数为8位
0122: 图像高度为0x0122 = 290px
01B6: 图像宽度为 438px
03:颜色分量数为3:YCrCb
01 11 00:01分量:Y ; 水平垂直采样1, 1; 量化表为0
02 11 01:02分量:Cr; 水平垂直采样1, 1; 量化表为1
03 11 01:03分量:Cb; 水平垂直采样1, 1; 量化表为1
FFC0段完了之后读取数据,读到FFC4
e. FFC4:DHT(Define Huffman Table)
表示:定义哈夫曼表
内容:
① 长度
2个byte紧接着Marker表示数据的长度(包括自身)
② 哈夫曼表
a) 哈夫曼表类型 与 哈夫曼表ID
长度:1个byte
类型:前四位, 0为DC直流表, 1为AC交流表
表ID:第四位.
b) 各个编码长度的数量
长度:16个byte
具体: L1, L2, L3, L4, L5, L6, L7, L8, L9, L10, L11, L12, L13, L14, L15, L16
表示: 编码长度为1的有L1个;
编码长度为2的有L2个;
编码长度为3的有L3个……
c) 编码内容
长度: Length = (L1+L2+…+L16) 个byte
表示: DC为对应编码的顺序/Category/权值
AC为对应编码的Run/Size
具体: L1个:V1-0, V1-1…
L2个:V2-0, V2-1…
L16个:V16-0,V16-1…
l 可以看到例图中有四个FFDB Marker,读取内容具体在程式解释时介绍.
同样地跳过哈夫曼表的内容之后,读到了0180的位置,接下来就看到FFDA的Marker
f. FFDA:SOS(Start of Scan)
① 长度
2个byte(但包括自身)
② 颜色分量数
1个byte (与SOF中的值类似)值为三种
a) 1:灰度图
b) 3:YCbCr或 YIQ
c) 4:CMYK
③ 颜色分量信息
长度:颜色分量数×3 byte
a) 颜色分量ID, 长1 byte
b) 直流/交流系数表号,长1 byte
高4位:直流分量使用的哈夫曼树编号
低4位:交流分量使用的哈夫曼树编号
④ 压缩图像数据
a)谱选择开始 1字节 固定值 0x00
b)谱选择结束 1字节 固定值 0x3F
c)谱选择 1字节 在基本 JPEG中总为 00
l 照着说明读取例图数据可得:
FFDA 000C 03 01 00 02 11 03 11 00 3F 00
000c: 长度为12
03: 颜色分量数 3: YCbCr
01 00: 颜色分量01(Y) 直流使用0号,交流使用0号
02 11: 颜色分量02(Cr) 直流使用1号,交流使用1号
03 11: 颜色分量03(Cb) 直流使用1号,交流使用1号
l 本段结束后,紧接着就是真正的图像信息了.图像信息直至遇到一个标记代码就自动结束,一般就是以 EOI 标记表示结束.
l 真正的图像压缩信息分析于后面解码时解释
g. FFD9:EOI(End of Image)
表示图像结束
3. 程序解码分析:
a. 程序界面
界面设计四个tabsheet分开显示读取分析到的数据以及一个open按钮打开图像文件
b. 程序设计
① 文件读取
使用FILE型的指针 FILE *fp 打开目标图片.
对于fp具体使用到的函数有:
l fp = fopen(路径, 方式);
l fseek()
原型是int fseek(FILE *stream, long offset, int whence);如果成功返回0,参数offset是移动的字符数,whence是移动的基准,取值是符号常量.
SEEK_SET 0 文件开头
SEEK_CUR 1 当前读写的位置
SEEK_END 2 文件尾部
l fread()
原型是size_t fread(void *ptr, size_t size, size_t n, FILE *stream);参数ptr是保存读取的数据,void*的指针可用任何类型的指针来替换,如char*、int *等等来替换;size是每块的字节数;n是读取的块数,如果成功,返回实际读取的块数(不是字节数)
l feof(fp)
判断是否到达文件尾,是则返回true,否则false
l fclose()
fclose()的功能就是关闭用fopen()打开的文件,其原型是:int fclose(FILE *fp);如果成功,返回0,失败返回EOF.
② 相关Marker位置读取
先用一个函数来全盘扫描文件,寻找FFDB以及FFC4的Maker位置并储存于数组中.
对应判断则用Marker的10进制值来判断.如FF为255,DB为219.
③ 量化表读取分析
根据FFDB相关说明,读取了表的精度以及ID之后,读取64个字节存于数组中则可.
程序先用一维数组储存ZigZag扫描索引,再用另一个一维数组读取64个数据,然后用ZigZag索引读取该一维数组并显示于文本中则可.
结果量化表为:
④ 哈夫曼表读取分析
根据FFC4相关说明,读取了哈夫曼表的类型以及ID之后,读取L1~L16(Ln)之后,先产生Ln个n,继续读取Vn-m值(权值),按顺序赋值给刚刚产生的长度顺序(若是AC表,此处的Vn-m值则包含两个信息为 Run/Size,切分则可),同时产生相应编码.
l 编码产生算法如下:
每个Huffman表第一个编码值为0,下一个编码值的产生为:与前一编码相同长度的则按顺序在前一个编码值加一,不同长度的则=(上一个编码值+1)*2,产生新的编码值.再用相应长度表示该值的二进制值则可.
l 字节值与表类型
00
|
DC /直流0号表
|
01
|
DC /直流1号表
|
10
|
AC /交流0号表
|
11
|
AC /交流1号表
|
l 哈夫曼表获取程序
void TForm1::getHuffmanTable(unsigned char *LenList, vector<int> Value, bool isAC)
{
//五个空格
this->Edittext->Text = this->Edittext->Text+ "\n序号 长度 值 二进制 权值\n";
//传进了的Value为权值,values
vector<int> Code;
int rankNow = 0;
int ValueNow = 0;
if(!isAC)
{
for(int i = 0 ; i < 16; i ++)
{
for(int j = 0 ; j <LenList[i]; j ++)
{
this->Edittext->Text = this->Edittext->Text+ AnsiString(rankNow) + " ";//序号
this->Edittext->Text = this->Edittext->Text+ AnsiString((i+1)) + " ";//长度
Code.push_back(ValueNow);
this->Edittext->Text = this->Edittext->Text+ AnsiString(ValueNow) + " ";//值
showBinary(ValueNow, (i+1), 0);
this->Edittext->Text = this->Edittext->Text+ AnsiString(Value[rankNow++]) + " \n";
ValueNow ++;
}
ValueNow = ValueNow<<1;
}
}
else
{
int R,S;
for(int i = 0 ; i < 16; i ++)
{
for(int j = 0 ; j <LenList[i]; j ++)
{
this->Edittext->Text = this->Edittext->Text+ AnsiString(rankNow) + " ";//序号
this->Edittext->Text = this->Edittext->Text+ AnsiString((i+1)) + " ";//长度
Code.push_back(ValueNow);
this->Edittext->Text = this->Edittext->Text+ AnsiString(ValueNow) + " ";//值
showBinary(ValueNow, (i+1), 0);
R = Value[rankNow]/16;
S = Value[rankNow]%16;
this->Edittext->Text = this->Edittext->Text+ AnsiString(R)+"/"+AnsiString(S)+ " \n";
rankNow++;
ValueNow ++;
}
ValueNow = ValueNow<<1;
}
}
}
|
l 按以上顺序产生哈夫曼表如下:
TcTh: 00 DC /直流0号表 Y
序号 长度 值 二进制 权值
0 2 0 00 5
1 3 2 010 3
2 3 3 011 4
3 3 4 100 6
4 3 5 101 7
5 4 12 1100 1
6 4 13 1101 2
7 4 14 1110 8
8 5 30 11110 0
9 6 62 111110 9
|
TcTh: 10 AC /交流0号表 Y
序号 长度 值 二进制 权值
0 2 0 00 0/1
1 3 2 010 0/2
2 3 3 011 0/3
3 3 4 100 0/4
4 4 10 1010 0/0
5 4 11 1011 0/5
6 4 12 1100 1/1
7 5 26 11010 0/6
8 5 27 11011 1/2
9 5 28 11100 2/1
10 6 58 111010 3/1
11 7 118 1110110 0/7
12 7 119 1110111 1/3
13 7 120 1111000 2/2
14 7 121 1111001 4/1
15 7 122 1111010 5/1
16 7 123 1111011 6/1
17 8 248 11111000 0/8
18 8 249 11111001 1/4
19 8 250 11111010 3/2
20 8 251 11111011 7/1
21 9 504 111111000 2/3
22 9 505 111111001 4/2
23 9 506 111111010 8/1
24 10 1014 1111110110 1/5
25 10 1015 1111110111 9/1
26 10 1016 1111111000 10/1
27 11 2034 11111110010 0/9
28 11 2035 11111110011 1/6
29 11 2036 11111110100 3/3
30 11 2037 11111110101 5/2
31 11 2038 11111110110 6/2
32 11 2039 11111110111 11/1
33 11 2040 11111111000 12/1
34 11 2041 11111111001 13/1
35 12 4084 111111110100 2/4
36 12 4085 111111110101 4/3
37 12 4086 111111110110 7/2
38 12 4087 111111110111 11/3
39 13 8176 1111111110000 1/7
40 13 8177 1111111110001 2/6
41 13 8178 1111111110010 3/4
42 13 8179 1111111110011 7/3
43 13 8180 1111111110100 8/2
44 13 8181 1111111110101 11/4
45 13 8182 1111111110110 14/1
46 14 16366 11111111101110 2/5
47 14 16367 11111111101111 2/7
48 14 16368 11111111110000 4/4
49 14 16369 11111111110001 4/5
50 14 16370 11111111110010 5/3
51 14 16371 11111111110011 6/3
52 14 16372 11111111110100 6/4
53 14 16373 11111111110101 7/4
54 14 16374 11111111110110 7/5
55 14 16375 11111111110111 7/6
56 14 16376 11111111111000 8/4
57 14 16377 11111111111001 10/4
58 15 32756 111111111110100 3/5
59 15 32757 111111111110101 3/6
60 15 32758 111111111110110 3/8
61 15 32759 111111111110111 4/6
62 15 32760 111111111111000 5/4
63 15 32761 111111111111001 5/5
64 15 32762 111111111111010 5/6
65 15 32763 111111111111011 6/5
66 15 32764 111111111111100 10/3
67 16 65530 1111111111111010 9/2
68 16 65531 1111111111111011 10/2
69 16 65532 1111111111111100 11/2
70 16 65533 1111111111111101 15/0
71 16 65534 1111111111111110 15/1
|
TcTh: 01 DC /直流1号表 CbCr
序号 长度 值 二进制 权值
0 2 0 00 2
1 2 1 01 3
2 3 4 100 0
3 3 5 101 4
4 3 6 110 5
5 4 14 1110 1
6 5 30 11110 6
7 6 62 111110 7
8 7 126 1111110 8
|
TcTh: 11 AC /交流1号表 CbCr
序号 长度 值 二进制 权值
0 2 0 00 0/0
1 2 1 01 0/1
2 3 4 100 0/2
3 3 5 101 0/3
4 4 12 1100 0/4
5 4 13 1101 1/1
6 5 28 11100 1/2
7 5 29 11101 2/1
8 6 60 111100 3/1
9 7 122 1111010 0/5
10 7 123 1111011 1/3
11 7 124 1111100 2/2
12 7 125 1111101 4/1
13 8 252 11111100 3/2
14 9 506 111111010 1/4
15 9 507 11111101 2/3
16 9 508 111111100 5/1
17 10 1018 1111111010 0/6
18 10 1019 1111111011 3/3
19 10 1020 1111111100 4/2
20 11 2042 11111111010 1/5
21 11 2043 11111111011 6/1
22 11 2044 11111111100 8/1
23 12 4090 111111111010 1/6
24 12 4091 111111111011 7/1
25 12 4092 111111111100 9/1
26 13 8186 1111111111010 4/3
27 13 8187 1111111111011 5/2
28 13 8188 1111111111100 10/1
29 13 8189 1111111111101 11/1
30 14 16380 11111111111100 2/4
31 14 16381 11111111111101 6/2
32 14 16382 11111111111110 14/1
|
⑤ 图像压缩数据解压缩
图像压缩信息截图:
l 程序读出的16进制(每100个byte分开)
8 5 E 8 F D 5 1 0 A 4 6 8 5 B B 4 B B A A D 3 1 6 D 9 3 9 4 B 4 C 6 9 A F A 3 6 B 4 F 2 4 9 3 E 1 0 6 B C 0 D 9 5 4 A 1 6 6 C F A 5 5 5 1 8 D B 1 D 1 6 9 F 6 1 1 A 4 A D E F 7 6 7 D 6 4 5 3 3 1 2 1 A D 5 6 A 9 2 E 2 1 B 7 D B 4 0 2 A D 8 4 E 5 2 4 2 B E C 6 B B 9 7 3 7 2 8 6 8 C C 9 D 7 E C C 8 B 9 1 B 4 F 7 6 7 6 E 0 8 0 4 7 0 7 1 C 0 A F 3 A D 8 0 8 2 B 0 8 2 4 9 4 9 C 6 4 7 B 1 A 1 6 1 6 C 7 F //
2 E 0 A 6 5 D B 2 5 4 6 4 9 0 8 E F 9 A 5 B 6 4 A 8 7 0 0 2 9 2 4 6 7 F 8 D 1 D 7 1 E 5 3 5 2 2 6 B 4 B 4 5 0 D D 9 6 7 C 3 8 3 B D 8 F C 9 B D 2 8 5 E 1 8 B 9 D 9 E E 1 C A 2 1 3 6 D 0 4 2 5 B 5 E 4 9 2 4 F A E 6 B 7 7 3 E D E 5 1 8 C 4 B 3 8 D 5 F B 6 F 6 4 A 5 3 A 0 9 8 8 6 C 3 8 D C 6 7 1 6 D 3 2 A 3 B D 2 D 1 F 1 A 1 0 A F 5 0 0 D 6 6 4 2 C 3 4 9 C 9 C 8 7 1 6 A D 3 7 1 6 D 5 B D 6 7 7 3 E F //
3 A 4 A 9 6 F A F A A C D 4 9 3 D 8 A 7 B 4 5 A 3 A 7 A F 8 F B 1 6 6 8 E D A 1 4 4 7 7 4 9 2 9 1 8 E 4 5 5 6 5 3 D 9 9 F 7 4 3 B 0 D 3 7 A B 5 D D 8 D 8 7 0 2 0 8 3 D 4 9 1 5 1 F 6 2 5 2 D 2 3 7 9 7 3 6 3 D D 1 8 0 D 9 4 B 4 8 7 3 8 3 D E 0 4 8 C 8 F 6 A 3 A 9 F 7 D 9 C 6 0 4 9 9 6 3 7 1 D 6 C 8 6 5 6 C 2 D 4 0 E 7 3 9 D A 4 D 1 E 4 4 6 3 2 4 7 6 20 D 5 C B 7 A D 1 1 6 4 3 9 7 9 0 8 8 7 1 D 9 1
//尾到02B0行处
|
l 利用程序全部转为二进制数值后对二进制进行手工解码.
数值的译码表如下
AC((Run/Size) 的形式來描述,RS = binary 'RRRRSSSS')
SSSS AC coefficients
|
1 -1, 1
2 -3,-2, 2, 3 3 -7..-4, 4..7 4 -15..-8, 8..15 5 -31..-16, 16..31 6 -63..-32, 32..63 7 -127..-64, 64..127 8 -255..-128, 128..255 9 -511..-256, 256..511 10 -1023..-512, 512..1023 |
DC
|
SSSS DIFF values
|
0 0
1 -1, 1 2 -3,-2, 2, 3 3 -7..-4, 4..7 4 -15..-8, 8..15 5 -31..-16, 16..31 6 -63..-32, 32..63 7 -127..-64, 64..127 8 -255..-128, 128..255 9 -511..-256, 256..511 10 -1023..-512, 512..1023 11 -2047..-1024, 1024..2047 |
l 由于整理的采样因子是1:1:1,则Y分量,Cb分量和Cr的分量的水平方向和垂直方向都是每一个像素采样1次.也就是图像的每一个像素都是采样点.
因此,对于整张图片来说,数据流的数据为:
[Y1,Cb1,Cr1], [Y2,Cb2,Cr2], [Y3,Cb3,Cr3], [Y4,Cb4,Cr4], [Y5,Cb5,Cr5]……
颜色分量单元内部综合运用了RLE行程编码和哈夫曼编码来压缩数据.每
个像素的数据流由两部分构成:编码和数值,并且两者基本以互相隔开方式出现(除非该编码的权值为零).具体读入单个颜色分量单元的步骤如下:
a) 从此颜色分量单元数据流的起点开始一位一位的读入,直到读入的编码与该分量直流哈夫曼树的某个码字(叶子结点)一致,然后用直流哈夫曼树查得该码字对应的权值.权值(共 8位)表示该直流分量数值的二进制位数,也就是接下来需要读入的位数.
b) 继续读入位数据,直到读入的编码与该分量交流哈夫曼树的某个码字(叶子结点)一致,然后用交流哈夫曼树查得该码字对应的权值.权值的高4位表示当前数值前面有多少个连续的零,低4位表示该交流分量数值的二进制位数,也就是接下来需要读入的位数.
c) 不断重复步骤 b,直到满足交流分量数据结束的条件.而结束条件有两个,只要满足其中一个即可:
读入码字的权值为零,表示往后的交流变量全部为零;
已经读入 63 个交流分量.
d) 各个数值的译码按照上面的两个译码表进行.
l 译码过程
把所有的颜色分量单元按颜色分量(Y、Cr、Cb)分类.每一种颜色分量内,相邻的两个颜色分量单元的直流变量是以差分来编码的.也就是说,通过步骤 3解码出来的直流变量数值只是当前颜色分量单元的实际直流变量减去前一个颜色分量单元的实际直流变量.也就是说,当前直流变量要通过前一个颜色分量单元的实际(非解码)直流分量来校正: DCn=DCn-1+Diff
其中Diff为差分校正变量,也就是直接解码出来的直流系数.但如果当前颜色分量单元是第一个单元,则解码出来的直流数值就是真正的直流变量.
我的做法是先解码,直接解出diff,等绘制block时再算DCn=DCn-1+Diff.
l 译码
<mcu1> Y
1000 0101 111010 001111 11010 101000 100 0010 100 1000 11010 000101
权=6 0/6 值=-63+15 0/6 值=40 0/4 值=2-15 0/4 8 0/6 5-63=-58
值=-63+11=-52 =-48 =-13
[-52] [-48] [40] [ -13] [8] [-58]
1011 10110 100 1011 1011 10101 010 11 010 01 100 0101 1011 01100 100 1110
0/5 22 0/4 11 0/5 21 0/2 3 0/2 1-3=-2 0/4 5-15 0/5 12-31 0/4 14
[22] [11] [21 ] [3] [-2 ] [-10] [-19] [14]
010 10 010 11 010 01 100 0110 100 1101 011 111 010 00 11011 01 011 010
0/2 2 0/2 3 0/2 1-3 0/4 6-15 0/4 13 0/3 7 0/2 -3 1/2 1-3 0/3 2-7
[2] [ 3 ] [-2] [-9] [13] [7] [-3] [0,-2] [ -5]
011 110 010 0100 1 00 1 001 1 1110 0001 000 0 0110 10
0/3 6 0/2 1-3 0/1 1 0/1 1 0/1 1 2/2 1-3 0/1 -1 0/1 1 0/0
[6] [-2] [1] [1] [1] [0,0,-2] [-1] [1] EOB
Cb
11 1100 0000 1101 1001 0101 0100 1010 0001 0110 0110 11 00
权:6 1-63 0/3 4 0/3 2-7 0/2 2 0/2 -3 0/3 4 1/1 1 0/0
[-62] [4] [-5] [2] [-3] [4] [0,1] EOB
Cr
1111 1010 0101 0101 0101 0001 1000 1101 1011 0001 1101 000
权:7 74 0/3 2-7 0/2 1-3 0/2 1-3 0/3 5 0/2 1-3 1/1 -1 0/0
[74] [-5] [-2] [-2] [5] [-2] [0,-1] EOB
由于部落格的文字排版一直出现问题(二进制编码排版对不齐以及保存文章出错),因此后续的解码以及作业心得如有需要可以点击 pdf文章 进入直接浏览pdf文章.
沒有留言:
張貼留言