一:實驗原理
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編碼。
(5)AC係數的之字形掃描
由於經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開始。
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
標識符: 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
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定義了第二個量化表,這裏不再贅述。
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
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
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
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
顏色分量 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
譜選擇結束 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中序號對應)
得到量化表長度(可能包含多張量化表)
得到量化表的精度
得到及檢查量化表的序號(只能是 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 個數據)
得到量化表內容( 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 中序號對應)。
解析DHT的函數 parse_DHT:得到 Huffman 表的類型( AC、 DC)、序號
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.附加:文字噪聲
蚊子噪聲的來歷:銳利的高頻截止
表現爲下列現象:
壓縮率 | 圖像細節 |
低 | |
中 | |
高 | |
最高 |