天天看點

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

實驗5_JPEG

  • 一、JPEG是什麼?
    • 1、JPEG簡介
    • 2、JPEG優缺點
    • 3、JPEG4種運作模式
  • 二、JPEG編碼
    • 1、Level offset(零偏置)
    • 2、8×8DCT
    • 3、量化
    • 4、DCT直流值和AC交流系數的編碼
    • 5、熵編碼
  • 三、JPEG檔案格式分析
    • 1、JPEG格式說明
    • 2、JPEG主要資料段說明
      • 1)SOI ->Start of Image
      • 2)APP0 ->Application 0(保留标記)
      • 3)APPx -> APPn->Application,x=1~15(可任選)
      • 4)DQT ->Define Quantization Table,定義量化表
      • 5)SOF0 ->Start of Frame
      • 6)DHT ->Difine Huffman Table
      • 7)DRI ->Define Restart Interval
      • 8)SOS ->Start of Scan
      • 9)EOI ->End of Image
  • 四、實驗解碼代碼分析
    • 1、jedc_private結構體
    • 2、compontent結構體
    • 3、build_huffman_table函數
    • 4、build_quantization_table函數
    • 5、生成YUV圖像
    • 6、輸出量化矩陣
    • 7、輸出Huffman碼表
    • 8、結果分析
      • 1)生成檔案
      • 2)YUV圖像
      • 3)量化表
      • 4)huffman表
  • 鳴謝

一、JPEG是什麼?

1、JPEG簡介

JPEG( Joint Photographic Experts Group)即聯合圖像專家組,是用于連續色調靜态圖像壓縮的一種标準,檔案字尾名為.jpg或.jpeg,是最常用的圖像檔案格式。其主要是采用預測編碼(DPCM)、離散餘弦變換(DCT)以及熵編碼的聯合編碼方式,以去除備援的圖像和彩色資料,屬于有損壓縮格式,它能夠将圖像壓縮在很小的儲存空間,一定程度上會造成圖像資料的損傷。尤其是使用過高的壓縮比例,将使最終解壓縮後恢複的圖像品質降低,如果追求高品質圖像,則不宜采用過高的壓縮比例。

然而,JPEG壓縮技術十分先進,它可以用有損壓縮方式去除備援的圖像資料,換句話說,就是可以用較少的磁盤空間得到較好的圖像品質。而且JPEG是一種很靈活的格式,具有調節圖像品質的功能,它允許用不同的壓縮比例對檔案進行壓縮,支援多種壓縮級别,壓縮比率通常在10;1到40;1,壓縮比越大,圖像品質就越低;相反地,壓縮比越小,圖像品質就越高。同一幅圖像,用JPEG格式存儲的檔案是其他類型檔案的1/10~1/20,通常隻有幾十KB,品質損失較小,基本無法看出。JPEG格式壓縮的主要是高頻資訊,對色彩的資訊保留較好,适合應用于網際網路;它可減少圖像的傳輸時間,支援24位真彩色;也普遍應用于需要連續色調的圖像中。

JPEG格式可分為标準JPEG、漸進式JPEG及JPEG2000三種格式。

  1. 标準JPEG格式;此類型在網頁下載下傳時隻能由上而下依序顯示圖像,直到圖像資料全部下載下傳完畢,才能看到圖像全貌。
  2. 漸進式JPEG;此類型在網頁下載下傳時,先呈現出圖像的粗略外觀後,再慢慢地呈現出完整的内容,而且存成漸進式JPG格式的文檔比存成标準JPG格式的文檔要來得小,是以如果要在網頁上使用圖像,可以多用這種格式。
  3. JPEG2000;它是新一代的影像壓縮法,壓縮品質更高,并可改善在無線傳輸時,常因信号不穩造成馬賽克現象及位置錯亂的情況,改善傳輸的品質。

2、JPEG優缺點

優點:

1)它支援極高的壓縮率,是以JPEG圖像的下載下傳速度大大加快。

2)它能夠輕松地處理16.8M顔色,可以很好地再現全彩色的圖像。

3)在對圖像的壓縮處理過程中,該圖像格式可以允許自由地在最小檔案尺寸(最低圖像品質)和最大檔案尺寸(最高圖像品質)之間選擇。

4)該格式的檔案尺寸相對較小,下載下傳速度快,有利于在帶寬并不“富裕”的情況下傳輸。

缺點:

1)并非所有的浏覽器都支援将各種JPEG圖像插入網頁。

2)壓縮時,可能使圖像的品質受到損失,是以不适宜用該格式來顯示高清晰度的圖像。

3、JPEG4種運作模式

在JPEG算法中,共包含4種運作模式,其中一種是基于DPCM的無損壓縮算法,另外3種是基于DCT的有損壓縮算法。其要點如下:

