实验五-jpg解码

一、实验原理
1.jpg编码
JPEG(Joint Photographic Experts Group),文件后缀名为.jpg,.jpeg,是一种常用的图像文件格式。采用有损压缩方式去除冗余的图像和彩色数据,在获得极高压缩率的同时能展现十分丰富生动的图像。
编码过程如图示:
实验五-jpg解码_第1张图片
解码为编码的逆过程。
2.jpg文件格式
jpeg文件以Segment的形式组织,均以0xFF开始,后面跟1字节的Marker和2字节的Segment Length(包含Length本身长度,不包括Marker和0xFF)。
常用的Marker有:
1)SOI(start of image)0xFFDB
所有的jpeg文件必须以SOI开始。
2)APPn(reserved for application use)0xFFE0-0xFFEF
eg:FFE0 运用程序保留标记0
FF E0 00 10 4A 46 49 46 00 01 01 01 00 48 00 48 00 00
length 2byte:0X0010=16
标识符:4A 46 49 46 00 –JFIF
Version 2byte:0101
Units 1byte:01 –X,Y are dots per inch
Xdensity 2byte:0x0048 –水平方向点密度
Ydensity 2byte:0x0048 –垂直方向点密度
缩略图水平像素数目 1byte :0x00
缩略图垂直像素数目 1byte :0x00
缩略图24bitRGB 数据:没有缩略图
3)DQT(Define Quantization Table) 0xFFDB
实验五-jpg解码_第2张图片
length 2byte:0x 00 43
QT信息 1byte 0-3位:QT号(0-3,可能有4张量化表,亮度,色度,可能会有rgb各一张量化表) 4-7位:QT精度(0:8bit,1:16bit)
QT实际数据:nbyte n=64*QT精度的字节数。(对于8*8的宏块来说,共有8*8=64个量化系数)
需要注意的是,jpeg文件中的量化表是按之字形扫描的格式存的,要得到真正的量化表还需要反之字形扫描;

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
};
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++) {
       *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
     }
   }

}

解析到的量化表为
实验五-jpg解码_第3张图片
实验五-jpg解码_第4张图片
4)SOF(start of Frame)0xFFC0
eg:FF C0 00 11 08 0F C0 0B D0 03 01 22 00 02 11 01 03 11 01
length 2byte :0x11—17byte
图像精度(每个数据样本的位数)1byte:0x08
图像高度 2byte:0x0FC0=4032
图像宽度 2byte : 0X0BD0=3024
颜色分量数 1byte:03(ycrcb)
颜色分量id 1byte:01—-y
采样率 1byte :4-7位水平采样率:2 0-3位垂直采样率:2
量化表号 1byte:00
颜色分量id 1byte:02—-U
采样率 1byte :位水平采样率:1 垂直采样率:1
量化表号 1byte:01
颜色分量id 1byte:03—–V
采样率 1byte :水平采样率:1 垂直采样率:1
量化表号 1byte:01
5)DHT,define Huffman Table 0xFFC4
实验五-jpg解码_第5张图片
这里写图片描述
length 2byte:0x001f—31byte
huffman表信息 1byte:4-7位huffman表类型(0:Dc,1:Ac):0
0-3位huffman表id:0
huffmanTableIndex:16byte 1-16位码长的码字各有多少个
huffman权值信息:nbyte(n为huffmanTableIndex的和,表示每个码字的权值)
实验五-jpg解码_第6张图片
6)SOS(start of Scan)0xFFDA
eg:FF DA 00 0C 03 01 00 02 11 03 11 03 3F 00
length 2byte:0x000c—12
颜色分量数 1byte:0x03 –和SOF中的颜色分量数相同
颜色分量信息 6byte:每个颜色分量占2byte
颜色分量id 1byte:0x01
颜色分量使用的huffman编号:0-3位直流分量使用的表号;4-7位交流分量使用的表号
y: 01 dc–0 ac–0
cb:02 dc–1 ac–1
cr: 03 dc–1 ac–1
压缩图像数据 3byte:
谱选择开始 1byte 固定值0x00
谱选择结束 1byte 固定值0x3f
谱选择 1byte 在基本jpeg中总为00
7)EOI end of image 0xFFD9
结束符,jpeg文件中固定为EOI。
3.解码数据结构

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

  //add by zhn
  int tempY[4];
  int tempU,tempV;
  //end add

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

