【原創】Libjpeg 庫使用心得(一) JPEG圖像DCT系數的獲取和訪問


【原創】繼續我的項目研究,現在采用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_rowfirst_undef_row,最內層的范圍是blocksperrow。就是最多訪問rows_in_array行,即最多訪問圖片高度數的行數,但是內存中可能無法一次存下整張圖片,因此還有內存中存儲的行數,即rows_in_mem,或者是從cur_start_rowfirst_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系數也都全部訪問結束。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM