实验报告5_JPEG编解码原理及程序调试

来源:互联网 发布:汉诺塔的非递归算法 编辑:程序博客网 时间:2024/06/16 17:04

一.实验原理

JPEG是一种有损压缩格式,能够将图像压缩在很小的储存空间,图像中重复或不重要的资料会被丢失,因此容易造成图像数据的损伤。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。

JPEG编码流程:

JPEG编解码流程:


1.编码过程详解:

(1)零偏置:

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

(2)8x8DCT变换

将图像分为8x8的子块,边缘不满足8x8的部分则用边缘像素填充,然后对每一个子块进行DCT变换。

DCT变换公式:


(3)对DCT系数进行量化

量化采用中平型均匀量化器,量化步距是按照系数所在位置颜色分量来确定。

因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值
根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化


(4)编码

DC编码:

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

对DIFF用Huffman编码:分成类别

-类别ID:一元码编码
-类内索引:采用定长码


AC编码(游程编码):

由于经DCT变换后,系数大多数集中在左上角,即低频分量区,因此采用Z字形按频率的高低顺序读出,可以出现很多连零的机会。可以使用游程编码。尤其在最后,如果都是零,给出EOB (End of Block)即可。


在JPEG和MPEG编码中规定为:(run, level)
-表示连续run个0,后面跟值为level的系数
-如:0,2,0,0,3,0,-4,0,0,0,-6,0,0,5,7
-表示为(1, 2),(2, 3) ,…
编码:
-Run: 最多15个,用4位表示RRRR
-Level:类似DC
  -分成16个类别,用4位表示SSSS表示类别号
  -类内索引
-对(RRRR, SSSS)联合用Huffman编码
-对类内索引用定长码编码

2.JPEG数据格式

JPEG语法结构中定义了一系列标记(Markers)
-均以0xFF开始,后跟1字节的标记标识符和2字节的标记长度以及该标记所对应的payload
-标记长度部分高位在前,低位在后,不包含该标记的头两个字节
-熵编码部分的数据在0xFF后由编码器插入0x00,解码器解码时跳过此字节不予处理
-SOI( Start Of Image)和EOI( End Of Image)标记没有payload


可在下例中找到对应:

二.实验流程

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

(2)理解程序设计的整体框架

(3)理解三个结构体的设计目的:struct huffman_table, struct component , struct jdec_private

(4)理解在视音频编码调试中 TRACE TRACE的目和含义
         -会打开和关闭 TRACE TRACE
         -会根据自己的要求修改 TRACE TRACE

(5)以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。

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

(7)输出某一个 AC 值图像并统计其概率分布(使用第三个实验中的 Huffman HuffmanHuffman 编码器)。

三.关键代码分析

1.将输出文件保存成YUV格式

tinyjpeg.h

enum tinyjpeg_fmt{   TINYJPEG_FMT_GREY = 1,   TINYJPEG_FMT_BGR24,   TINYJPEG_FMT_RGB24,   TINYJPEG_FMT_YUV420P,   TINYJPEG_FMT_YUV,//add by hyw};
loadjpeg.c

main函数中:

……int output_format = TINYJPEG_FMT_YUV;//将输出格式设置为yuv……input_filename = argv[current_argument];  if (strcmp(argv[current_argument+1],"yuv420p")==0)    output_format = TINYJPEG_FMT_YUV420P;  else if (strcmp(argv[current_argument+1],"rgb24")==0)    output_format = TINYJPEG_FMT_RGB24;  else if (strcmp(argv[current_argument+1],"bgr24")==0)    output_format = TINYJPEG_FMT_BGR24;  else if (strcmp(argv[current_argument+1],"grey")==0)    output_format = TINYJPEG_FMT_GREY;  else if (strcmp(argv[current_argument + 1], "yuv") == 0)//add by hyw    output_format = TINYJPEG_FMT_YUV;  else    exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n");  output_filename = argv[current_argument+2]; 
int load_multiple_times(const char *filename, const char *outfilename, int output_format){……   switch (output_format)   {    case TINYJPEG_FMT_RGB24:    case TINYJPEG_FMT_BGR24:      write_tga(outfilename, output_format, width, height, components);      break;    case TINYJPEG_FMT_YUV420P:      write_yuv(outfilename, width, height, components);      break;    case TINYJPEG_FMT_GREY:      write_pgm(outfilename, width, height, components);      break;    case TINYJPEG_FMT_YUV:      write_pgm(outfilename, width, height, components);//add by hyw      break;   }……}

int convert_one_image(const char *infilename, const char *outfilename, int output_format){……   switch (output_format)   {    case TINYJPEG_FMT_RGB24:    case TINYJPEG_FMT_BGR24:      write_tga(outfilename, output_format, width, height, components);      break;    case TINYJPEG_FMT_YUV420P:      write_yuv(outfilename, width, height, components);      break;    case TINYJPEG_FMT_GREY:      write_pgm(outfilename, width, height, components);      break;    case TINYJPEG_FMT_YUV:      write_yuv_file(outfilename, width, height, components);//add by hyw      break;   }   ……}

int tinyjpeg_decode(struct jdec_private *priv, int pixfmt){……switch (pixfmt) { case TINYJPEG_FMT_YUV420://add by hyw     case TINYJPEG_FMT_YUV420P:       colorspace_array_conv = convert_colorspace_yuv420p;       if (priv->components[0] == NULL) priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);       if (priv->components[1] == NULL) priv->components[1] = (uint8_t *)malloc(priv->width * priv->height/4);       if (priv->components[2] == NULL) priv->components[2] = (uint8_t *)malloc(priv->width * priv->height/4);       bytes_per_blocklines[0] = priv->width;       bytes_per_blocklines[1] = priv->width/4;       bytes_per_blocklines[2] = priv->width/4;       bytes_per_mcu[0] = 8;       bytes_per_mcu[1] = 4;       bytes_per_mcu[2] = 4;       break;……

//自己定义的写入yuv文件函数static void write_yuv2(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],1,width* height, F);    fwrite(components[1], 1, width* height/4, F);    fwrite(components[2], 1, width* height/4, F);}

2.将量化矩阵和Huffman表输出到txt文件

tinyjpeg.h

#define RTXT 1//add by hyw#define RTXTFILE "result_jpeg.txt"//add by hyw

tinyjpeg.c

输出huffman码表:

static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)    {      ......        #if TRACE         fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);         fflush(p_trace);        #endif        #if RTXT         fprintf(p_rtxt, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);         fflush(p_rtxt);        #endif       ......    }

输出量化表:

static void build_quantization_table(float *qtable, const unsigned char *ref_table){  /* Taken from libjpeg. Copyright Independent JPEG Group's LLM idct.   * For float AA&N IDCT method, divisors are equal to quantization   * coefficients scaled by scalefactor[row]*scalefactor[col], where   *   scalefactor[0] = 1   *   scalefactor[k] = cos(k*PI/16) * sqrt(2)    for k=1..7   * We apply a further scale factor of 8.   * What's actually stored is 1/divisor so that the inner loop can   * use a multiplication rather than a division.   */  int i, j;  static const double aanscalefactor[8] = {     1.0, 1.387039845, 1.306562965, 1.175875602,     1.0, 0.785694958, 0.541196100, 0.275899379  };   const unsigned char *zz = zigzag;  for (i=0; i<8; i++) {     for (j=0; j<8; j++) {
  #if RTXT     fprintf(p_rtxt, "%d ", ref_table[*zz]);//输出的量化矩阵为之字形扫描顺序     fflush(p_rtxt);  #endif
 *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j]; } } #if RTXT
     fprintf(p_rtxt, "\n");
     fflush(p_rtxt);
  #endif}

DQT码表头尾:

static int parse_DQT(struct jdec_private *priv, const unsigned char *stream){  int qi;  float *table;  const unsigned char *dqt_block_end;#if TRACE  fprintf(p_trace,"> DQT marker\n");  fflush(p_trace);#endif#if RTXT//add by hyw   fprintf(p_rtxt, "> DQT marker\n");   fflush(p_rtxt);#endif  dqt_block_end = stream + be16_to_cpu(stream);  stream += 2;/* Skip length */  while (stream < dqt_block_end)   {     qi = *stream++;#if SANITY_CHECK     if (qi>>4)       snprintf(error_string, sizeof(error_string),"16 bits quantization table is not supported\n");     if (qi>4)       snprintf(error_string, sizeof(error_string),"No more 4 quantization table is supported (got %d)\n", qi);#endif     table = priv->Q_tables[qi];     build_quantization_table(table, stream);     stream += 64;   }#if TRACE  fprintf(p_trace,"< DQT marker\n");  fflush(p_trace);#endif#if RTXT//add by hyw  fprintf(p_rtxt, "< DQT marker\n");  fflush(p_rtxt);#endif  return 0;}

3.输出DC图像和某一AC的图像:

tinyjpeg.h

    void DC(struct jdec_private *priv);//DC    void AC(struct jdec_private *priv);//AC    void outputDCAC(struct jdec_private *priv);//输出图像


tinyjpeg.c

 int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)    {    ..................................................      priv->DCImage = (int *)malloc(sizeof(int)*priv->width * priv->height/64);//edit by lee 2017 5 18      priv->ACImage = (int *)malloc(sizeof(int)*priv->width * priv->height/64);//edit by lee 2017 5 18      outputDCAC(priv);//by hyw      .......................................    }

DC

static void DCiamge(struct jdec_private *priv)    {        static long i = 0;//i为DCT块的个数        if (i<priv->height*priv->width / 64)//8x8块        {            priv->DCImage[i] = priv->component_infos[cY].DCT[0];//cY=0        }        i++;    }

AC

static void ACiamge(struct jdec_private *priv)    {        static long int i = 0;        if (i<priv->height*priv->width / 64)            priv->ACImage[i] = priv->component_infos[cY].DCT[2];//选取DCT[64]的第二个ac系数(取值范围(1,63))        i++;    }

输出图像

static void outputDCAC(struct jdec_private *priv)    {        int i = 0;        int dcmax, dcmin;//方便归一化        int acmax, acmin;//方便归一化        unsigned char *temp;//记录归一化后的值方便输出        /*设置初值*/        dcmin = priv->DCImage[0];        dcmax = priv->DCImage[0];        acmin = priv->ACImage[0];        acmax = priv->ACImage[0];        temp = (unsigned char*)malloc(priv->height*priv->width / 64);//找出最大最小值并临时存放值        for (i = 0; i < (priv->height*priv->width / 64); i++)        {            if (priv->DCImage[i] > dcmax)                dcmax = priv->DCImage[i];            if (priv->DCImage[i] < dcmin)                dcmin = priv->DCImage[i];            if (priv->ACImage[i] > acmax)                acmax = priv->ACImage[i];            if (priv->ACImage[i] < acmin)                acmin = priv->ACImage[i];        }//找最大最小值        for (i = 0; i < (priv->height*priv->width / 64); i++)        {            temp[i] = (unsigned char) 255*(priv->DCImage[i] - dcmin) / (dcmax - dcmin);        }        fwrite(temp, 1, priv->width*priv->height / 64, DFILE);        for (i = 0; i < (priv->height*priv->width / 64); i++)        {            temp[i] = (unsigned char)255*(priv->ACImage[i] - acmin)/ (acmax - acmin);        }        fwrite(temp, 1, priv->width*priv->height / 64, AFILE);        if (temp) free(temp);    }

四.实验结果