实验五 JPEG解码

来源:互联网 发布:软件企业资质 编辑:程序博客网 时间:2024/06/05 11:23

实验原理

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表的类型(ACDC)、序号 
      
依据数据重建 Huffman

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

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

*对每个宏块的DCT系数进行IDCT,得到YCbCr

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

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

(6)YCbCr转化为需要的色彩空间并保存。

实验步骤:

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

  1. FILE *c_file;//输出表格的文件指针  
  2. FILE *acfile ;// AC图像  
  3. FILE *dcfile ;// DC图像  
  4. static char out_str[20];// c_file中输出的字符串  
  5. static unsigned char *outDCbuf = NULL;// 为DC图像开缓冲区  
  6. 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

蚊子噪声现象

低压缩比

高压缩比


原创粉丝点击