日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JPEG 原理分析及 JPEG 解码器的调试

發布時間:2023/12/18 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JPEG 原理分析及 JPEG 解码器的调试 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 前言
  • 一、JPEG
    • 1、JPEG概述
    • 2、文件結構
    • 3、編碼流程
    • 4、解碼流程
  • 二、實驗內容
    • 1、tinyjpeg-internal.h定義結構體
    • 2、JPEG文件主要操作代碼
  • 三、實驗結果
    • 1、YUV圖像
    • 2、量化表
    • 3、霍夫曼碼表
    • 4、分量圖像


前言

? JPEG( Joint Photographic Experts Group)是用于連續色調靜態圖像壓縮的一種標準,文件后綴名為.jpg或.jpeg,是最常用的圖像文件格式。其主要是采用預測編碼(DPCM)離散余弦變換(DCT)以及熵編碼的聯合編碼方式,以去除冗余的圖像和彩色數據,屬于有損壓縮格式。它的特點在于夠將圖像壓縮在很小的儲存空間,但不可避免地一定程度上會造成圖像數據的損傷。尤其是使用過高的壓縮比例。使用JPEG方法將使最終解壓縮后恢復的圖像質量降低,如果追求高品質圖像,則不宜采用過高的壓縮比例。

? 然而,JPEG編碼可以用有損壓縮方式去除冗余的圖像數據。換句話說,就是可以用較少的磁盤空間得到較好的圖像品質。而且JPEG是一種很靈活的格式,具有調節圖像質量的功能,它允許用不同的壓縮比例對文件進行壓縮,支持多種壓縮級別,壓縮比率通常在10:1到40:1。JPEG格式壓縮的主要是高頻信息,對色彩的信息保留較好,適合應用于互聯網;它可減少圖像的傳輸時間,支持24位真彩色;也普遍應用于需要連續色調的圖像中。

? 本實驗則在已經對JPEG的原理進行學習理解的基礎上,對C語言實現的JPEG分析和解碼程序進行分析,進行JPEG向YUV的轉換,對JPEG文件的等信息進行分析。


一、JPEG

1、JPEG概述

? JPEG文件格式是JPEG(聯合圖像專家組)標準的產物,該標準由ISO與CCI TT(國際電報電話咨詢委員會)共同制定,是面向連續色調靜止圖像的一種壓縮標準。其壓縮步驟大致分為四步:

  • 顏色轉換
    ? 由于JPEG只支持YUV顏色模式,而不支持RGB顏色模式,所以在將彩色圖像進行壓縮之前,必須先對顏色模式進據轉換。轉換完成之后還需要進行數據采樣。一般采用的采樣比例是2:1:1或4:2:2。由于在執行了此項工作之后,每兩行數據只保留一行,因此,采樣后圖像數據量將壓縮為原來的一半。

  • DCT變換
    ? DCT(DiscreteConsineTransform)是將圖像信號在頻率域上進行變換,分離出高頻和低頻信息的處理過程。然后再對圖像的高頻部分(即圖像細節)進行壓縮,以達到壓縮圖像數據的目的。首先將圖像劃分為多個8*8的矩陣。然后對每一個矩陣作DCT變換(變換公式此略)。變換后得到一個頻率系數矩陣,其中的頻率系數都是浮點數。

  • 量化
    ? 由于在后面編碼過程中使用的碼本都是整數,因此需要對變換后的頻率系數進行量化,將之轉換為整數。由于進行數據量化后,矩陣中的數據都是近似值,和原始圖像數據之間有了差異,這一差異是造成圖像壓縮后失真的主要原因。

  • 編碼
    ? 編碼采用兩種機制:一是0值的行程長度編碼;二是熵編碼(EntropyCoding)。在JPEG中,采用曲徊序列,即以矩陣對角線的法線方向作“之”字排列矩陣中的元素。這樣做的優點是使得靠近矩陣左上角、值比較大的元素排列在行程的前面,而行程的后面所排列的矩陣元素基本上為0值。行程長度編碼是非常簡單和常用的編碼方式,在此不再贅述。編碼實際上是一種基于統計特性的編碼方法。在JPEG中允許采用HUFFMAN編碼或者算術編碼。

? 本實驗則對JPEG的DCT變換、量化和編碼操作進行學習與代碼分析。

?

2、文件結構

? 此實驗使用實驗素材中的圖片進行 .jpg文件結構分析。使用HexFlex軟件進行十六進制分析。

? JPEG文件數據以數據段(Segment)為單位,以高位在前低位在后的形式進行存儲,不同的段以首部兩個字節進行標志,且大多段的有效數據長度(不含段頭)均在段頭后的2個字節進行標識。不同的段其段頭兩字節有特殊標識。

? JPEG文件不同段類型分為:

  • 文件開始標記 SOI
    ?標記固定為0xFFD8,是所有的JPEG文件的文件頭。

  • 應用程序保留標記 APP0
    ?固定標記為0xFFE0,其中包含如圖所示字段。


    ?同時部分JPEG文件可能還含有APPn標記,以攜帶圖片對應的應用程序信息(攝制設備等)。其中n的值為1-15,對應的固定標記為0xFFE1-0xFFEF。

  • 定義量化表 DQT
    ?固定標記為0xFFDB,在JPEG文件中通常存在兩個,分別為亮度信息的量化表與色度信息的量化表。每個量化表的長度一般為0x0043(若文件中兩量化表合在一起長度則為0x0084)。

  • 幀圖像開始 SOF0
    ?固定標記為0xFFC0,其內容如下。

  • 定義霍夫曼表 DHT
    ?固定標記為0xFFC4,后面兩字節為DHT段長度。
    ?DHT段第五字節則是霍夫曼表ID和類型。其中高四位為表類型,0和1分別代表當前表為直流分量霍夫曼表和交流分量霍夫曼表。低四位則為表ID,表示當前表為第幾個表。
    ?其后的16字節則代表霍夫曼編碼后對應碼長的碼字個數。霍夫曼碼長為1至16的碼字個數分別由這16個字節進行表示。
    ?再其后的字節則為編碼內容,表示每個符字對應的權值。

    ?上圖即為示例文件中某個ID為0的直流分量霍夫曼表。可看到此表中2-16位碼字應共有1+2+5+3+3+3+2+5+3+4+2+2+2+1+5=43個。選中后面表示每種碼字的權值,可看到的確位43個。

    ?而其中權值含義則為在解碼過程中,此碼字后的數據碼字的位長。根據位長讀取緊隨其后的碼字,即可進行解碼。

  • 定義掃描起點 SOS
    ?固定標志為0xFFDA,除段長度2字節外,存儲包含顏色分量信息和壓縮圖像數據。

    圖中0xFFDA后即為此段數據。

3、編碼流程

? JPEG編碼的過程如下圖所示。而解碼則是編碼的逆過程。

? 如圖所示。過程分別為:

  • Level Offset 零偏置
    ? 對于灰度級峰值為2n的圖片,通過將像素減去2n-1,將無符號數轉換為有符號數,從而使像素的絕對值出現3位10進制的概率大大減少,縮小數據范圍。

  • 8×8 DCT變換
    ?對每個單獨的圖像分量,進行8×8塊為單位的DCT變換,將系數向矩陣左上位置進行集中,以適合后續的交流直流分量分別編碼的方式。

  • Uniform Scalar Quantization 量化表量化
    ?對亮度信息和色度信息分別根據量化表進行量化。

  • 編碼
    ?編碼分為上下兩路,分別對量化后的系數矩陣作直流分量和交流分量的編碼。
    ?其中直流分量采用DPCM(差分脈沖調制編碼)的形式進行編碼之后,再對編碼所得的預測差值進行霍夫曼熵編碼。
    ?而對量化矩陣中的交流分量,由于DCT變換后的系數矩陣能量大多數存在于左上角,故使用之字形掃描。

    ?而對之字形掃描之后的數據進行游程編碼得到(RRRR,SSSS)格式的游程碼數據,再對數據進行固定格式的霍夫曼編碼,從而得到最終編碼數據。

4、解碼流程

