数据压缩实验五

来源:互联网 发布:软件删除system32 编辑:程序博客网 时间:2024/05/21 14:09

实验原理

jpeg编解码原理


jpeg文件格式


 

8*8块编码



零偏置

对于灰度级是2^n的像素,通过减去2^n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值。

目的:使像素的绝对值出现3位10进制的概率大大减少。

DCT变换


量化

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

因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值。根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化。如果原始图象中细节丰富,则去掉的数据较多,量化后的系数与量化前差别。反之,细节少的原始图象在压缩时去掉的数据少些。

ac游程编码

在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编码  对类内索引用定长码编码


ac系数的之字形扫描


DC差分编码

8×8图像块经过DCT变换之后得到的DC直流系数有两个特点:系数的数值比较大、相邻8×8图像块的DC系数值变化不大:冗余

根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值 DIFF进行编码:

DIFF=DCk- DCk-1



解码重构

与编码相反::

解码Huffman数据、解码DC差值、重构量化后的系数、DCT逆变换、丢弃填充的行/列、反0偏置、对丢失的CbCr分量差值(下采样的逆过程) YCbCr -》 RGB

实验步骤

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

2. 程序调试过程中,应做到:

理解程序设计的整体框架

理解三个结构体的设计目的

struct huffman_table

struct component

struct jdec_private

理解在视音频编解码调试中TRACE的目的和含义

会打开和关闭TRACE

会根据自己的要求修改TRACE

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

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

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

实验代码及分析

1、输出一个yuv文件

yuv输出的时候定义部分和yuv420,即分别输出y、u、v、文件的过程基本相同,照此进行修改

//LOADJPEG.C//将三个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], width, height, F);fwrite(components[1], width*height / 4, 1, F);fwrite(components[2], width*height / 4, 1, F);fclose(F);}
/* Save it */  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_yuv2(outfilename, width, height, components);break;   }
static void usage(void){    fprintf(stderr, "Usage: loadjpeg [options] <input_filename.jpeg> <format> <output_filename>\n");    fprintf(stderr, "options:\n");    fprintf(stderr, "  --benchmark - Convert 1000 times the same image\n");    fprintf(stderr, "format:\n");    fprintf(stderr, "  yuv420p - output 3 files .Y,.U,.V\n");    fprintf(stderr, "  rgb24   - output a .tga image\n");    fprintf(stderr, "  bgr24   - output a .tga image\n");    fprintf(stderr, "  gray    - output a .pgm image\n");fprintf(stderr, "  yuv - output a .yuv image\n");    exit(1);}
主函数中
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)  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];
//TINYJPEG.Henum tinyjpeg_fmt {   TINYJPEG_FMT_GREY = 1,   TINYJPEG_FMT_BGR24,   TINYJPEG_FMT_RGB24,   TINYJPEG_FMT_YUV420P,   TINYJPEG_FMT_YUV,};
函数中的内容和YUV420中的相同
//TINYJPEG.C  case TINYJPEG_FMT_YUV:  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;
2、结构体分析

component:存放MCU用来作为最小的数据块访问数据,其中Hfactor表示水平取样,Vfactor表示垂直

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};

jade_private表示是控制整个文件的变量,在另两个结构体的上层

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];  jmp_buf jump_state;  /* Internal Pointer use for colorspace conversion, do not modify it !!! */  uint8_t *plane[COMPONENTS];};

huffman_table:用来输出ac和dc系数的huffman表

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];};

3、trace

