您的位置:首页 > 其它

实验五 JPEG解码

2017-07-06 20:48 120 查看
实验原理

JPEG简介

JPEG是Joint PhotographicExperts Group(联合图像专家小组)的缩写,是第一个国际图像压缩标准。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。

JPEG 在文件中以 Segment 的形式组织,它具有以下特点:

1.均以 0xFF 开始,后跟 1 byte 的 Marker 和 2 byte 的 Segment length(包含表示 Length 本身所占用的 2 byte,不含“0xFF” + “Marker” 所占用的 2 byte);

2.采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;

3. Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;

JPEG编码原理图

 零偏置:零偏置,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。零偏置:零偏置,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。零偏置:零偏置,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。


零偏置:

零偏置,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。

DCT:

经过DCT变换后,图像中的低频分量会集中在左上角,由于图像低频能量高,故而左上角数值大,右下角有较多的0值。

量化:

人眼对低频敏感,对高频不敏感。所以低频细量化,高频粗量化;人眼对亮度信号敏感,对色度信号不敏感。所以亮度和色度分量分别采用不同的量化表。

DC系数差分编码:

8×8图像块经过DCT变换之后得到的DC直流系数有两个特点:系数的数值比较大和相邻8×8图像块的DC系数值变化不大:冗余;根据这个特点, JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值DIFF进行huffma编码:

之字形扫描:

游程编码的扫描过程,由于DCT变换后,越往右下角的分量越小甚至为0,于是采用之字形扫描。将二维降到一维后会在高频部分出现连0,由此可以提高编码效率。

熵编码:

利用相邻块之间DC系数的空间相关性,对DC系数进行DPCM编码,即当前块的DC系数减去前一块的DC系数后编码。对AC系数进行游程编码(Run-Length Ecoding)。

JPEG图像格式介绍:



解码流程:

(1)读取文件

(2)解析Segment Marker 

*解析SOI

*解析APP0 

       检查标识”JFIF”及版本 

       得到一些参数

*解析DQT 

       得到量化表长度(可能包含多张量化表) 

       得到量化表的精度 

       得到及检查量化表的序号(只能是0~3) 

       得到量化表内容(64个数据)

*解析SOF0 

       得到每个sample的比特数、长宽、颜色分量数 

       得到每个颜色分量的ID、水平采样因子、垂直采样因子、使用的量化表序号(与DQT中序号对应)

*解析DHT 

       得到 Huffman
表的类型(AC、DC)、序号 

       依据数据重建 Huffman


*解析SOS 

       得到解析每个颜色分量的DC、AC值所使用的Huffman表序号(与DHT中序号对应)(3)依据每个分量的水平、垂直采样因子计算MCU的大小,并得到每个
MCU 中8*8宏块的个数。

(4)对每个MCU解码(依照各分量水平、垂直采样因子对MCU中每个分量宏块解码)。
*对每个宏块进行Huffman解码,得到DCT系数

*对每个宏块的DCT系数进行IDCT,得到Y、Cb、Cr

*遇到 Segment Marker RST
时,清空之前的 DC DCT
系数

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

(6)将Y、Cb、Cr转化为需要的色彩空间并保存。

实验步骤:

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

YUVViewer观看的YUV文件。

2.程序调试过程中,应做到:理解程序设计的整体框架;理解三个结构体的设计目的;

   struct  huffman_table      struct  component      struct  jdec_private

主要代码分析:

理解struct huffman_table、struct component、struct jdec_private三个结构体的设计目的:

struct huffman_table
{
/* Fast look up table, using HUFFMAN_HASH_NBITS bits we can have directly the symbol,
* if the symbol is <0, then we need to look into the tree table */
short int lookup[HUFFMAN_HASH_SIZE];
/* code size: give the number of bits of a symbol is encoded */
unsigned char code_size[HUFFMAN_HASH_SIZE];
/* some place to store value that is not encoded in the lookup table
* FIXME: Calculate if 256 value is enough to store all values
*/
uint16_t slowtable[16-HUFFMAN_HASH_NBITS][256];
};