?解碼則是對編碼的逆操作,其步驟大致為:

  • 解碼霍夫曼編碼碼字數據
  • 分別解碼直流分量和交流分量
  • 矩陣反量化
  • DCT逆變換
  • 丟棄補充的行和列(在編碼過程中防止圖像行列無法被8整除而進行邊緣拓展)
  • 反0偏置
  • 對插值Cb Cr分量進行插值
  • 還原圖像數據信息(向RGB轉換)

二、實驗內容

?實驗通過對JPEG文件解碼工程進行調試,對工程運行邏輯進行分析和理解,并加入實驗內容,以實現實驗目的。
?工程jpeg_dec共有三個頭文件 stdint.h、tinypeg.h和tinyjpeg-internal.h,以及三個代碼代碼.c文件 jidctflc.c、loadjpeg.c和tinyjpeg.c。

1、tinyjpeg-internal.h定義結構體

① 霍夫曼碼表結構體

//霍夫曼碼表 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];};

② 圖像塊結構體

//一個8×8的塊(DCT變換及量化編碼單元) struct component{unsigned int Hfactor; //水平采樣因子unsigned int Vfactor; //垂直采樣因子float* Q_table; /* Pointer to the quantisation table to use */struct huffman_table* AC_table; //交流DCT系數的哈夫曼碼表指針struct huffman_table* DC_table; //直流DCT系數的哈夫曼碼表指針short int previous_DC; //前一個塊的直流系數(用于進行預測差分編碼)short int DCT[64]; //存儲當前塊的8×8 DCT系數 #if SANITY_CHECKunsigned 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;/* Pointer to the current stream */const unsigned char* stream; //指向文件流的指針unsigned int reservoir, nbits_in_reservoir;struct component component_infos[COMPONENTS];/* quantization tables */float Q_tables[COMPONENTS][64]; //量化表/* Huffman Tables */struct huffman_table HTDC[HUFFMAN_TABLES]; //直流系數霍夫曼表struct huffman_table HTAC[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]; //存儲反DCT變換之后的分量(4:2:0)jmp_buf jump_state;/* Internal Pointer use for colorspace conversion, do not modify it !!! */uint8_t* plane[COMPONENTS];/* 添加:用于存放直流系數(DCT[0])和交流系數(DCT[1])的數組 */int* dc, * ac; //用于讀取的指針unsigned char* outdc, * outac; //用于輸出的指針};

2、JPEG文件主要操作代碼

?實驗中對JPEG進行解碼等主要操作均在tinyjpeg.c文件中。此實驗通過代碼運行邏輯順序進行分析理解,并在過程中通過添加代碼完成實驗要求。

① 解析JPEG文件數據段
?對讀取過程中的文件段頭(兩字節)標志進行讀取,并判斷段的種類,調用相應的函數進行操作。

//判斷jpeg文件 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)) //以0xFFD8開始snprintf(error_string, sizeof(error_string), "Not a JPG file ?\n");priv->stream_begin = buf + 2; //跳過0xFFD8頭,讀取文件頭priv->stream_length = size - 2; //除去0xFFD8頭的長度,為文件流總長度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){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 TRACEfprintf(p_trace, "> Unknown marker %2.2x\n", marker);fflush(p_trace); #endifbreak;}stream = next_chunck; //下一個數據塊,繼續進行掃描}if (!dht_marker_found) { #if TRACEfprintf(p_trace, "No Huffman table loaded, using the default one\n");fflush(p_trace); #endifbuild_default_huffman_tables(priv);}#ifdef SANITY_CHECKif ((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"); #endifreturn 0; bogus_jpeg_format: #if TRACEfprintf(p_trace, "Bogus jpeg format\n");fflush(p_trace); #endifreturn -1;}

② DQT段操作函數
?對DQT段進行解析,以得到量化表信息。同時加入代碼,來輸出所有的量化矩陣。

