实验五 JPEG原理分析及JPEG 解码器的调试

来源:互联网 发布:魔兽争霸冰封王座mac 编辑:程序博客网 时间:2024/06/03 08:01

一、JPEG简介

1、JPEG简介

JPEG(Joint Photographic Experts Group)是国际电信联盟(International Telecommunication Union,ITU)、国际标准化组织(International Organization for Standardization,ISO)和国际电工委员会(International Electrotechnical Commission,IEC)共同制定的第一个国际图像压缩标准。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域,网站上80%的图像都采用了JPEG压缩标准。

2、JPEG文件格式

(1)peg在文件中以segment的形式组织,有以下特点:每个segment均以OXFF开始,后跟1bit的marker和2bit的segmentlength(包含segmentlength本身所占的2bit,不含0xFF以及marker所占的2bit)。

每个marker对应的意思如下:


(2)采用大位字节序,高位在前,低位在后

(3)data部分中,若0xFF 后面是00,则跳过不读

2.文件内容

SOI:(FFD8)图像开始 APPO:应用程序保留标记  (版本参数信息)

DCT量化表 :量化表长度、量化精度、量化表ID、表项(长度为64bit(8位精度),记录了8*8DCT变换后每个像素的量化步长,由于DC、AC、亮度、色度使用不同的量化编,所有量化表最多有4个)

SOFO 帧图像开始:记录每一帧图像的数据长度、样本数据的位数、图像的高度、图像的宽度、颜色分量数(JFIF使用YCbCr)、颜色分量信息(分量ID(Y、U、v)、采样因子(4:4:4、4:2:2、4:2:0)、量化表ID) 

 DHT(定义huffman码表):表长度、表ID(0:亮度 1:色度)、表类型(0:直流 1:交流)不同位数的码字数量(16字节分别记录了长度为1到16的码字的个数)、权值

DC表:权值的大小直流分量数值的二进制位数,读取后经过查表查得对应的DC值。权值的字节数为DC经DPCM编码后码字个数的总和

AC表:权值的高四位表示当前数值前面有多少个0,低4为表示交流分量数值的二进制位数。

 SOS扫描开始:数据长度、颜色分量数(与SOF0相同)、颜色分量信息(颜色分量ID:1 2 3对应Y U V)、表号:(高位为直流系数使用的hufman表数、低位为交流系数使用的huffman表数)、压缩图像数据

 EOI:(FFD9)图像结束

二、JPEG编解码原理

1.jpeg编码原理

(1)编码原理


(2)流程分析

a.图像预处理:

将输入图像分成若干个8*8的小块儿。在此过程中需对图像的宽和高进行剪切,使其都为8的倍数,不足的部分复制与其最近的像素值。目的是方便进行8*8DCT变换

将原像素值减去128,使像素值在-128~128之间,目的是减少像素值达到3位数(十进制)的概率。像素值越大,量化后所需的码字越长,不利于编码的有效性。

b.DCT

作用:能量守恒:经DCT变换前后,图像的 能量不变

    能量集中:经DCT变换后,能量集中在左上角

    去相关:经DCT变化后,图像的相关性减少,有利于提高后续huffman编码的有效性;从信息论的角度来说,若不去相关,总的量化失真不能写成隔值之和。
由于人眼对低频敏感,高频不敏感,经DCT变化后,多数低频信息集中在左上角,且数值较大,相关性较小,只需对左上角的值进行细量化,能在保证图像质量的同时提高压缩比。
c.量化(中平型量化器)
量化步距根据系数所在的位置和颜色分量来定,Y分量和左上角位置的系数步距小,UV分量和右下角位置的分量步距大
由于人眼对亮度信号比对色度信号更敏感,因此使用了两种量化表:亮度量化值(量化步距小)和色度量化值(量化步距小)
由于人眼对低频敏感,高频不敏感,因此对低频分量采用细量化,高频分量采用粗量化。(如果原始图像细节比较丰富,则量化后去掉的数据多了,差距较大。细节越少的图量化误差越小)
d.编码
DC系数:差分编码
8*8图像块经过DCT变换后,得到的直流系数有两个特点:数值大、相邻数值变化不大。
根据这个特点,JPEG对DC系数进行差分脉冲调制编码(DPCM)(其中,相邻DC系数之差不在进行量化),对相邻图像块之间的量化DC之差DIFF进行huffman编码。
编码方法:
码字由两部分组成:类别ID+类内索引
将每一个DIFF经查下表,判断出在哪一个范围内,记下类别ID,作为码字的第一部分,DIFF的真值作为码字的第二部分。