struct component
{
unsigned int Hfactor;//水平采样因子
unsigned int Vfactor;//垂直采样因子
float *Q_table; /* Pointer to the quantisation table to use */
struct huffman_table *AC_table;
struct huffman_table *DC_table;
short int previous_DC; /* Previous DC coefficient */
short int DCT[64]; /* DCT coef */
#if SANITY_CHECK
unsigned int cid;
#endif
};

typedef void (*decode_MCU_fct) (struct jdec_private *priv);
typedef void (*convert_colorspace_fct) (struct jdec_private *priv);

struct jdec_private
{
/* Public variables */
uint8_t *components[COMPONENTS];
unsigned int width, height; /* Size of the image */
unsigned int flags;

/* Private variables */
const unsigned char *stream_begin, *stream_end;
unsigned int stream_length;

const unsigned char *stream; /* Pointer to the current stream */
unsigned int reservoir, nbits_in_reservoir;

struct component component_infos[COMPONENTS];
float Q_tables[COMPONENTS][64]; /* quantization tables */
struct huffman_table HTDC[HUFFMAN_TABLES]; /* DC huffman tables */
struct huffman_table HTAC[HUFFMAN_TABLES]; /* AC huffman tables */
int default_huffman_table_initialized;
int restart_interval;
int restarts_to_go; /* MCUs left in this restart interval */
int last_rst_marker_seen; /* Rst marker is incremented each time */

/* Temp space used after the IDCT to store each components */
uint8_t Y[64*4], Cr[64], Cb[64];
/*添加两个指针用于存放要输出的DC、AC图像,若要修改具体要输出的
AC分量,则要修改函数“output_AC_Image”*/
short int *DC_Image;
short int *AC_Image;

jmp_buf jump_state;
/* Internal Pointer use for colorspace conversion, do not modify it !!! */
uint8_t *plane[COMPONENTS];

};
将输出文件保存为可供YUVViewer观看的YUV文件

static void write_add_yuv(const char *filename, int width, int height, unsigned char **components)
{
FILE *F;
char temp[1024];

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[2], width*height / 4, 1, F);
fclose(F);
}

tinyjpeg.h

FILE *c_file;//输出表格的文件指针  
FILE *acfile ;// AC图像  
FILE *dcfile ;// DC图像  
static char out_str[20];// c_file中输出的字符串  
static unsigned char *outDCbuf = NULL;// 为DC图像开缓冲区  
static unsigned char *outACbuf = NULL; 为AC图像开缓冲区

enum tinyjpeg_fmt {
TINYJPEG_FMT_GREY = 1,
TINYJPEG_FMT_BGR24,
TINYJPEG_FMT_RGB24,
TINYJPEG_FMT_YUV420P,
TINYJPEG_FMT_YUV,
};


loadjpeg.c

int convert_one_image(const char *infilename, const char *outfilename, int output_format)
{
......
switch (output_format)
{
......
case TINYJPEG_FMT_YUV:
write_add_yuv(outfilename, width, height, components);
}
......
}


提取量化表

static const unsigned char zigzag[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  
  
};  
  
/*build_quantization_table*/  
//get the Q table  
#if C_TABLE  
  snprintf(out_str, sizeof(out_str),"%d", ref_table[*(zz-1)]);  //按照zigzag索引  
  fprintf(c_file,"%s\t",out_str);  
  if(j == 7)  
   fprintf(c_file,"\n\n");  
  fflush(c_file);  
#endif
以8×8的二维矩阵形式输出量化表,以TAB键为间隔

static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
......

for (i=0; i<8; i++)
{
for (j=0; j<8; j++)
{
#if TABLES
fprintf(f_tables, "%d\t", ref_table[*zz]);
fflush(f_tables);
if (j == 7)
{
fprintf(f_tables,"\n");
fflush(f_tables);
}

#endif
......
}
}

}


输出哈夫曼码表:

static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)
{
......
for (i=0; huffsize[i]; i++)
{
......
#if TABLES
fprintf(f_tables, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(f_tables);
#endif
......

}
}


输出DC图像与某一个AC值图像

output_DC_AC_Image用于将AC系数、某一AC系数存放在struct jdec_private的DC_Image和AC_Image空间中,并且根据色度取样格式、MCU的不同有不同的存放方式。

static void output_DC_AC_Image(struct jdec_private *priv,int temp)
{
static long int i = 0;
int row, column;
int width;
width = priv->width/8;
switch ( temp)
{
case 0://2*2
row = i / (2 * width);
column = (i % (2 * width)) / 4;
if (i % 4 == 0)
{
priv->DC_Image[row * 2 * width + column * 2] = priv->component_infos[0].DCT[0];
priv->AC_Image[row * 2 * width + column * 2] = priv->component_infos[0].DCT[1];
}
else if (i%4==1)
{
priv->DC_Image[row * 2 * width + column * 2 + 1] = priv->component_infos[0].DCT[0];
priv->AC_Image[row * 2 * width + column * 2 + 1] = priv->component_infos[0].DCT[1];
}
else if (i % 4 == 2)
{
priv->DC_Image[(row * 2 +1)* width + column * 2] = priv->component_infos[0].DCT[0];
priv->AC_Image[(row * 2 +1)* width + column * 2] = priv->component_infos[0].DCT[1];
}
else
{
priv->DC_Image[(row * 2 + 1) * width + column * 2 + 1] = priv->component_infos[0].DCT[0];
priv->AC_Image[(row * 2 + 1)* width + column * 2 + 1] = priv->component_infos[0].DCT[1];
}
break;
case 1://1*2
row = i / (2 * width);
column = (i % (2 * width)) / 2;
if (i % 2 == 0)
{
priv->DC_Image[row * 2 * width + column ] = priv->component_infos[0].DCT[0];
priv->AC_Image[row * 2 * width + column] = priv->component_infos[0].DCT[1];
}
else
{
priv->DC_Image[(row * 2 + 1)*width + column ] = priv->component_infos[0].DCT[0];
priv->AC_Image[(row * 2 + 1)*width + column ] = priv->component_infos[0].DCT[1];
}
break;
case 2://2*1
case 3://1*1
priv->DC_Image[i] = priv->component_infos[0].DCT[0];
priv->AC_Image[i] = priv->component_infos[0].DCT[1];
break;
default:
break;
}
i++;
}


向文件写出DC图像、AC图像

static void write_DC_AC_Image(struct jdec_private *priv)
{
int i;
short int DC_min, DC_max, AC_min, AC_max;
unsigned char *temp;
DC_min = priv->DC_Image[0];
DC_max = priv->DC_Image[0];
AC_min = priv->AC_Image[0];
AC_max = priv->AC_Image[0];
temp = (unsigned char*)malloc(priv->height*priv->width / 64);
for (i = 0; i < (priv->height*priv->width/64); i++)
{
if (priv->DC_Image[i] > DC_max)
DC_max = priv->DC_Image[i];
if (priv->DC_Image[i] < DC_min)
DC_min = priv->DC_Image[i];
if (priv->AC_Image[i] > AC_max)
AC_max = priv->AC_Image[i];
if (priv->AC_Image[i] < AC_min)
AC_min = priv->AC_Image[i];
}
for (i = 0; i < (priv->height*priv->width/64); i++)
{
temp[i] = (unsigned char)255 * (priv->DC_Image[i] - DC_min) / (DC_max - DC_min);
}
fwrite(temp, 1, priv->width*priv->height / 64, DC_FILE);
for (i = 0; i < (priv->height*priv->width / 64); i++)
{
temp[i] = (unsigned char)255 * (priv->AC_Image[i] - AC_min) / (AC_max - AC_min);
}
fwrite(temp, 1, priv->width*priv->height / 64, AC_FILE);
if (temp) free(temp);
}


实验结果分析





输出图像直流dc.yuv  交流ac.yuv





蚊子噪声现象

低压缩比



高压缩比

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据压缩