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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

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

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

一、JPEG編碼原理


1.將RGB轉換為YUV空間。(相關性較小)
2.零偏置。(使最大的絕對值大的無符號數變為絕對值小的有符號數,減小數據平均的位數,例如0~255變為-128~127)
3.做8*8DCT變換。(區分DC與AC成分,便于后續處理)
4.細量化低頻粗量化高頻。(利用人眼對高頻不敏感的特性節省空間)
5.針對DC系數使用DPCM編碼。(相鄰變化幅度較小,適合DPCM)
6.針對AC系數使用Z型掃描隨后使用游程編碼。(壓縮掉大量的0)
7.對DC與AC分別做哈夫曼編碼。(減少空間)

JPEG解碼為編碼的逆過程,了解編碼方式方便解碼。

二、JPEG解碼原理

1.JPEG文件格式

2.整體分析

查閱loadjpeg.c中的主函數得知該程序先通過輸入的參數來決定輸出的格式,然后load_multiple_times函數多次使用tinyjpeg_parse_header函數讀取jpeg文件頭,tinyjpeg_parse_header使用parse_JFIF函數來解析每個標識符,依次解析SOF、DQT、SOS、DHT、DRI。

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

3.結構體功能

huffman_table結構體:
存儲快速查找表、碼字長度和慢速查找表。

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

component結構體:
存儲水平采樣因子、垂直采樣因子,前一個直流系數,dct變換后的值,哈夫曼表和量化表。

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_CHECKunsigned int cid; #endif };

jdec_private結構體:
存儲jpeg中一個塊的定義和信息

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

4.TRACE的功能

#if XXX ... #endif

該格式可根據if后面的條件是否為真來決定是否編譯#if與#endif中間的代碼,所以程序中的#if TRACE即為一部分代碼的開關,TRACE為1時執行那些代碼,輸出trace_jpeg.txt文件。
TRACE的定義在tinyjpeg.h中,將TRACE修改為0即可關閉部分代碼。

5.輸出量化矩陣

該程序使用build_quantization_table生成量化矩陣,在該函數下方添加輸出代碼即可。

#if TRACEconst unsigned char* outzigzag = zigzag;for (int i = 0; i < 8; i++) {for (int j = 0; j < 8; j++) {fprintf(p_trace, "%d ", ref_table[*outzigzag++]);}fprintf(p_trace, "\n");} #endif

輸出的量化矩陣:

6.輸出哈夫曼碼表

該程序使用build_huffman_table生成哈夫曼碼表。

輸出的哈夫曼碼表:

7.輸出解碼圖像

原程序將yuv分別輸出到了三個文件中,更改后將yuv均寫入同一個文件即可得到yuv文件。

查閱實驗所用jpg文件得知圖像分辨率為1024*1024,用該分辨率查看得到的yuv文件即可得到正確圖像。

8.輸出DC、AC圖像

由JPEG編碼過程得知DC分量經過DPCM與哈夫曼編碼后變為jpeg直流數據,解碼時jpeg數據經過哈夫曼解碼與dpcm解碼后得到DC分量。該程序中tinyjpeg_decode用于解碼,所以修改tinyjpeg_decode函數。

tinyjpeg_decode:

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;/*---------------here------------------*/FILE* out_DC;FILE* out_AC;out_DC = fopen("out_DC.yuv", "wb");out_AC = fopen("out_AC.yuv", "wb");unsigned char* out_DC_Buf;unsigned char* out_AC_Buf;/*---------------here------------------*/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;}xstride_by_mcu = ystride_by_mcu = 8;if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) {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) {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) {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 {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){decode_MCU(priv);/*-----------------------------------here-------------------------------------*/out_DC_Buf = (unsigned char)((priv->component_infos->DCT[0] + 512) / 4 + 0.5);out_AC_Buf = (unsigned char)(priv->component_infos->DCT[1] + 128);fwrite(&out_DC_Buf, 1, 1, out_DC);fwrite(&out_AC_Buf, 1, 1, out_AC);/*-----------------------------------here-------------------------------------*/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)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); #endifreturn 0; }

DC分量為原圖的88數據得到,原圖為10241024,所以得到的圖像為128*128。
因為只有y分量沒有uv分量,所以得到的是黑白圖像,使用Luminance Only來顯示僅有亮度分量的圖片。

AC與DC同理。

所的圖片:

9.DC、AC圖像概率分布

將out_DC.yuv與out_AC.yuv放到matlab運行目錄中,運行以下代碼:

所得結果:
(藍色為DC分量,橙色為AC分量)

DC分量較為分散,AC分量較集中,兩者均大致關于中心位置對稱。

總結

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

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