trace是用来输出文件中间信息的,想要关闭trace只需要在定义trace的时候也就是在tinyjpeg.h文件中另#define TRACE 0
static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)
{
  int chuck_len;
  int marker;
  int sos_marker_found = 0;
  int dht_marker_found = 0;
  const unsigned char *next_chunck;


  /* Parse marker */
  while (!sos_marker_found)
   {
     if (*stream++ != 0xff)
       goto bogus_jpeg_format;
     /* Skip any padding ff byte (this is normal) */
     while (*stream == 0xff)
       stream++;


     marker = *stream++;
     chuck_len = be16_to_cpu(stream);
     next_chunck = stream + chuck_len;
     switch (marker)
      {
       case SOF:
 if (parse_SOF(priv, stream) < 0)
   return -1;
 break;
       case DQT:
 if (parse_DQT(priv, stream) < 0)
   return -1;
 break;
       case SOS:
 if (parse_SOS(priv, stream) < 0)
   return -1;
 sos_marker_found = 1;
 break;
       case DHT:
 if (parse_DHT(priv, stream) < 0)
   return -1;
 dht_marker_found = 1;
 break;
       case DRI:
 if (parse_DRI(priv, stream) < 0)
   return -1;
 break;
       default:
#if TRACE
fprintf(p_trace,"> Unknown marker %2.2x\n", marker);
fflush(p_trace);
#endif
 break;
      }


     stream = next_chunck;
   }


  if (!dht_marker_found) {
#if TRACE
  fprintf(p_trace,"No Huffman table loaded, using the default one\n");
  fflush(p_trace);
#endif
    build_default_huffman_tables(priv);
  }


#ifdef SANITY_CHECK
  if (   (priv->component_infos[cY].Hfactor < priv->component_infos[cCb].Hfactor)
      || (priv->component_infos[cY].Hfactor < priv->component_infos[cCr].Hfactor))
    snprintf(error_string, sizeof(error_string),"Horizontal sampling factor for Y should be greater than horitontal sampling factor for Cb or Cr\n");
  if (   (priv->component_infos[cY].Vfactor < priv->component_infos[cCb].Vfactor)
      || (priv->component_infos[cY].Vfactor < priv->component_infos[cCr].Vfactor))
    snprintf(error_string, sizeof(error_string),"Vertical sampling factor for Y should be greater than vertical sampling factor for Cb or Cr\n");
  if (   (priv->component_infos[cCb].Hfactor!=1) 
      || (priv->component_infos[cCr].Hfactor!=1)
      || (priv->component_infos[cCb].Vfactor!=1)
      || (priv->component_infos[cCr].Vfactor!=1))
    snprintf(error_string, sizeof(error_string),"Sampling other than 1x1 for Cr and Cb is not supported");
#endif


  return 0;
bogus_jpeg_format:
#if TRACE
  fprintf(p_trace,"Bogus jpeg format\n");
  fflush(p_trace);
#endif
  return -1;
}

4、输出huffman码表

首先在tinyjpeg.h文件中定义一个控制huffman码表的变量:tables

#define TABLES 1

函数dht下,输出acdc类别

if (index & 0xf0 ) {#if TABLES         fprintf(AC_TABLE, "Huffman table AC[%d] length=%d\n", index & 0xf, count);         fflush(AC_TABLE);    #endifaqi=0;//控制输出的是ac还是dc       build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]); }     else {#if TABLES         fprintf(DC_TABLE, "Huffman table DC[%d] length=%d\n", index & 0xf, count);         fflush(DC_TABLE);    #endif aqi=1;       build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]); }

输出量化表

 for(i=0;i<64;i++){ if((!(i%8))) { fprintf(Q_table,"\n%f",table[i]); } else  { fprintf(Q_table," %f",table[i]); }} fprintf(Q_table,"\n");

输出码表

fprintf(AC_TABLE,"val=%2.2x code=%8.8x codesize=%2.2d\n",val,code,code_size);#if TABLESif(aqi==0){fflush(AC_TABLE);}else{
#endiffprintf(DC_TABLE,"val=%2.2x code=%8.8x codesize=%2.2d\n",val,code,code_size);fflush(AC_TABLE);}


4、输出ac、dc图像

在tinyjpeg.h文件中添加FILE *AC_table和*DC_table的定义在主函数中开辟空间
char temp[1024];snprintf(temp, 1024, "%sDC.Y", output_filename);
DC_FILE = fopen(temp, "wb");
snprintf(temp, 1024, "%sAC.Y", output_filename);
AC_FILE = fopen(temp, "wb");

//tinyjpeg.c文件 dc分量需要被压缩,写文件的时候是8*8的块所以输出文件大小变为128*128,ac的值比较小,所以加128之后再输出DC[0]=(priv->component_infos->DCT[0]+512.0)/4;DCimage[0]=(unsigned char)(DC[0]+0.5);
fwrite(DCimage,1,1,DC_FILE);ACimage[0]=(unsigned char)(priv->component_infos->DCT[1]+128);fwrite(ACimage,1,1,AC_FILE);

实验结果











 



原创粉丝点击