AC系数:游程编码

先将数据之字形扫描,将二位数据扫描成一维数据,定义(run,level)结构,来对此一维数据编码。其中,run表示非0数据前0的个数,level表示非0数据的值。

run的最大值为1个,用RRRR表示,level的编码类似DC 系数,先分为16个类别,用4位SSSS表示,再查类内索引(用定长码编码)。

即交流huffman码字的前四位表示0的个数,后四位表示该交流分量数值的二进制位数,也即接下来需要读入的位数。

(3)性能、误差分析

蚊子噪声(波纹效应):由于对高频信号做DCT后进行滤波产生。高频信息经JPEG压缩,量化步长大时,DCT系数判为0的概率增加,频域高频截止,时域有较长拖尾u,表现在图像上就是蚊子噪声。

块效应:由于DCT系数量化时,每个块的量化误差不同,导致相邻块之间边沿的不连续性。

轮廓线:

2.jpeg解码流程

(1)读取文件

(2)解析segmentmarker

依次解析出SOI、APPO、DCT、SCF0、DHT、SOS、EOI

(3)依据每个分量的水平采样、垂直采样因子计算MCU(微控制单元,即RAM,即要开辟的图像内存?)的大小,并得到每个MCU中8*8宏块的个数。

(4)对每个MCU解码

对每个宏块进行huffman解码得到DCT系数,进行IDCT得到变换之前的数据。

(5)解析到EOI,解码结束

(6)将得到的Y、Cb、Cr转换成需要的色彩空间并保存。

二、实验步骤

1.逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供 YUVViewer观看的YUV文件。 

在write_yuv中修改如下代码snprintf(temp, 1024, "%s.yuv", filename);  F = fopen(temp, "wb");  fwrite(components[0], width, height, F);  fwrite(components[1], width*height/4, 1, F);  fwrite(components[1], width*height/4, 1, F);  fclose(F);
2. 程序调试过程中,应做到:
- 理解程序设计的整体框架

- 理解三个结构体的设计目的
struct huffman_table :用来存放huffman解码后的DC、AC系数
struct component :用来存放IDCT反变化后的颜色分量
struct jdec_private :用来统筹整个解码过程,包括huffman_table,component
- 理解在视音频编解码调试中TRACE的目的和含义 :trace用来记录解码过程,存放每个解码步骤完成后得到数据
- 会打开和关闭TRACE 

在loadjpeg.c的主函数中,以下代码用来打开trace文件:

#if TRACE  p_trace=fopen(TRACEFILE,"w");//p_trace是追踪文件  if (p_trace==NULL)  {  printf("trace file open error!");  }#endif
以下代码用来关闭文件:

#if TABLES   fclose(hufftable);#endif

- 会根据自己的要求修改TRACE 

在解码过程的每一步都将得到的数据写入trace.file,可在此时更改要写入的trace。例如,将文件的水平、垂直采样因子写入trace的过程如下:

#if TRACE     fprintf(p_trace,"Component:%d  factor:%dx%d  Quantization table:%d\n",           cid, c->Hfactor, c->Hfactor, Q_table ); fflush(p_trace);#endif

3. 以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。 

(1)输出量化矩阵

仿照trace文件的写入写出方法:首先在tinyjpeg.h中加入:

...FILE *octtable;//add by zsy...#define TRACE 1//add by nxn#define OCTTABLES 1 //add by zsy#define  OCTFILE "oct_jpeg.txt"//add by zsy

在loadjpeg.c中打开和关闭octtable:

//打开octtable文件#if OCTTABLES  //octtable=fopen(OCTFILE,"w");//octtable用来输出量化表  //if (octtable==NULL)  //{ // printf("octtable file open error!");  //}//只能处理输入一张图片  snprintf(temp, 1024, "%s.txt", output_filename);  octtable = fopen(temp, "wb");//输入多张图片#endif//关闭octtable文件#if OCTTABLES   fclose(octtable);#endif在parse_DQT(priv, stream)中记下量化表标号: qi = *stream++;//huffman表ID #if OCTTABLES//add by zsy,输出量化表IDfprintf(octtable,"DQT ID:%d\n",qi);fflush(octtable);#endif

在build_quantization_table(table, stream)中得到量化矩阵:

 for (i=0; i<8; i++) {     for (j=0; j<8; j++) { fprintf(octtable,"%d\t",ref_table[*zz]); //add by zsy*qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];//以zig序将量化表存储     } fprintf(octtable,"\n");//add by zsy   }


(2)输出hufman码表:
文件的定义,在主函数中的打开和关闭与上述相同。
在parse_DHT 中,输出huffman表类型和表ID:
 if (index & 0xf0 )//index&0xf0 位index的高四位,1代表AC表,0代表DC表 {//记录AC表ID add by zsy#if HUFFTABLESfprintf(hufftable,"hufftable AC %d号表\n",index&0xf);//index&0xf,index的低四位,代表表号#endif       build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);//得到AC系数 }     else //记录AC表ID add by zsy#if HUFFTABLESfprintf(hufftable,"hufftable DC %d号表\n",index&0xf);#endif       build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]); 在build_huffman_table中输出huffman码表: #if HUFFTABLES//add by zsy      fprintf(hufftable,"val=%2.2x\tcode=%8.8x\tcodesize=%2.2d\r\n", val, code, code_size); fflush(hufftable);    #endif


4. 输出DC、AC图像并经过huffman统计其概率分布(使用第三个实验中的Huffman编码器)。 

在loadjpeg.c中敌营DC、AC图像文件、指向文件的指针、以及Buf

FILE *DCimage;  FILE *ACimage;  float *DC=NULL;  unsigned char *DCBuf=NULL;  unsigned char *ACBuf=NULL; 
在main函数中开空间、打开关闭文件:
 DCFileName = argv[4];   DCimage=fopen(DCFileName,"wb");   ACFileName = argv[5];   ACimage=fopen(ACFileName,"wb");  
DC = (float*)malloc(1024 * 1024);  DCBuf=(unsigned char *)malloc(1024 * 1024);  ACBuf=(unsigned char*)malloc(1024 * 1024); 
在tinyjpeg中输出huffman表ID:
if (index & 0xf0 ) {//记录AC表ID add by zsy#if HUFFTABLESfprintf(hufftable,"hufftable AC %d号表\r\n",index&0xf);//index&0xf,index的高四位,代表AC表号#endifbuild_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);}     else {  //记录DC表ID add by zsy#if HUFFTABLESfprintf(hufftable,"hufftable DC %d 号表\r\n",index & 0xf);#endif       build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]); }
在build_huffman_table中输出huffman表:
#if HUFFTABLES//add by zsy      fprintf(hufftable,"val=%2.2x\tcode=%8.8x\tcodesize=%2.2d\r\n", val, code, code_size);
fflush(hufftable);
5. 观察蚊子噪声

四、实验结果

1.量化矩阵


2、huffman表:

3、DC\AC图像

DC图像:DCT[0]

AC 图像:                    DCT[1]                                                           DCT[2]                                                           DCT[3]

   

4.蚊子噪声

五、总结

1、由DC、AC图像可以看出,DC图像基本保留了原图的细节,而AC图像随着AC系数的减小图像也越来越模糊。

2、整个jpeg解码程序短小精悍,值得细细品味。整个程序顺下来犹如读了一本好书,荡气回肠。

3、其中有些地方还没有弄懂,相信以后写程序的时候还会回来再看几遍。

原创粉丝点击