實驗五-jpg解碼

一、實驗原理
1.jpg編碼
JPEG(Joint Photographic Experts Group),文件後綴名爲.jpg,.jpeg,是一種常用的圖像文件格式。採用有損壓縮方式去除冗餘的圖像和彩色數據,在獲得極高壓縮率的同時能展現十分豐富生動的圖像。
編碼過程如圖示:
這裏寫圖片描述
解碼爲編碼的逆過程。
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
這裏寫圖片描述
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];
     }
   }

}

解析到的量化表爲
這裏寫圖片描述
這裏寫圖片描述
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
這裏寫圖片描述
這裏寫圖片描述
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的和,表示每個碼字的權值)
這裏寫圖片描述
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
}

原始圖像
這裏寫圖片描述

dc係數圖像
這裏寫圖片描述
dc概率分佈
這裏寫圖片描述
ac係數1的概率分佈
這裏寫圖片描述
ac係數3的概率分佈
這裏寫圖片描述
五、實驗結論分析
dct的物理意義是將用各頻率分量的組合來表示原圖像,它的各個係數就表示該頻率分量的大小。一張圖片的dc係數與原圖像的概率分佈近似,而ac係數則服從拉普拉斯分佈,且頻率越高,分佈越集中。
附錄代碼鏈接
http://download.csdn.net/detail/qq_35473884/9857990

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章