4.typedef指向函数的用法
typedef void (*decode_MCU_fct) (struct jdec_private *priv);
decode_MCU_fct decode_MCU;
decode_MCU=decode_MCU_2x2_1plane;
decode_MCU(priv);
decode_MCU_fct是一个函数指针,用它来声明的decode_MCU也是一个函数指针,并用它指向decode_MCU_2x2_1plane函数,传入参数即可调用。
二、实验步骤
1)读取文件;
2)解析Segment Marker;
3)根据每个分量的水平和垂直采样因子计算MCU的大小,得到每个MCU中宏块的个数;
4)对每个MCU解码,知道解析到EOI,解码结束;
5)将y,cb,cr转化为需要的彩色空间并保存。
三、关键代码分析

int convert_one_image(const char *infilename, const char *outfilename, int output_format)
{
  FILE *fp;
  unsigned int length_of_file;
  unsigned int width, height;
  unsigned char *buf;
  struct jdec_private *jdec;
  unsigned char *components[3];
  //add by zhn
  unsigned char *yBuffer;
  unsigned char *uBuffer;
  unsigned char *vBuffer;
  //end add

  /* Load the Jpeg into memory */
  fp = fopen(infilename, "rb");
  if (fp == NULL)
    exitmessage("Cannot open filename\n");
  length_of_file = filesize(fp);
  buf = (unsigned char *)malloc(length_of_file + 4);
  if (buf == NULL)
    exitmessage("Not enough memory for loading file\n");
  fread(buf, length_of_file, 1, fp);
  fclose(fp);

  /* Decompress it */
  jdec = tinyjpeg_init();
  if (jdec == NULL)
    exitmessage("Not enough memory to alloc the structure need for decompressing\n");

  if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)
    exitmessage(tinyjpeg_get_errorstring(jdec));

  /* Get the size of the image */
  tinyjpeg_get_size(jdec, &width, &height);

  snprintf(error_string, sizeof(error_string),"Decoding JPEG image...\n");
  if (tinyjpeg_decode(jdec, output_format) < 0)
    exitmessage(tinyjpeg_get_errorstring(jdec));

  /* 
   * Get address for each plane (not only max 3 planes is supported), and
   * depending of the output mode, only some components will be filled 
   * RGB: 1 plane, YUV420P: 3 planes, GREY: 1 plane
   */
  tinyjpeg_get_components(jdec, components);

  /* 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_onefile(outfilename, width, height, components);
      break;
    case TINYJPEG_FMT_GREY:
      write_pgm(outfilename, width, height, components);
      break;
   }

  /* Only called this if the buffers were allocated by tinyjpeg_decode() */
  tinyjpeg_free(jdec);
  /* else called just free(jdec); */

  free(buf);
  return 0;
}
int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)//add by zhn outdctY
{
  unsigned int x, y, xstride_by_mcu, ystride_by_mcu;
  unsigned int bytes_per_blocklines[3], bytes_per_mcu[3];
  decode_MCU_fct decode_MCU;
  const decode_MCU_fct *decode_mcu_table;
  const convert_colorspace_fct *colorspace_array_conv;
  convert_colorspace_fct convert_to_pixfmt;
  //add by zhn
  unsigned char *yBuffer;
  unsigned char *uBuffer;
  unsigned char *vBuffer;
  int *yB,*uB,*vB;
  int *ytemp,*utemp,*vtemp;
  int dcSize,i,j;
  FILE *O,*D;
  int debug=1;
  int count[256],countBefore[256];
  int Q_table[3][64];
  int MaxY,MaxU,MaxV;
  memset(count,0,256*sizeof(int));
  memset(countBefore,0,256*sizeof(int));
  //end add
  if (setjmp(priv->jump_state))
    return -1;

  /* To keep gcc happy initialize some array */
  bytes_per_mcu[1] = 0;
  bytes_per_mcu[2] = 0;
  bytes_per_blocklines[1] = 0;
  bytes_per_blocklines[2] = 0;

  decode_mcu_table = decode_mcu_3comp_table;
  switch (pixfmt) {
     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;

     case TINYJPEG_FMT_RGB24:
       colorspace_array_conv = convert_colorspace_rgb24;
       if (priv->components[0] == NULL)
     priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);
       bytes_per_blocklines[0] = priv->width * 3;
       bytes_per_mcu[0] = 3*8;
       break;

     case TINYJPEG_FMT_BGR24:
       colorspace_array_conv = convert_colorspace_bgr24;
       if (priv->components[0] == NULL)
     priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);
       bytes_per_blocklines[0] = priv->width * 3;
       bytes_per_mcu[0] = 3*8;
       break;

     case TINYJPEG_FMT_GREY:
       decode_mcu_table = decode_mcu_1comp_table;
       colorspace_array_conv = convert_colorspace_grey;
       if (priv->components[0] == NULL)
     priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);
       bytes_per_blocklines[0] = priv->width;
       bytes_per_mcu[0] = 8;
       break;

     default:
#if TRACE
         fprintf(p_trace,"Bad pixel format\n");
         fflush(p_trace);
#endif
       return -1;
  }

四、实验结果分析

static void write_yuv_onefile(const char *filename, int width, int height, unsigned char **components)
{
  FILE *F;

  F = fopen(filename, "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);
}
/*
 * Decode a 2x2
 *  .-------.
 *  | 1 | 2 |
 *  |---+---|
 *  | 3 | 4 |
 *  `-------'
 */
static void decode_MCU_2x2_3planes(struct jdec_private *priv)
{
    int i;
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 16);
  //add by zhn
  priv->tempY[0]=priv->component_infos[cY].DCT[4];
  //end add
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+8, 16);
  //add by zhn
  priv->tempY[1]=priv->component_infos[cY].DCT[4];
#if debug_dct
  for(i=0;i<64;i++)
  {
      if(priv->component_infos[cY].DCT[i]>=255)
          printf("%4d",priv->component_infos[cY].DCT[i]);
  }
#endif
  //end add
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64*2, 16);
  //add by zhn
  priv->tempY[2]=priv->component_infos[cY].DCT[4];
  //end add
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64*2+8, 16);
  //add by zhn
  priv->tempY[3]=priv->component_infos[cY].DCT[4];
  //end add

  // Cb
  process_Huffman_data_unit(priv, cCb);
  IDCT(&priv->component_infos[cCb], priv->Cb, 8);
  //add by zhn
#if debug_dct
  for(i=0;i<64;i++)
  {
      if(priv->component_infos[cCb].DCT[i]>=255)
          printf("%4d",priv->component_infos[cCb].DCT[i]);
  }
#endif
  priv->tempU=priv->component_infos[cCb].DCT[4];
  //end add

  // Cr
  process_Huffman_data_unit(priv, cCr);
  IDCT(&priv->component_infos[cCr], priv->Cr, 8);
  //add by zhn
#if debug_dct
  for(i=0;i<64;i++)
  {
      if(priv->component_infos[cCr].DCT[i]>=255)
          printf("%4d",priv->component_infos[cCr].DCT[i]);
  }
#endif
  priv->tempV=priv->component_infos[cCr].DCT[4];
  //end add
}

原始图像
实验五-jpg解码_第7张图片

dc系数图像
实验五-jpg解码_第8张图片
dc概率分布
实验五-jpg解码_第9张图片
ac系数1的概率分布
实验五-jpg解码_第10张图片
ac系数3的概率分布
实验五-jpg解码_第11张图片
五、实验结论分析
dct的物理意义是将用各频率分量的组合来表示原图像,它的各个系数就表示该频率分量的大小。一张图片的dc系数与原图像的概率分布近似,而ac系数则服从拉普拉斯分布,且频率越高,分布越集中。
附录代码链接
http://download.csdn.net/detail/qq_35473884/9857990

你可能感兴趣的:(数据压缩)