1)無損壓縮編碼模式。采用預測法和哈夫曼編碼(或算術編碼)以保證重建圖像與原圖像完全相同(設均方誤差為零),可見無失真。

2)基于DCT的順序編碼模式。根據DCT變換原理,按從上到下、從左到右的順序對圖像資料進行壓縮編碼。當資訊傳送到接收端時,首先按照上述規律進行解碼,進而還原圖像。在此過程中存在資訊丢失,是以這是一種有損圖像壓縮編碼。

3)基于DCT的累進編碼模式。它也是以DCT變換為基礎的,但是其掃描過程不同。它通過多次掃描的方法來對一幅圖像進行資料壓縮。其描述過程采取由粗到細逐漸累加的方式進行。圖像還原時,在螢幕上首先看到的是圖像的大緻情況,而後逐漸地細化,直到全部還原出來為止。

4)基于DCT的分層編碼模式。這種模式是以圖像分辨率為基準進行圖像編碼的。它首先是從低分辨率開始,逐漸提高分辨率,直至與原圖像的分辨率相同為止。圖像重建時也是如此。可見其效果與基于DCT累進編碼模式相似,但其處理起來更複雜,所獲得的壓縮比也更高一些。

二、JPEG編碼

基本(baseline) JPEG編碼器如下圖:

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

下面按步驟簡單的介紹一下各部分:

1、Level offset(零偏置)

對于灰階級是2n的像素,通過減去2n-1,将無符号的整數值變成有符号數

➢ 對于n=8,即将0-255的值域,通過減去128,轉換為值域在-128-127之間的值

◼目的:使像素的絕對值出現3位10進制的機率大大減少

2、8×8DCT

對每個單獨的彩色圖像分量,把整個分量圖像分成8×8的圖像塊,如圖所示,并作為兩維離散餘弦變換DCT的輸入

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

DCT變換使用下式計算

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

逆變換使用下式計算

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

其中

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

3、量化

為了達到壓縮的目的,DCT系數需作量化,量化表針對的設計。例如,利用人眼的視覺特性,對在圖象中占有較大能量的低頻成分,賦予較小的量化間隔和較少的比特表示,以獲得較高的壓縮比。

JPEG的量化采用線性均勻量化器,量化公式為:

Cq(u,v)=Integer(Round(C(u,c)/Q(u,v))
           

其中Q(u,v)是量化器步長,它是量化表的元素,量化元素随DCT系數的不同和彩色分量的不同有不同的值。量化表的大小也為8X8,和64個變換系數一一對應。在JPEG算法中,對于8X8的亮度資訊和色度資訊,分别給出了預設的量化表。這個量化表在實驗的基礎上,結合人眼的視覺特性而獲得的

4、DCT直流值和AC交流系數的編碼

DC系數差分編碼與AC系數遊程編碼,64個變換系數中,DC系數是處于左上角的,它實際上等于64個圖像采樣值的平均值。相鄰的8×8子塊之間的DC系數有較強的相關性。JPEG對于量化後的DC系數采用差分編碼,二相鄰塊的DC系數的內插補點為DCi-(DCi-1)。其餘63個AC系數量化後通常出現較多零值,JPEG算法采用遊程編碼,并建議在8×8矩陣中按照“Z”形的次序進行,可增加零的連續次數。

系數編碼後都采用統一的格式表示,包含二個符号的内容:第一符号占一個位元組,對于DC系數而言,它的高4位總為零;對于AC系數而言,它表示到下一個非零系數前,所包含的連續為零的系數個數。第一位元組的低4位表示DC內插補點的幅值編碼所需的比特數,或表示AC系數中下一個非零幅值編碼所需的比特數。第二個符号位元組表示DC內插補點的幅值,或下一個非零AC系數的幅值。

還需要指出,由于第一位元組中隻有4位表示遊程長度,量大值為15。當遊程長度大于15時,可以插入一個至多個(10)位元組,直至剩下的零AC系數個數小于15為止。是以,63個AC系數表示為由二個符号對組成的序列,其中也可能插入10個位元組,快結束位元組以全零表示。

l DC值:量化後,坐标u=v=0時的取值。它是整個塊能量的主要部分,它有兩個特點:

– 該值比較大

– 相鄰的兩個圖像塊之間的DC值變化不大

l 對于量化後的AC系數,它是一個稀疏矩陣:矩陣中許多位置上的值為零。

– 采用RLE編碼

– 用EOB(特殊的碼字表示塊的結束

l 例如下面的量化後的亮度快,按Z字形排列:

下标:0 1 2 3 4 5 6 7 8 9—30 31 32—63

系數:12 5 -2 0 2 0 0 0 1 0 -1 0

5、熵編碼

對于上面給出的碼序列,再進行統計特性的熵編碼。這僅對于序列中每個符号對中的第一個位元組進行,第二個幅值位元組不作編碼,仍然直接傳送。

JPEG建議使用二種熵編碼方法:哈夫曼編碼和自适應二進制算術編碼。對于哈夫曼編碼,JPEG提供了針對DC系數、AC系數使用的哈夫曼表(包括對于圖像的亮度值與色度值二種情況),在編碼和解碼時使用。JPEG解碼器能夠同時存儲最多4套不同的熵編碼表。

三、JPEG檔案格式分析

1、JPEG格式說明

JPEG檔案可以看作由多條資料段拼接成的檔案,每條資料段包括兩個部分:标記碼和資料流。

标記碼:由兩個位元組構成,其前一個位元組為0xFF(通常隻有一個0xFF,可以多個連續的0xFF),後一個位元組則根據不同意義有不同數值。

資料流:記錄了關于JPEG檔案的相應資訊(有些資料段無資料流)。

常用的資料段有SOI、APPx、DQT、SOF0、DHT、DRI、SOS、EOI等(SOI等隻是資料段名,例如SOI是标記碼為0xFFD8的資料段名,DHT是标記碼為0xFFC4的資料段名)。

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

2、JPEG主要資料段說明

1)SOI ->Start of Image

含義:圖像開始

标記碼:0xFFD8 占2位元組

資料流:無

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

2)APP0 ->Application 0(保留标記)

