在記憶體內進行 JPEG 壓縮

| | 0 Comments| 15:53
Categories:

一般要將圖片儲存成特定的檔案格式、進行壓縮的話,大部分都是直接寫到磁碟裡。不過,有的時候其實可能只是要進行壓縮,而不想寫到磁碟上(速度比較慢、而且還要再讀回來),這個時候,就必須要特別處理。

而 Independent JPEG Group 的 JPEG 函式庫(官網)目前就有提供 jpeg_mem_dest() 這個函式,可以再壓縮的時候,把資料寫到指定的記憶體空間,而不用寫到磁碟上。

如果要把相關功能包成一個函式的話,寫法大概就會像下面這樣:

std::pair<unsigned char*,unsigned long> CompressJPEG(
unsigned int uWidth, unsigned int uHeight,
unsigned char* pImg, int iQuality = 75 )
{
std::pair<unsigned char*,unsigned long> mRes
= std::make_pair( (unsigned char*)NULL, 0 );
 
// setup JPEG compression structure data
jpeg_compress_struct jcInfo;
jpeg_error_mgr jErr; // JPEG error handler
jcInfo.err = jpeg_std_error ( &jErr );
 
// initialize JPEG compression object
jpeg_create_compress( &jcInfo );
 
// specify data destination is memory
jpeg_mem_dest( &jcInfo, &mRes.first, &mRes.second );
 
// image format
jcInfo.image_width = uWidth;
jcInfo.image_height = uHeight;
jcInfo.input_components = 3;
jcInfo.in_color_space = JCS_RGB;
 
// set default compression parameters
jpeg_set_defaults( &jcInfo );
 
// set image quality
jpeg_set_quality( &jcInfo, iQuality, TRUE );
 
// start compressor
jpeg_start_compress( &jcInfo, TRUE );
int iRowStride = jcInfo.image_width * jcInfo.input_components;
while( jcInfo.next_scanline < jcInfo.image_height )  {
JSAMPROW pData = &( pImg[ jcInfo.next_scanline * iRowStride ] );
jpeg_write_scanlines( &jcInfo, &pData, 1 );
}
 
// finish compression
jpeg_finish_compress( &jcInfo );
 
// release JPEG compression object
jpeg_destroy_compress( &jcInfo );
 
return mRes;
}

這邊要輸入的參數基本上有四個,uWidthuHeight 是圖片的寬和高,pImg 則是圖片的內容(未壓縮的);最後的 iQuality 則是壓縮成 JPEG 時,所要設定的品質(0-100)。至於影像的像素格式,Heresy 這邊是寫死成三個 channel、也就是 RGB 的格式。

回傳的結果有兩種資料,這邊用 std::pair 來做簡單的包裝。裡面第一項的 unsigned char* 指到的記憶體空間,就是壓縮完的資料,而第二項的 unsigned long 數值,則是代表這個資料的長度。

而如果是要使用的話,假設有一個 unsigned char 的陣列 pImg,它的大小是 w x h 的話,那只要執行下方程式碼的第一行,就可以取得壓縮成 JPEG 的資料(pJpeg)了~

std::pair<unsigned char*,unsigned long> pJpeg = CompressJPEG( w, h, pImg, 100 );
ofstream of( "e:\\test.jpg", ios::binary );
of.write( (char*)pJpeg.first, pJpeg.second );
of.close();

而如果要驗證的話,最簡單的方法,還是把他寫出來、變成實際的檔案;這時候,只要透過 ofstrem,用 binary 的形式,整個寫出來就好了。

參考:《Compressing IplImage to JPEG using libjpeg in OpenCV

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *