數據壓縮實驗五:JPEG文件解碼實驗分析

一:實驗原理

1.JPEG編碼原理

JPEG 是Joint Photographic Experts Group(聯合圖像專家小組)的縮寫,是第一個國際圖像壓縮標準。
.jpeg/.jpg是最常用的圖像文件格式,是一種有損壓縮格式。JPEG編碼框圖如下圖所示:

(1)零偏置level offset

對於灰度級是2n的像素,通過減去2n-1,將無符號的整數值變成有符號數;
對於n=8,即將0~255的值域,通過減去128,轉換爲值域在-128~127之間的值。這樣做的目的是: 使像素的絕對值出現3位10進制的概率大大減少。

(2)8x8 DCT變換

DCT變換是指對每個單獨的彩色圖像分量,把整個分量圖像分成8×8的圖像塊,再以8x8的圖像塊爲一個單位進行量化和編碼處理。我們可以利用DCT變換去相關的特性,去除冗餘信息,提高編碼效率。

(3)量化

我們可以通過量化減少數據的編碼位數,提高編碼效率;
因爲人眼對亮度信號比對色差信號更敏感,因此使用了兩種量化表:亮度量化值和色差量化值;   
根據人眼的視覺特性(對低頻敏感,對高頻不太敏感)對低頻分量採取較細的量化,對高頻分量採取較粗的量化

(4)DC係數差分編碼

8×8圖像塊經過DCT變換之後得到的DC直流係數有兩個特點:係數的數值比較大和相鄰8×8圖像塊的DC係數值變化不大:冗餘;
根據這個特點, JPEG算法使用了差分脈衝調製編碼(DPCM)技術,對相鄰圖像塊之間量化DC係數的差值DIFF進行編碼:
對DIFF進行Huffman編碼。

5AC係數的之字形掃描

由於經DCT變換後,係數大多數集中在左上角,即低頻分量區,因此採用Z字形按頻率的高低順序讀出,可以出現很多連零的機會。可以使用遊程編碼。尤其在最後,如果都是零,給出EOB (End of Block)即可。zigzag掃描如下圖:

(6)AC係數遊程編碼

在經過之字形掃描排序後的AC係數,存在很多連0。爲了進一步提高編碼效率,因此對AC係數進行遊程編碼(RLC)處理之後,再進一步進行Huffman編碼。

(7)將進行處理之後的AC和DC係數,送到Huffman編碼器分別進行Huffman編碼

JPEG中共採用了四張Huffman碼錶:亮度DC、亮度AC、色度DC、色度AC,即分別對圖像的亮度和色度,直流和交流數據進行編碼處理。


2.JPEG文件格式分析

(1)segment的組織形式

EG 在文件中以 Segment 的形式組織,它具有以下特點:
均以 0xFF 開始,後跟 1 byte 的 Marker 和 2 byte 的 Segment length(包含表示Length 本身所佔用的 2 byte,不含“ 0xFF” + “Marker” 所佔用的 2 byte);
採用 Motorola 序(相對於 Intel 序),即保存時高位在前,低位在後;
Data 部分中, 0xFF 後若爲 0x00,則跳過此字節不予處理;

(2)文件格式分析

下面,我們通過對一個具體的JPEG文件格式的分析,來了解相關marker:


FFD8:SOI, Start of Image,圖像開始 
所有的JPEG文件都必須以SOI開始。


FFE0:Application,應用程序保留標記 0
length: 16 byte (2 byte)----00 10
標識符: JFIF (5 byte)----4A 46 49 46 00
Version: 0101 (2 byte)----01 01
Units: 01 (1 byte) X and Y are dots per inch----01
Xdensity: 96 (2 bytes) Horizontal pixel density(水平方向點密度)----00 60
Ydensity:96 (2 bytes) Vertical pixel density(垂直方向點密度)---- 00 60
縮略圖水平像素數目: 00 (1 byte)----00
縮略圖垂直像素數目: 00 (1 byte)----00
縮略圖 24bitRGB 點數目: 縮略圖水平像素數目 * 縮略圖垂直像素數目 = 00


FFDB:DQT, Define Quantization Table,定義量化表
length: 67 byte (2 byte)----00 43
QT information - precision: 00 (Higher 4 bit) (8 bit)
QT information - index: 00 (Lower 4 bit)----00
qt_table:從08一直到下一個FFDB之前,爲量化表的64個量化值

下一個FFDB定義了第二個量化表,這裏不再贅述。

FFC0: SOF0 , Start of Frame, 基線離散餘弦變換
length: 17 byte (2 byte)----00 11
圖像精度(每個數據樣本的位數) : 8----08
Image Height: 900 (2 byte)----03 84
Image Width: 1440 (2 byte)----05 A0
顏色分量數: 03( YCrCb) (1 byte)----03
顏色分量 ID: 01 (1 byte) (Y)----01
SampRate_Y_H: 10 (Higher 4 bit)
SampRate_Y_V: 10 (Lower 4 bit)----22
Y QtTableID: 00 (1 byte)----00
顏色分量 ID: 02 (1 byte) (U)----02
SampRate_U_H: 01 (Higher 4 bit)
SampRate_U_V: 01 (Lower 4 bit)----11
U QtTableID: 01 (1 byte)----01
顏色分量 ID: 03 (1 byte) (V)----03
SampRate_V_H: 01 (Higher 4 bit)
SampRate_V_V: 01 (Lower 4 bit)----11
V QtTableID: 01 (1 byte)----01


FFC4: DHT, Define Huffman Table,定義 Huffman 樹表
length: 31byte (2 byte)----00 1F
Huffman 表類型: 0 (Higher 4 bit) (DC)
Huffman 表 ID: 0 (Lower 4 bit) (0 號表)----00
HuffmanTableIndex: 0
code_len_table:16bytes
剩下的是各碼字對應的權值。
後面的三個FFC4分別爲AC0號表,DC1號表,AC1號表,這裏不再贅述。

FFDA: SOS, Start of Scan,掃描開始
length: 12 byte (2 byte)----00 0C
顏色分量數:3(1byte)----03
顏色分量 ID: 1 (1 byte) (Y)----01
Y Dc HuffmanTreeIndex: 0 (Higher 4 bit)
Y Ac HuffmanTreeIndex: 0(Lower 4 bit)----00
顏色分量 ID: 2 (1 byte) (U or V)----02
UV Dc HuffmanTreeIndex: 1 (Higher 4 bit)
UV Ac HuffmanTreeIndex: 1 (Lower 4 bit)----11
顏色分量 ID: 3 (1 byte) (U or V)----03
UV Dc HuffmanTreeIndex: 1 (Higher 4 bit)
UV Ac HuffmanTreeIndex: 1(Lower 4 bit)----11
譜選擇開始 1 byte---- 固定值 0x00
譜選擇結束 1 byte---- 固定值 0x3F
譜選擇 1 byte---- 在基本 JPEG 中總爲 00


二:JPEG解碼流程

JPEG解碼流程爲編碼的逆過程,具體流程如下所示:
1 .讀取文件

2. 解析 Segment Marker
2.1 解析 SOI
2.2 解析 APP0
 檢查標識“ JFIF”及版本
 得到一些參數
2.3 解析 DQT
 得到量化表長度(可能包含多張量化表)
 得到量化表的精度
 得到及檢查量化表的序號(只能是 0 —— 3)
 得到量化表內容( 64 個數據)
2.4 解析 SOF0
 得到每個 sample 的比特數、長寬、顏色分量數
 得到每個顏色分量的 ID、水平採樣因子、垂直採樣因子、使用的量化表序號(與 DQT 中序號對應)
2.5 解析 DHT
 得到 Huffman 表的類型( AC、 DC)、序號
 依據數據重建 Huffman 表
2.6 解析 SOS
 得到解析每個顏色分量的 DC、 AC 值所使用的 Huffman 表序號(與 DHT中序號對應)

3.依據每個分量的水平、垂直採樣因子計算 MCU 的大小,並得到每個 MCU 中 8*8宏塊的個數

4 .對每個 MCU 解碼(依照各分量水平、垂直採樣因子對 MCU 中每個分量宏塊解碼)
4.1 對每個宏塊進行 Huffman 解碼,得到 DCT 係數
4.2 對每個宏塊的 DCT 係數進行 IDCT,得到 Y、 Cb、 Cr
4.3 遇到 Segment Marker RST 時,清空之前的 DC DCT 係數

5 .解析到 EOI,解碼結束

6.將 Y、 Cb、 Cr 轉化爲需要的色彩空間並保存。

三:關鍵代碼分析

JPEG解碼程序工程文件目錄如下:

該JPEG解碼系統,是按照分層設計的思想組織。因此這裏我們按照調試的順序,通過數據流的走向對整個系統的各個模塊進行分析。因此爲了更好地描述整個系統,我們在tinyjpeg_internal文件中定義了三個結構體:

struct huffman_table(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];
};


struct component (8*8宏塊結構體)

struct component 

{
  unsigned int Hfactor;//水平採樣因子
  unsigned int Vfactor;//垂直採樣因子
  float *Q_table; //指向該宏塊使用的量化表
  struct huffman_table *AC_table;//指向該宏塊直流係數的Huffman碼錶
  struct huffman_table *DC_table;//指向該宏塊交流係數的Huffman碼錶
  short int previous_DC; /* Previous DC coefficient *///前一個塊的DC係數
  short int DCT[64]; /* DCT coef *///該塊的DCT係數,其中DCT[0]爲該塊直流,其他爲交流
#if SANITY_CHECK
  unsigned int cid;
#endif
};

struct jdec_private(文件解碼信息結構體)

struct jdec_private(文件解碼信息結構體)

{
  /* Public variables */
  uint8_t *components[COMPONENTS];//分別指向YUV分量結構體的指針數組
  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]; //對YUV進行量化的量化表
  struct huffman_table HTDC[HUFFMAN_TABLES]; //DC係數編碼的Huffman碼錶
  struct huffman_table HTAC[HUFFMAN_TABLES]; //AC係數編碼的Huffman碼錶
  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];//反DCT之後存三個分量的數組
 
  jmp_buf jump_state;
  /* Internal Pointer use for colorspace conversion, do not modify it !!! */
  uint8_t *plane[COMPONENTS];
 
};


1.讀取文件

在命令行中,我們設置了輸入的JPEG文件,輸出的文件和輸出格式:
主函數main函數中,我們打開對輸入輸出文件,並解析了輸出格式:
int main(int argc, char *argv[])
{
  int output_format = TINYJPEG_FMT_YUV420P;//將輸出格式初始化爲yuv420P
  char *output_filename, *input_filename;//定義輸入文件和輸出文件指針
  clock_t start_time, finish_time;
  unsigned int duration;
  int current_argument;
  int benchmark_mode = 0;

#if TRACE//TRACE=1,則中間代碼會編譯,TRACE=0,則會忽略
  p_trace=fopen(TRACEFILE,"w");
  if (p_trace==NULL)
  {
	  printf("trace file open error!");
  }
#endif
  if (argc < 3)
    usage();

  current_argument = 1;
  while (1)
   {
     if (strcmp(argv[current_argument], "--benchmark")==0)//字符比較,若輸入了基準模式benchmark,則加1
       benchmark_mode = 1;
     else
       break;//否則跳出
     current_argument++;
   }

  if (argc < current_argument+2)
    usage();

  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;//確認文件輸出格式
  /*add by yangyulan*/
  else if (strcmp(argv[current_argument+1],"yuvone")==0)
    output_format =TINYJPEG_FMT_YUV420one;
  /*end by yangyulan*/
  else
    exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey,yuvone\n");
  output_filename = argv[current_argument+2];//輸出文件指針指向第三個文件
其中通過在系統各處添加trace,通過初始化trace的值,可以在需要的時候輸出一些中間結果。

根據數據走向,整個JPEG解碼過程,都是由下面的covert_one函數實現:
 if (benchmark_mode)
    load_multiple_times(input_filename, output_filename, output_format);
  else
    convert_one_image(input_filename, output_filename, output_format);//調用convert函數
在convert_one_image函數中,我們進行了以下解碼處理。
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];//定義三個字符數組

  /* 把文件中的數據讀如緩存中*/
  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);//將文件裏面的jpg數據讀到buf中
  fclose(fp);//關閉文件指針

  /* 解壓縮*/
  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)//tinyjpeg_parse_header函數在tinyjpeg.c中
    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);

  /* 按所要求的個數輸出文件*/
  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;
   }

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

  free(buf);
  return 0;
}

任務一:將輸出文件保存爲可供YUVViewer觀看的YUV文件。

1.在命令行將輸出格式設爲要求的格式(yuv),格式名稱自定義。

2.在main函數中,條件判斷屬於哪一格式輸出。
 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;//確認文件輸出格式
3.在convert_one_image函數中,進行格式判斷,並調用相應的輸出函數。
  /* 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;
   }
4.這裏輸出爲yuv,調用write_yuv函數
static void write_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, "ab");
 fwrite(components[0], width, height, F);
 fclose(F);
 snprintf(temp, 1024, "%s.yuv", filename);
 F = fopen(temp, "ab"); 
 fwrite(components[1], width*height/4, 1, F);
 fclose(F);
 snprintf(temp, 1024, "%s.yuv", filename);
 F = fopen(temp, "ab");
 fwrite(components[2], width*height/4, 1, F);
 fclose(F);
 printf("ok");
}


2.解析 Segment Marker(tinyjpeg_parse_header中)

tinyjpeg_parse_header函數:

int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size)
{
  int ret;

  /* Identify the file */
  if ((buf[0] != 0xFF) || (buf[1] != SOI))//文件開頭是0xFF,D8即SOI文件開始標誌,開始不是FFD8則報錯
    snprintf(error_string, sizeof(error_string),"Not a JPG file ?\n");

  priv->stream_begin = buf+2;//沒錯則將文件流的開始向後移兩個字節
  priv->stream_length = size-2;//將剩餘長度也減兩個字節
  priv->stream_end = priv->stream_begin + priv->stream_length;//定位到文件最後

  ret = parse_JFIF(priv, priv->stream_begin);

  return ret;
}

其中的parse_JFIF函數的作用便是解析文件頭:

static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)//解析JFIF
{
  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++;//E0賦值給marker,E0==APP0
     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;
}
parse SOI:
 if ((buf[0] != 0xFF) || (buf[1] != SOI))//文件開頭是0xFF,D8即SOI文件開始標誌,開始不		是FFD8則報錯
    snprintf(error_string, sizeof(error_string),"Not a JPG file ?\n");

parse_DQT函數:定義量化表

得到量化表長度(可能包含多張量化表)
得到量化表的精度
得到及檢查量化表的序號(只能是 0 —— 3)
得到量化表內容( 64 個數據)
//解析量化表
static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
  int qi;
  /*add by yangyulan*/

  /*end by yangyulan*/
  float *table;//定義了用於指向量化表的指針
  const unsigned char *dqt_block_end;//指向量化表的結束地址
#if TRACE
  fprintf(p_trace,"> DQT marker\n");
  fflush(p_trace);
#endif
  dqt_block_end = stream + be16_to_cpu(stream);//量化塊結束的位置
  stream += 2;	/* Skip length */  //跳過兩字節的存儲長度,如00 43

  while (stream < dqt_block_end)//當還在表內
   {
     qi = *stream++;//將量化表中的值逐個賦給qi
#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;//指向下一塊
   }

build_quantization_table函數:

static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
  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;//zigzag爲之字形掃描順序係數
  for (i=0; i<8; i++) {
     for (j=0; j<8; j++) {
       *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
     }
   }
}

Zigzag數組:

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

任務二:輸出量化表到TXT文件:

在解析量化表之後,我們可以中間輸出量化表到txt文件中:即在parse_DQT函數的後面添加如下代碼:
/*add by yangyulan*/
  /*for--print DQT into qfile.txt*/
  qfile=fopen("q_file.txt","ab");
  fputs("量化表",qfile);
  fputc(10,qfile);
  for( i=0;i<8;i++)
  {
	  for(j=0;j<8;j++)
	  {
			fprintf(qfile,"%f",*table);
			table++;
	  }
	  fputc(10,qfile);
	  
  }
  
  fclose(qfile);
  /*end by yangyulan*/



parse_SOF函數:

得到每個 sample 的比特數、長寬、顏色分量數;得到每個顏色分量的 ID、水平採樣因子、垂直採樣因子、使用的量化表序號(與 DQT 中序號對應)。

static int parse_SOF(struct jdec_private *priv, const unsigned char *stream)//基線餘弦變換
{
  int i, width, height, nr_components, cid, sampling_factor;
  int Q_table;
  struct component *c;
#if TRACE
  fprintf(p_trace,"> SOF marker\n");
  fflush(p_trace);
#endif
  print_SOF(stream);//打印SOF,即獲得圖像寬高和圖像精度,並打印出來

  height = be16_to_cpu(stream+3);//獲得圖像高度
  width  = be16_to_cpu(stream+5);//獲得圖像寬度
  nr_components = stream[7];//獲得圖像精度
#if SANITY_CHECK
  if (stream[2] != 8)
    snprintf(error_string, sizeof(error_string),"Precision other than 8 is not supported\n");
  if (width>JPEG_MAX_WIDTH || height>JPEG_MAX_HEIGHT)
    snprintf(error_string, sizeof(error_string),"Width and Height (%dx%d) seems suspicious\n", width, height);
  if (nr_components != 3)
    snprintf(error_string, sizeof(error_string),"We only support YUV images\n");
  if (height%16)
    snprintf(error_string, sizeof(error_string),"Height need to be a multiple of 16 (current height is %d)\n", height);
  if (width%16)
    snprintf(error_string, sizeof(error_string),"Width need to be a multiple of 16 (current Width is %d)\n", width);
#endif
  stream += 8;//分別解析YUV分量
  for (i=0; i<nr_components; i++) {
     cid = *stream++;//該分量ID
     sampling_factor = *stream++;//該分量的採樣率
     Q_table = *stream++;//該分量的量化表
     c = &priv->component_infos[i];//指向該分量的結構體指針
#if SANITY_CHECK
     c->cid = cid;
     if (Q_table >= COMPONENTS)
       snprintf(error_string, sizeof(error_string),"Bad Quantization table index (got %d, max allowed %d)\n", Q_table, COMPONENTS-1);
#endif
     c->Vfactor = sampling_factor&0xf;//該分量的垂直採樣率
     c->Hfactor = sampling_factor>>4;//水平採樣率
     c->Q_table = priv->Q_tables[Q_table];//該分量使用的量化表
#if TRACE
     fprintf(p_trace,"Component:%d  factor:%dx%d  Quantization table:%d\n",
           cid, c->Hfactor, c->Hfactor, Q_table );
	 fflush(p_trace);
#endif

  }
  priv->width = width;//寬高的信息
  priv->height = height;
#if TRACE
  fprintf(p_trace,"< SOF marker\n");
  fflush(p_trace);
#endif

  return 0;
}


解析DHT的函數 parse_DHT:得到 Huffman 表的類型( AC、 DC)、序號

static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)//解析Huffman碼錶
{
  unsigned int count, i;
  unsigned char huff_bits[17];//碼長從1到16的數目數組
  int length, index;
  /*add by yangyulan*/
  FILE *hufftable;
  hufftable=fopen("huffmantable_file.txt","ab"); 
  /*end by yangyulan*/

  length = be16_to_cpu(stream) - 2;//得到碼長(可能包含多張表)
  stream += 2;	/* Skip length */
#if TRACE
  fprintf(p_trace,"> DHT marker (length=%d)\n", length);
  fflush(p_trace);
#endif

  while (length>0) {//如果碼長大於0
     index = *stream++;//把該塊賦值給index

     /* We need to calculate the number of bytes 'vals' will takes */
     huff_bits[0] = 0;//碼長爲0的爲0個,下標與碼長相對應
     count = 0;//總碼字數
     for (i=1; i<17; i++) {
	huff_bits[i] = *stream++;//各碼長的個數分別賦值
	count += huff_bits[i];//總的碼字數
     }
#if SANITY_CHECK
     if (count >= HUFFMAN_BITS_SIZE)
       snprintf(error_string, sizeof(error_string),"No more than %d bytes is allowed to describe a huffman table", HUFFMAN_BITS_SIZE);
     if ( (index &0xf) >= HUFFMAN_TABLES)
       snprintf(error_string, sizeof(error_string),"No more than %d Huffman tables is supported (got %d)\n", HUFFMAN_TABLES, index&0xf);
#if TRACE
     fprintf(p_trace,"Huffman table %s[%d] length=%d\n", (index&0xf0)?"AC":"DC", index&0xf, count);
	 fflush(p_trace);
	 /*add by yangyulan*/
	 fprintf(hufftable,"Huffman table %s[%d] length=%d\n", (index&0xf0)?"AC":"DC", index&0xf, count);
	 fflush(hufftable);
	 /*end by yangyulan*/
	 
#endif
#endif

     if (index & 0xf0 )//高位爲1則爲AC表
	 { 	
		 build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);
	 }
      
     else//否則爲DC表
	 {
		 build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);

	 }
      
     length -= 1;
     length -= 16;
     length -= count;
     stream += count;
  }
#if TRACE
  fprintf(p_trace,"< DHT marker\n");
  fflush(p_trace);
#endif
  return 0;
}

build_huffman_table:依據數據重建 Huffman 表 

static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)//創建碼錶
{
  unsigned int i, j, code, code_size, val, nbits;
  unsigned char huffsize[HUFFMAN_BITS_SIZE+1], *hz;
  unsigned int huffcode[HUFFMAN_BITS_SIZE+1], *hc;
  int next_free_entry;
  /*add by yangyulan*/
	FILE *hufftable;
	 hufftable=fopen("huffmantable_file.txt","ab"); 
  /*end by yangyulan*/

  /*
   * Build a temp array 
   *   huffsize[X] => numbers of bits to write vals[X]
   */
  hz = huffsize;
  for (i=1; i<=16; i++)//碼長爲1~16
   {
     for (j=1; j<=bits[i]; j++)//碼長爲1~16的個數
       *hz++ = i;//第1~bits[1]的碼長都爲1...
   }
  *hz = 0;//最後碼長賦爲0
 
  memset(table->lookup, 0xff, sizeof(table->lookup));
  for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++)
    table->slowtable[i][0] = 0;//都初始化爲0

  /* Build a temp array
   *   huffcode[X] => code used to write vals[X]
   */
  code = 0;
  hc = huffcode;//指向碼字
  hz = huffsize;//重新指向
  nbits = *hz;//從第一個開始,碼長賦值
  while (*hz)//碼長大於0 時
   {
     while (*hz == nbits)//碼長未改變時
      {
	*hc++ = code++;//碼字加1
	hz++;//指向下一個碼字
      }
     code <<= 1;//否則碼字加1補0
     nbits++;
   }

  /*
   * Build the lookup table, and the slowtable if needed.
   */
  next_free_entry = -1;
  for (i=0; huffsize[i]; i++)//當各碼長碼字數不爲0 時
   {
     val = vals[i];//vals[i]表示i碼長碼字個數
     code = huffcode[i];//碼字
     code_size = huffsize[i];///碼長
	#if TRACE
     fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
	 fflush(p_trace);
	 /*add by yangyulan*/
	  fprintf(hufftable,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
	  fflush(hufftable);
	 /*end by yangyulan*/
	
    #endif
	 
     table->code_size[val] = code_size;
     if (code_size <= HUFFMAN_HASH_NBITS)
      {
	/*
	 * Good: val can be put in the lookup table, so fill all value of this
	 * column with value val 
	 */
	int repeat = 1UL<<(HUFFMAN_HASH_NBITS - code_size);
	code <<= HUFFMAN_HASH_NBITS - code_size;
	while ( repeat-- )
	  table->lookup[code++] = val;

      }
     else
      {
	/* Perhaps sorting the array will be an optimization */
	uint16_t *slowtable = table->slowtable[code_size-HUFFMAN_HASH_NBITS-1];
	while(slowtable[0])
	  slowtable+=2;
	slowtable[0] = code;
	slowtable[1] = val;
	slowtable[2] = 0;
	/* TODO: NEED TO CHECK FOR AN OVERFLOW OF THE TABLE */
      }

   }

}

任務三:輸出Huffman碼錶到txt文件:

在解析重建Huffman表之後,我們可以中間輸出Huffman表到txt文件中:即在trace中添加如下代碼:
#if TRACE
     fprintf(p_trace,"Huffman table %s[%d] length=%d\n", (index&0xf0)?"AC":"DC", index&0xf, count);
	 fflush(p_trace);
	 /*add by yangyulan*/
	 fprintf(hufftable,"Huffman table %s[%d] length=%d\n", (index&0xf0)?"AC":"DC", index&0xf, count);
	 fflush(hufftable);
	 /*end by yangyulan*/
	 
#endif

parse_SOS函數:

得到解析每個顏色分量的 DC、 AC 值所使用的 Huffman 表序號(與 DHT中序號對應)
static int parse_SOS(struct jdec_private *priv, const unsigned char *stream)
{
  unsigned int i, cid, table;
  unsigned int nr_components = stream[2];//獲得分量數
#if TRACE
  fprintf(p_trace,"> SOS marker\n");
  fflush(p_trace);
#endif

#if SANITY_CHECK
  if (nr_components != 3)
    snprintf(error_string, sizeof(error_string),"We only support YCbCr image\n");
#endif

  stream += 3;//指向Y分量ID
  for (i=0;i<nr_components;i++) {
     cid = *stream++;//ID賦值給cid
     table = *stream++;//對應的量化和Huffman碼錶
#if SANITY_CHECK
     if ((table&0xf)>=4)
	snprintf(error_string, sizeof(error_string),"We do not support more than 2 AC Huffman table\n");
     if ((table>>4)>=4)
	snprintf(error_string, sizeof(error_string),"We do not support more than 2 DC Huffman table\n");
     if (cid != priv->component_infos[i].cid)
        snprintf(error_string, sizeof(error_string),"SOS cid order (%d:%d) isn't compatible with the SOF marker (%d:%d)\n",
	      i, cid, i, priv->component_infos[i].cid);
#if TRACE
     fprintf(p_trace,"ComponentId:%d  tableAC:%d tableDC:%d\n", cid, table&0xf, table>>4);
	 fflush(p_trace);
#endif
#endif
     priv->component_infos[i].AC_table = &priv->HTAC[table&0xf];//得到每個顏色分量的ACHuffman碼錶
     priv->component_infos[i].DC_table = &priv->HTDC[table>>4];//得到每個顏色分量的DCHuffman碼錶
  }
  priv->stream = stream+3;//指向熵編碼數據流的開始
#if TRACE
  fprintf(p_trace,"< SOS marker\n");
  fflush(p_trace);
#endif
  return 0;
}
分析完文件頭的各個marker後,我們開始對熵編碼數據進行解碼,這部分在tinyjpeg_decode函數中實現:

3.依據每個分量的水平、垂直採樣因子計算 MCU 的大小,並得到每個 MCU 中 8*8宏塊的個數

 xstride_by_mcu = ystride_by_mcu = 8;//初始化爲4:4:4的情況,即MCU的寬和高都爲8像素
  if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) {//Y分量的垂直和水平採樣因子相等
     decode_MCU = decode_mcu_table[0];//每個MCU就包括1個Y分量
     convert_to_pixfmt = colorspace_array_conv[0];
#if TRACE
     fprintf(p_trace,"Use decode 1x1 sampling\n");
	 fflush(p_trace);
#endif
  } else if (priv->component_infos[cY].Hfactor == 1) {//如果水平採樣因子爲1,垂直爲2,
     decode_MCU = decode_mcu_table[1];//每個MCU 包含2個Y分量
     convert_to_pixfmt = colorspace_array_conv[1];
     ystride_by_mcu = 16;//一個MCU的高爲16像素
#if TRACE
     fprintf(p_trace,"Use decode 1x2 sampling (not supported)\n");
	 fflush(p_trace);
#endif
  } else if (priv->component_infos[cY].Vfactor == 2) {//如果水平採樣因子爲2,垂直爲2,
     decode_MCU = decode_mcu_table[3];//每個MCU 包含4個Y分量
     convert_to_pixfmt = colorspace_array_conv[3];
     xstride_by_mcu = 16;//一個mcu的寬爲16像素
     ystride_by_mcu = 16;//一個mcu的高爲16像素
#if TRACE 
	 fprintf(p_trace,"Use decode 2x2 sampling\n");
	 fflush(p_trace);
#endif
  } else {//如果水平採樣因子爲2,垂直爲1
     decode_MCU = decode_mcu_table[2];//每個MCU 包含2個Y分量
     convert_to_pixfmt = colorspace_array_conv[2];
     xstride_by_mcu = 16;//一個mcu的寬爲16
#if TRACE
     fprintf(p_trace,"Use decode 2x1 sampling\n");
	 fflush(p_trace);
#endif
  }

4.對每個 MCU 解碼(依照各分量水平、垂直採樣因子對 MCU 中每個分量宏塊解碼)

對每個宏塊進行 Huffman 解碼,得到 DCT 係數
對每個宏塊的 DCT 係數進行 IDCT,得到 Y、 Cb、 Cr
遇到 Segment Marker RST 時,清空之前的 DC DCT 係數
/*
 * Decode all the 3 components for 1x1 
 */
static void decode_MCU_1x1_3planes(struct jdec_private *priv)
{
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 8);
  
  // Cb
  process_Huffman_data_unit(priv, cCb);
  IDCT(&priv->component_infos[cCb], priv->Cb, 8);

  // Cr
  process_Huffman_data_unit(priv, cCr);
  IDCT(&priv->component_infos[cCr], priv->Cr, 8);
}

/*
 * Decode a 1x1 directly in 1 color
 */
static void decode_MCU_1x1_1plane(struct jdec_private *priv)//採樣格式爲1:1:1
{
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 8);
  
  // Cb
  process_Huffman_data_unit(priv, cCb);
  IDCT(&priv->component_infos[cCb], priv->Cb, 8);

  // Cr
  process_Huffman_data_unit(priv, cCr);
  IDCT(&priv->component_infos[cCr], priv->Cr, 8);
}


/*
 * Decode a 2x1
 *  .-------.
 *  | 1 | 2 |
 *  `-------'
 */
static void decode_MCU_2x1_3planes(struct jdec_private *priv)//採樣格式爲2:1:1
{
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+8, 16);

  // Cb
  process_Huffman_data_unit(priv, cCb);
  IDCT(&priv->component_infos[cCb], priv->Cb, 8);

  // Cr
  process_Huffman_data_unit(priv, cCr);
  IDCT(&priv->component_infos[cCr], priv->Cr, 8);
}

/*
 * Decode a 2x1
 *  .-------.
 *  | 1 | 2 |
 *  `-------'
 */
static void decode_MCU_2x1_1plane(struct jdec_private *priv)
{
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+8, 16);

  // Cb
  process_Huffman_data_unit(priv, cCb);

  // Cr
  process_Huffman_data_unit(priv, cCr);
}


/*
 * Decode a 2x2
 *  .-------.
 *  | 1 | 2 |
 *  |---+---|
 *  | 3 | 4 |
 *  `-------'
 */
static void decode_MCU_2x2_3planes(struct jdec_private *priv)
{
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+8, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64*2, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64*2+8, 16);

  // Cb
  process_Huffman_data_unit(priv, cCb);
  IDCT(&priv->component_infos[cCb], priv->Cb, 8);

  // Cr
  process_Huffman_data_unit(priv, cCr);
  IDCT(&priv->component_infos[cCr], priv->Cr, 8);
}

/*
 * Decode a 2x2 directly in GREY format (8bits)
 *  .-------.
 *  | 1 | 2 |
 *  |---+---|
 *  | 3 | 4 |
 *  `-------'
 */
static void decode_MCU_2x2_1plane(struct jdec_private *priv)
{
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+8, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64*2, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64*2+8, 16);

  // Cb
  process_Huffman_data_unit(priv, cCb);

  // Cr
  process_Huffman_data_unit(priv, cCr);
}

/*
 * Decode a 1x2 mcu
 *  .---.
 *  | 1 |
 *  |---|
 *  | 2 |
 *  `---'
 */
static void decode_MCU_1x2_3planes(struct jdec_private *priv)
{
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 8);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64, 8);

  // Cb
  process_Huffman_data_unit(priv, cCb);
  IDCT(&priv->component_infos[cCb], priv->Cb, 8);

  // Cr
  process_Huffman_data_unit(priv, cCr);
  IDCT(&priv->component_infos[cCr], priv->Cr, 8);
}

/*
 * Decode a 1x2 mcu
 *  .---.
 *  | 1 |
 *  |---|
 *  | 2 |
 *  `---'
 */
static void decode_MCU_1x2_1plane(struct jdec_private *priv)
{
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 8);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64, 8);

  // Cb
  process_Huffman_data_unit(priv, cCb);

  // Cr
  process_Huffman_data_unit(priv, cCr);
}

process_Huffman_data_unit函數:對一個8*8的彩色分量單元進行解碼:

static void process_Huffman_data_unit(struct jdec_private *priv, int component)
{
  unsigned char j;
  unsigned int huff_code;
  unsigned char size_val, count_0;


  struct component *c = &priv->component_infos[component];
  short int DCT[64];


  /* 初始化DCT係數表*/
  memset(DCT, 0, sizeof(DCT));

  /* DC係數解碼*/
  huff_code = get_next_huffman_code(priv, c->DC_table);
  //trace("+ %x\n", huff_code);
  if (huff_code) {
     get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, huff_code, DCT[0]);// 查表的 DC DCT 係數(殘值)
     DCT[0] += c->previous_DC;
     c->previous_DC = DCT[0];// DC 係數採用差分編碼, 恢復原值
} else { DCT[0] = c->previous_DC; } /* AC係數解碼 */ j = 1; while (j<64) { huff_code = get_next_huffman_code(priv, c->AC_table); //trace("- %x\n", huff_code); size_val = huff_code & 0xF;// Amplitude 幅度 count_0 = huff_code >> 4;// 零遊程長度 if (size_val == 0)// 0 不是一個有效的 Amplitude 值,這裏做零遊程標誌 { /* 零遊程 */ if (count_0 == 0) break; /* EOB found, go out */ else if (count_0 == 0xF) j += 16; /* skip 16 zeros */ } else { j += count_0; /* 忽略零遊程 */ if (__unlikely(j >= 64))//出錯了 { snprintf(error_string, sizeof(error_string), "Bad huffman data (buffer overflow)"); break; } get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]);// 查表得到 AC DCT 係數 j++; } } for (j = 0; j < 64; j++) c->DCT[j] = DCT[zigzag[j]]; }

5.解完所有 MCU,解碼結束

for (y=0; y < priv->height/ystride_by_mcu; y++) // 行循環
{
//trace("Decoding row %d\n", y);
priv->plane[0] = priv->components[0] + (y * bytes_per_blocklines[0]);
priv->plane[1] = priv->components[1] + (y * bytes_per_blocklines[1]);
priv->plane[2] = priv->components[2] + (y * bytes_per_blocklines[2]);
for (x=0; x < priv->width; x+=xstride_by_mcu) // 列循環
{
decode_MCU(priv); // 解碼( Huffman 解碼 + IDCT)
convert_to_pixfmt(priv);
priv->plane[0] += bytes_per_mcu[0];
priv->plane[1] += bytes_per_mcu[1];
priv->plane[2] += bytes_per_mcu[2];
if (priv->restarts_to_go>0)
{
priv->restarts_to_go--;
if (priv->restarts_to_go == 0)
{
priv->stream -= (priv->nbits_in_reservoir/8);
resync(priv); // 清空 preDC(所有顏色分量)
if (find_next_rst_marker(priv) < 0) // 查找 RST 標記
return -1;
}
}
}
}

任務四:輸出DC圖像,AC圖像並經過huffman統計其概率分佈

1.在結構體中創建兩個int指針和兩個char型指針;int型便於計算,char型用於輸出
  /*add by yangyulan for DCimage and ACimage*/
  int  *dcimg,*acimg;
  unsigned char *dcimg_ch,*acimg_ch;
  /*end by yangyulan for DCimage and ACimage*/
2.定義兩個文件指針分別打開兩個yuv文件:DC圖像和AC圖像
 由於DCT變換的能量守恆和要求DC係數和AC係數均爲有效的數據(0~255),我們定義了用於後面作歸一化處理的4個變量:acmax,dcmax,acmin,dcmin。
  /*add by yangyulan for DCimage and ACimage*/
  FILE *DCimg=fopen("DCimage.yuv","wb");
  FILE *ACimg=fopen("ACimage.yuv","wb");
    static int ida=0;
 int acmax, dcmax, acmin, dcmin;
  /*end by yangyulan for DCimage and ACimage*/
3.爲創建的指針分配空間:(注意數據類型)
/*add by yangyulan for DCimage and ACimage*/
	priv->dcimg = (int *)malloc(sizeof(int)*priv->width * priv->height/64);//每個8*8的塊只有一個直流係數
    priv->acimg = (int*)malloc(sizeof(int)*priv->width * priv->height / 64);//
	priv->dcimg_ch = (unsigned char *)malloc(sizeof(unsigned char)*priv->width * priv->height/64);
    priv->acimg_ch = (unsigned char*)malloc(sizeof(unsigned char)*priv->width * priv->height / 64);//

  /*end by yangyulan for DCimage and ACimage*/
4.每解一個8*8的塊,將DC(DCT[0])和AC(這裏是DCT[1])寫入內存
	/*add by yangyulan*/
	if(ida<priv->width*priv->height/64)
	{
	priv->dcimg[ida] = priv->component_infos[cY].DCT[0];
	priv->acimg[ida] = priv->component_infos[cY].DCT[1];
	ida++;
	}
	/*end by yangyulan*/
5.歸一化處理
根據DCT的能量守恆特性,反DCT之後的DC取值最大可達到8*256,且反差分編碼後數據可能爲負數。因此需要對得到的DC係數進行歸一化處理,使其值分佈在[0,255]之間,AC係數亦是如此。
/*add by yangyulan for DCimage and ACimage*/

  
  acmax = priv->acimg[0];
  acmin = priv->acimg[0];
  dcmax = priv->dcimg[0];
  dcmin = priv->dcimg[0];
  for (ida = 0; ida < priv->width*priv->height / 64; ida++)
  {
      if (priv->acimg[ida] >=acmax)
          acmax = priv->acimg[ida];
      if (priv->dcimg[ida] >= dcmax)
          dcmax = priv->dcimg[ida];
      if (priv->acimg[ida] <= acmin)
          acmin = priv->acimg[ida];
      if (priv->dcimg[ida] <=dcmin)
          dcmin = priv->dcimg[ida];
  }
  for (ida = 0; ida < priv->width*priv->height / 64; ida++)
  {
      priv->acimg_ch[ida] = (unsigned char)(255 *(priv->acimg[ida]-acmin)/ (acmax - acmin));
  }

  for (ida= 0; ida < priv->width*priv->height / 64; ida++)
  {
      priv->dcimg_ch[ida] =(unsigned char)(255 *(priv->dcimg[ida]-dcmin)/ (dcmax - dcmin));
  }


	fwrite(priv->dcimg_ch, 1, priv->width*priv->height / 64, DCimg);
    fwrite(priv->acimg_ch, 1, priv->width*priv->height / 64, ACimg);

  /*end by yangyulan for DCimage and ACimage*/
6.再將輸出的DC圖像和AC圖像經過Huffman編碼器,得到其概率分佈函數。Huffman編碼器的程序前面實驗已經給出,這裏不再贅述。

四:實驗結果

1.任務一實驗結果:



2.任務二實驗結果:

3.任務三實驗結果:


4.任務四實驗結果:

輸出的DC和AC圖像;

DC和AC圖像的概率分佈:

5.附加:文字噪聲
蚊子噪聲的來歷:銳利的高頻截止
表現爲下列現象:
壓縮率        圖像細節
最高
從上表可以看出,圖像壓縮得越厲害,蚊子噪聲越明顯。


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