含義:應用程式保留标記0

标記碼:0xFFE0——占2位元組

資料流:

資料段長度——占2位元組

辨別符——5位元組(固定值0x4A46494600=“JFIF0”)

版本号——2位元組

密度機關——1位元組(0->無機關 1->點數/英寸 2->點數/厘米)

X方向像素密度——2位元組

Y方向像素密度——2位元組

縮略圖水準像素數目——1位元組

縮略圖垂直像素數目——1位元組

縮略圖RGB位圖——長度為是3的倍數

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

3)APPx -> APPn->Application,x=1~15(可任選)

含義:應用程式保留标記x

标記碼:0xFFE1~0xFFF——2位元組(手機照片通常包含APP1,内容由地點,時間等)

資料流:

資料段長度——占2位元組(不同資料段資料格式不同)

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

4)DQT ->Define Quantization Table,定義量化表

含義:定義量化表

标記碼:0xFFDB——占2位元組

資料流:

資料段長度——占2位元組

量化表長度——占2位元組

量化表ID——占1位元組

量化表内容——占64位元組

(量化表可以有多個,不超過4個)

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

量化表1

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

量化表2

5)SOF0 ->Start of Frame

含義:幀圖像起始

标記碼:0xFFC0——占2位元組

資料流:

資料段長度——占2位元組

精度——1位元組

圖像高度——2位元組

圖像寬度——2位元組

顔色分量數——1位元組(1-灰階圖 3-YCrCb或YIQ 4-CMYK)

顔色分量資訊——9位元組

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

6)DHT ->Difine Huffman Table

含義:定義哈夫曼表

标記碼:0xFFC4——占2位元組

資料流:

資料段長度——占2位元組

霍夫曼表長度——占2位元組

表ID和表類型——1位元組

不同位數的碼字數量——16位元組

編碼内容——占16個不同位數的碼字數量之和位元組

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

哈夫曼表1

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

哈夫曼表2

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

哈夫曼表3

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

哈夫曼表4

7)DRI ->Define Restart Interval

含義:定義差分編碼累計複位的間隔

标記碼:0xFFDD——占2位元組

資料流:

資料段長度——占2位元組(長度固定為4)

MCU塊的單元中的重新開始間隔——占2位元組

8)SOS ->Start of Scan

含義:定義差分編碼累計複位的間隔

标記碼:0xFFDA——占2位元組

資料流:

資料段長度——占2位元組

顔色分量數——1位元組(1-灰階圖是 3-YCrCb或YIQ 4-CMYK)

顔色分量ID——1位元組

直流/交流系數表号——1位元組

壓縮圖像資料——3位元組(固定值0x003F00)

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

9)EOI ->End of Image

含義:圖像結束

标記碼:0xFFD9 占2位元組

資料流:無

四、實驗解碼代碼分析

1、jedc_private結構體

jdec_private 結構體,其用于訓示解碼過程中的所有資訊,量化表,霍夫曼碼表以及圖像資料;

struct jdec_private
{
  //公共變量
  uint8_t *components[COMPONENTS];
  unsigned int width, height;	// 圖像大小 
  unsigned int flags;