/* 解析DQT(0xFFDB),獲取量化矩陣元素 */ static int parse_DQT(struct jdec_private* priv, const unsigned char* stream){int qi;float* table; //指向量化表元素const unsigned char* dqt_block_end;//指向量化表尾#if TRACEfprintf(p_trace, "> DQT marker\n");fflush(p_trace); #endifdqt_block_end = stream + be16_to_cpu(stream); //讀取文件數據中第一和第二字節,從而得出長度stream += 2; //跳過長度信息的兩字節,指向有效數據/* 是否已經達到了尾部 */while (stream < dqt_block_end){qi = *stream++;#if SANITY_CHECK//高四位為量化位數,此字節為0x00為8位,0x01為16位if (qi >> 4)snprintf(error_string, sizeof(error_string), "16 bits quantization table is not supported\n");//低四位為量化表IDif (qi > 4)snprintf(error_string, sizeof(error_string), "No more 4 quantization table is supported (got %d)\n", qi); #endiftable = priv->Q_tables[qi]; //量化表元素數據build_quantization_table(table, stream);/* 以之字形掃描順序輸出量化矩陣 */FILE* fp = fopen("table.txt", "a");if (fp == NULL){printf("Fail to open file!\n");exit(0);}fprintf(fp, "量化表\n");for (int i = 0; i < 64; i++){if (i != 0 && i % 8 == 0)fprintf(fp, "\n");fprintf(fp, "%f\t ", table[i]);}printf(fp, "\n");stream += 64;}#if TRACEfprintf(p_trace, "< DQT marker\n");fflush(p_trace); #endifreturn 0;}

③ SOF段操作函數

/* 解析SOF0(FFC0),得到圖像基本數據 */ 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 TRACEfprintf(p_trace, "> SOF marker\n");fflush(p_trace); #endifprint_SOF(stream);//不包含FFC0頭情況下,第三四字節為圖像高度,第五六字節為圖像寬度,后面緊跟一字節為顏色分量數(03)height = be16_to_cpu(stream + 3);width = be16_to_cpu(stream + 5);nr_components = stream[7];#if SANITY_CHECKif (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); #endifstream += 8; //指針繼續向后走for (i = 0; i < nr_components; i++) {cid = *stream++; //顏色分量IDsampling_factor = *stream++; //水平和垂直采樣因子Q_table = *stream++; //顏色分量的量化表序號c = &priv->component_infos[i]; //指向該分量的結構體指針#if SANITY_CHECKc->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); #endifc->Vfactor = sampling_factor & 0xf; //取低四位為垂直采樣因子c->Hfactor = sampling_factor >> 4; //取高四位為水平采樣因子c->Q_table = priv->Q_tables[Q_table]; //以量化表序號獲取量化表#if TRACEfprintf(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 TRACEfprintf(p_trace, "< SOF marker\n");fflush(p_trace); #endifreturn 0;}

④ DHT段操作函數
?通過解析DHT數據段,來得到霍夫曼碼表信息,同時加入代碼輸出所有霍夫曼碼表。

/* DHT數據解析(0xFFC4,一般為兩個),存儲霍夫曼碼表 */ static int parse_DHT(struct jdec_private* priv, const unsigned char* stream){unsigned int count, i;unsigned char huff_bits[17]; //碼長上限為16,此數組存儲特定碼長的碼字個數int length, index;length = be16_to_cpu(stream) - 2; //除去2字節標記代碼后得到的表長stream += 2; /* Skip length */#if TRACEfprintf(p_trace, "> DHT marker (length=%d)\n", length);fflush(p_trace); #endif/* 在表不為空的情況下讀取 */while (length > 0) {index = *stream++;huff_bits[0] = 0;count = 0;for (i = 1; i < 17; i++) {huff_bits[i] = *stream++; //統計特定碼長的碼字個數count += huff_bits[i]; // count個字節對應的就是每個字符對應的權值}#if SANITY_CHECKif (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 TRACEfprintf(p_trace, "Huffman table %s[%d] length=%d\n", (index & 0xf0) ? "AC" : "DC", index & 0xf, count);fflush(p_trace); #endif#endif/* 添加:以txt文件輸出所有的HUFFMAN碼表 */FILE* fp = fopen("huffman.txt", "a");if (fp == NULL){printf("Fail to open file!\n");exit(0); //退出程序(結束程序)}// 高四位為1是交流分量表if (index & 0xf0){fprintf(fp, "huffman_table AC%d號表\n", (index & 0xf));build_huffman_table(huff_bits, stream, &priv->HTAC[index & 0xf]); //建立霍夫曼表}else{fprintf(fp, "huffman_table DC%d號表\n", (index & 0xf));build_huffman_table(huff_bits, stream, &priv->HTDC[index & 0xf]);}length -= 1;length -= 16;length -= count;stream += count; //向后繼續推進,直到length為0}#if TRACEfprintf(p_trace, "< DHT marker\n");fflush(p_trace); #endifreturn 0;}

⑤ SOS段操作函數

/* 解析SOS(0xFFDA),以得到顏色分量的霍夫曼表序號(對應DHT中的序號數據) */ static int parse_SOS(struct jdec_private* priv, const unsigned char* stream){unsigned int i, cid, table;unsigned int nr_components = stream[2]; //讀取顏色分量數(僅支持3)#if TRACEfprintf(p_trace, "> SOS marker\n");fflush(p_trace); #endif#if SANITY_CHECKif (nr_components != 3)snprintf(error_string, sizeof(error_string), "We only support YCbCr image\n"); #endifstream += 3;for (i = 0; i < nr_components; i++) { //對每個顏色進行表查找和處理cid = *stream++; //表IDtable = *stream++; //表數據#if SANITY_CHECKif ((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 TRACEfprintf(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]; //低四位為ACpriv->component_infos[i].DC_table = &priv->HTDC[table >> 4]; //高四位為DC}priv->stream = stream + 3;#if TRACEfprintf(p_trace, "< SOS marker\n");fflush(p_trace); #endifreturn 0;}

⑥ 對讀取結構體數據進行處理
?在此函數中,對每個塊的水平和垂直采樣情況進行解析,計算MCU。同時通過調用不同的MCU讀取和處理函數進行數據處理。
?同時在此函數中添加代碼以實現輸出原JPEG圖像的直流分量圖像和其中一個交流分量圖像。

//主要解碼過程 int tinyjpeg_decode(struct jdec_private* priv, int pixfmt){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;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 TRACEfprintf(p_trace, "Bad pixel format\n");fflush(p_trace); #endifreturn -1;}/* 添加:分別輸出DC分量圖像和AC分享圖像 */FILE* DC_image = fopen("DCimage.yuv", "wb");FILE* AC_image = fopen("ACimage.yuv", "wb");priv->dc = (int*)malloc(sizeof(int) * (priv->width * priv->height) / 64); // 每個8x8的塊僅有一個直流分量priv->ac = (int*)malloc(sizeof(int) * (priv->width * priv->height) / 64);// 因為只輸出某一AC值圖像,所以AC和DC所占空間相同xstride_by_mcu = ystride_by_mcu = 8; //初始化為4:4:4,即MCU寬高比為1:1,均為8像素if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) {//Y分量垂直和水平的采樣因子相等(4:4:4),每個MCU僅包含一個Y分量,且MCU的寬和高不變decode_MCU = decode_mcu_table[0];convert_to_pixfmt = colorspace_array_conv[0];#if TRACEfprintf(p_trace, "Use decode 1x1 sampling\n");fflush(p_trace); #endif}else if (priv->component_infos[cY].Hfactor == 1) {//水平采樣為1,垂直采樣為2(4:2:2),每個MCU包含2個Y分量,且MCU的高需要增加一倍decode_MCU = decode_mcu_table[1];convert_to_pixfmt = colorspace_array_conv[1];ystride_by_mcu = 16;#if TRACEfprintf(p_trace, "Use decode 1x2 sampling (not supported)\n");fflush(p_trace); #endif}else if (priv->component_infos[cY].Vfactor == 2) {//水平采樣和垂直采樣均為2(4:2:0或4:1:1),每個MCU包含4個Y分量,且MCU的寬和高都需要增加一倍decode_MCU = decode_mcu_table[3];convert_to_pixfmt = colorspace_array_conv[3];xstride_by_mcu = 16;ystride_by_mcu = 16;#if TRACE fprintf(p_trace, "Use decode 2x2 sampling\n");fflush(p_trace); #endif}else {//水平采樣和垂直采樣均為2(另一種4:2:2),每個MCU包含4個Y分量,且MCU的寬需要增加一倍decode_MCU = decode_mcu_table[2];convert_to_pixfmt = colorspace_array_conv[2];xstride_by_mcu = 16;#if TRACEfprintf(p_trace, "Use decode 2x1 sampling\n");fflush(p_trace); #endif}resync(priv);/* Don't forget to that block can be either 8 or 16 lines */bytes_per_blocklines[0] *= ystride_by_mcu;bytes_per_blocklines[1] *= ystride_by_mcu;bytes_per_blocklines[2] *= ystride_by_mcu;bytes_per_mcu[0] *= xstride_by_mcu / 8;bytes_per_mcu[1] *= xstride_by_mcu / 8;bytes_per_mcu[2] *= xstride_by_mcu / 8;/* Just the decode the image by macroblock (size is 8x8, 8x16, or 16x16) */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){// 每解碼一個8x8的宏塊,將支流系數(DCT[0])和交流系數(DCT[1])分別賦值給定義的dc和ac參數int i = 0;if (i < priv->width * priv->height / 64){priv->dc[i] = priv->component_infos[0].DCT[0];priv->ac[i] = priv->component_infos[0].DCT[1];}i++;decode_MCU(priv); //進行MCU內的解碼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); //清空顏色分量if (find_next_rst_marker(priv) < 0) //查找RST標記return -1;}}}} #if TRACEfprintf(p_trace, "Input file size: %d\n", priv->stream_length + 2);fprintf(p_trace, "Input bytes actually read: %d\n", priv->stream - priv->stream_begin + 2);fflush(p_trace); #endif/* 根據DCT的能量守恒特性,反DCT之后的DC取值最大可達到8*256,且反差分編碼后數據可能為負數。因此需要對得到的直流和交流系數進行處理,使其值分布在[0,255]之間*/priv->outdc = (unsigned char*)malloc(sizeof(unsigned char) * (priv->width * priv->height) / 64);priv->outac = (unsigned char*)malloc(sizeof(unsigned char) * (priv->width * priv->height) / 64);int dcmax = priv->dc[0];int acmax = priv->ac[0];int dcmin = priv->dc[0];int acmin = priv->ac[0];for (int j = 0; j < priv->width * priv->height / 64; j++){if (priv->dc[j] > dcmax)dcmax = priv->dc[j];if (priv->dc[j] < dcmin)dcmin = priv->dc[j];if (priv->ac[j] > acmax)acmax = priv->ac[j];if (priv->ac[j] < acmin)acmin = priv->ac[j];}// 數據范圍處理for (int k = 0; k < priv->width * priv->height / 64; k++){priv->outdc[k] = (unsigned char)(255 * (priv->dc[k] - dcmin) / (dcmax - dcmin));priv->outac[k] = (unsigned char)(255 * (priv->ac[k] - acmin) / (acmax - acmin));}fwrite(priv->outdc, 1, priv->width * priv->height / 64, DC_image);fwrite(priv->outac, 1, priv->width * priv->height / 64, AC_image);return 0;}

三、實驗結果

?對工程進行生成和運行,可得到實驗輸出結果:

1、YUV圖像

2、量化表

?實驗中向txt文件中對JPEG文件的兩個量化表進行了輸出。

3、霍夫曼碼表

?實驗中向txt文件中對JPEG文件的所有霍夫曼碼表進行了輸出。
?其中包括:

  • 直流碼表0:
  • 直流碼表1:
  • 交流碼表0
  • 交流碼表1

    ?同時在huffman文件中存儲了所有碼表的完整信息。

4、分量圖像

?實驗中輸出了原圖像中的直流分量圖像和交流分量圖像,并對兩圖像的概率密度分布進行了繪圖。

直流分量交流分量
輸出圖像
概率分布

總結

以上是生活随笔為你收集整理的JPEG 原理分析及 JPEG 解码器的调试的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。