【原創】繼續我的項目研究,現在采用Libjpeg庫函數來進行處理,看了庫函數之后發現C語言被這些人用的太牛了,五體投地啊。。。廢話不多說,下面就進入正題。
Libjpeg庫在網上下載還是挺方便的,這里就不附上來了,當然如果找不到的話,也可以發郵件給我,我的郵箱是gungnir2011@gmail.com。
打開庫函數會看到有很多很多的文件,里面有兩個解決方案,一個是apps,一個是jpeg。apps里面有5個工程,分別是用於壓縮,解壓,轉換,讀取JPEG中COM段,寫入JPEG中COM段,COM段可以看做是JPEG中的注釋。要想獲取到DCT系數,比較好用的工程是壓縮,解壓和轉換,要是想直接可以操作DCT系數的話還是使用轉換的那個工程比較好,即jpegtran。
這里就需要提到庫中對於DCT系數操作的函數,主要用上的就是jpeg_read_coefficients()和jpeg_write_coefficients()函數。這是兩個非常好用的函數,從名字就可以看出,分別是讀取DCT系數和寫入DCT系數。因為jpegtran工程中可以提供無損轉換,因此就用上了直接對於DCT系數操作的函數。這兩個函數的源碼就不貼了,在jpeg的解決方案中全方案查找一下就能找到源碼,這里也不需要關注他內部如何實現的,只要知道需要什么參數,返回什么就可以了。還有一點要補充的是,讀取出的DCT系數是量化后的DCT系數。
1 jvirt_barray_ptr * jpeg_read_coefficients (j_decompress_ptr cinfo); 2 3 void jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays);
上面jvirt_barray_ptr *就是讀取DCT系數所返回的值的類型,我們再來看看這個類型是怎么定義的:
1 typedef struct jvirt_barray_control * jvirt_barray_ptr;
可以看出是指向一個結構體的指針,再來看看結構體是什么樣子:
1 struct jvirt_barray_control { 2 JBLOCKARRAY mem_buffer; /* => the in-memory buffer */ 指向內存中的內容的指針 3 JDIMENSION rows_in_array; /* total virtual array height */ 總的虛擬數組的高度,即圖片的高度 4 JDIMENSION blocksperrow; /* width of array (and of memory buffer) */ 數組的寬度,也就是圖片的寬度 5 JDIMENSION maxaccess; /* max rows accessed by access_virt_barray */ access_virt_barry所能訪問到的最多的行數,這里不太明白是用來做什么的 6 JDIMENSION rows_in_mem; /* height of memory buffer */ 在內存中內容的高度 7 JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ 這里不大清楚,我也沒有用上 8 JDIMENSION cur_start_row; /* first logical row # in the buffer */ 內存中內容的第一行的行號,一般都為0 9 JDIMENSION first_undef_row; /* row # of first uninitialized row */ 第一個沒有初始化的行的行號,可以看作是含數據的最后一行的后一行 10 boolean pre_zero; /* pre-zero mode requested? */ 預設0模式的判斷 11 boolean dirty; /* do current buffer contents need written? */ 現在的內容是否需要寫入 12 boolean b_s_open; /* is backing-store data valid? */ 不大清楚 13 jvirt_barray_ptr next; /* link to next virtual barray control block */ 下一個結構體的指針 14 backing_store_info b_s_info; /* System-dependent control info */ 不清楚 15 };
這下就明朗了,這里作者寫的很好,每個都給了注釋說明作用,后面的中文是我添加以便更好理解的。注釋中紅色突出的是需要用上的內容,mem_buffer是用來找到內存中的內容的指針,我們也是通過它來訪問DCT系數的,這個指針類型會在下面介紹訪問的時候再細說,這里先略過,next指針同樣略過。其他的內容主要是用來控制循環的范圍的,最外層的循環范圍可以是rows_in_array,再次內層的范圍是rows_in_mem或者從cur_start_row到first_undef_row,最內層的范圍是blocksperrow。就是最多訪問rows_in_array行,即最多訪問圖片高度數的行數,但是內存中可能無法一次存下整張圖片,因此還有內存中存儲的行數,即rows_in_mem,或者是從cur_start_row到first_undef_row,這兩個范圍都可以表示內存中存儲的行數,而每一行又有blocksperrow個block,一個block是一個8*8的矩陣。每個block中存儲的就是量化后的DCT系數,也就達到了獲取DCT系數的目的。
==================================================分割線=====================================================
上面說完了DCT系數的獲取,都已經能得到一個block了,就可以直接訪問了,為什么還要單獨拿出來說一下DCT系數的訪問。只能說是因為那些庫函數的作者太牛了,指針用的出神入化,當然更多的原因應該是我太菜了。。。求輕噴。。。在理解了他指針的使用之后其實挺簡單的,下面就來介紹介紹:
還記得上面提到的JBLOCKARRAY指針類型么,就是這個,我們就從這里開始,這是指向內存中內容的指針,也是我們訪問DCT系數的頭,我們先來看看它以及其他相關指針類型的定義:
1 typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ 一個系數的block 2 typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ 指向一行系數block的指針 3 typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ 一個系數block的2維數組指針
JCOEF就是short類型,這是頭文件里typedef的,就不貼出了。可以看出,JBLOCK其實是一個大小為DCTSIZE2的數組,DCTSIZE2就是64,同樣是頭文件里typedef的,而JBLOCKROW是一個指向JBLOCK型的指針,我們的頭JBLOCKARRAY是指向JBLOCKROW型的指針,也就是指向JBLOCK型的一個二級指針(不知道有沒有這個說法,是我自創的~)。
作者為什么要用這么麻煩的指針型呢,別急,我們再來看看注釋。JBLOCK是一個系數的block,這好理解,一個block有64個系數,JBLOCK是大小為64的short型數組,很明顯,每個元素就是一個系數。JBLOCKROW是指向一行系數block的指針,這什么意思呢?上面說到的那個結構體中很多成員變量都與行有關,因為庫函數中對於圖片是一行一行的處理的,我們可以把圖片看成由許多行組成的一個2維數組,每個數組元素是一個block,這樣就好理解多了,JBLOCKROW就是指向這個2維數組一行的一個指針,那么JBLOCKARRAY也容易理解了,就是一個指向這個2維數組的指針。
具體的結構關系請看下圖(圖片也是本人手繪的~):

也就是說在內存中的buffer其實保存的是一個個JBLOCKROW型的指針,每個JBLOCKROW型指針都指向了一行系數,每一行最多能訪問圖片的寬度,即blocksperrow個block,要不然再往后訪問的數據就不對了,同樣也只能訪問圖片的高度數那么多個的JBLOCKROW,否則也會訪問出錯!!
主要結構關系都已經說明白了,下面就直接貼上我的代碼,也可以更直觀的看到是如何訪問的,加深理解:
1 void func(jvirt_barray_ptr* coef_arrays) 2 { 3 jvirt_barray_ptr temp_src_coef_arrays; 5 temp_src_coef_arrays = *coef_arrays; 6 while (temp_src_coef_arrays) 7 { 8 JBLOCKARRAY jbarray = (temp_src_coef_arrays)->mem_buffer; 9 JBLOCKROW jbrow = NULL; 10 for (int i = 0; i < (temp_src_coef_arrays)->rows_in_mem; i++) 11 { 12 jbrow = *jbarray; 13 for (int j = 0; j < (temp_src_coef_arrays)->blocksperrow; j++) 14 { 15 for (int k = 1; k <64; k++) 16 { 17 if ((*(jbrow + j))[k] != 0) 18 { 19 ......20 } 21 } 22 } 23 jbarray++; 24 } 25 temp_src_coef_arrays = (temp_src_coef_arrays)->next; 26 }28 }
函數中的參數就是由jpeg_read_coefficients()函數返回的值,通過它找到訪問的頭,在訪問完一個block之后,JBLOCKROW型指針向后移動訪問下一個block,直到一行block訪問結束,跳出一行的循環,通過JBLOCKARRAY取到下一個JBLOCKROW型指針,繼續循環一行的訪問,直到所有的行都訪問結束,DCT系數也都全部訪問結束。