  // 私有變量
  const unsigned char *stream_begin, *stream_end;
  unsigned int stream_length;

  const unsigned char *stream;	//指向目前流的指針
  unsigned int reservoir, nbits_in_reservoir;

  struct component component_infos[COMPONENTS];
  float Q_tables[COMPONENTS][64];		//量化表
  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;				//在此重新啟動間隔中留下的mcu。
  int last_rst_marker_seen;			//RST标記每次遞增
  //IDCT之後用于存儲每個元件的臨時空間
  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];

};
           

2、compontent結構體

Component 結構體,其用于參與霍夫曼解碼,反量化,IDCT 以及彩色空間變換,Hfactor和 Vfactor 用于說明水準與垂直的采樣情況。

struct component 
{
  unsigned int Hfactor;
  unsigned int Vfactor;
  float *Q_table;		// 指向要使用的量化表的指針。
  struct huffman_table *AC_table;
  struct huffman_table *DC_table;
  short int previous_DC;	// 前直流系數 
  short int DCT[64];		// DCT 系數
#if SANITY_CHECK
  unsigned int cid;
#endif
};
           

3、build_huffman_table函數

如名可知,是為了建構霍夫曼表:

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;

  /*
   * 建構臨時數組
   *   huffsize[X] => numbers of bits to write vals[X]
   */
  hz = huffsize;
  for (i=1; i<=16; i++)
   {
     for (j=1; j<=bits[i]; j++)
       *hz++ = i;
   }
  *hz = 0;

  memset(table->lookup, 0xff, sizeof(table->lookup));
  for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++)
    table->slowtable[i][0] = 0;

  /* 建構臨時數組
   *   huffcode[X] => code used to write vals[X]
   */
  code = 0;
  hc = huffcode;
  hz = huffsize;
  nbits = *hz;
  while (*hz)
   {
     while (*hz == nbits)
      {
	*hc++ = code++;
	hz++;
      }
     code <<= 1;
     nbits++;
   }

  /*
   * 建立查找表
   */
  next_free_entry = -1;
  for (i=0; huffsize[i]; i++)
   {
     val = vals[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);
    #endif
     table->code_size[val] = code_size;
     if (code_size <= HUFFMAN_HASH_NBITS)
      {
	/*
	 * 可以将Val放在查找表中,是以可以填充此表的所有值
	 * 值為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 */
      }

   }
}
           

4、build_quantization_table函數

如名可知,是為了建構量化表:

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

  //輸出量化表
#if TRACE
  const unsigned char* zhi = zigzag;
  for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++) {
          fprintf(p_trace, "%-10d", ref_table[*zhi++]);
          if (j == 7) {
              fprintf(p_trace, "\n");
          }
      }
  }
#endif

}
           

5、生成YUV圖像

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

因為YUV按照全部像素Y資料塊、U資料塊、V資料塊依次存放,且為4:2:0采樣,是以寫入方式為YYYY…,U…,V…;按順序寫入即可

6、輸出量化矩陣

量化表是在build_huffman_table函數中建立的,是以在其中加入以下輸出代碼即可輸出量化表:

//輸出量化表
#if TRACE
  const unsigned char* zhi = zigzag;
  for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++) {
          fprintf(p_trace, "%-10d", ref_table[*zhi++]);
          if (j == 7) {
              fprintf(p_trace, "\n");
          }
      }
  }
#endif

           

7、輸出Huffman碼表

AC和DC的Huffman碼表都是在build_huffman_table函數中完成建立的。是以我們進入這兩個函數,然後對他們進行檢視,并輸出碼表。函數中已經寫好:

#if TRACE
     fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
	 fflush(p_trace);
    #endif
           

8、結果分析

1)生成檔案

由下圖可見,一共生成5個檔案,其中output.Y、output.U、output.V是原本生成的Y、U、V分開的資料,因為不友善檢視,是以需要增加代碼使其生成.YUV檔案以便檢視,output.YUV即是生成的.YUV檔案;trace_jpeg.txt檔案包含了如huffman碼表,量化表等内容。

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

2)YUV圖像

輸出的YUV檔案,與原圖像相同,實驗結果正确

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

3)量化表

增加代碼,輸出量化表

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

4)huffman表

由表可知,huffman表是分直流與交流分量,分别有不同的碼表

實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝
實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝
實驗5_JPEG解碼一、JPEG是什麼?二、JPEG編碼三、JPEG檔案格式分析四、實驗解碼代碼分析鳴謝

鳴謝

本文參考CSDN部落客「翺翔菜鳥」的原創文章

原文連結:JPEG檔案格式分析——經典幹貨!!!https://blog.csdn.net/hunnanlitong/article/details/106323065

繼續閱讀