1. 程式人生 > >libjpeg-turbo解壓與壓縮JPEG影象原理

libjpeg-turbo解壓與壓縮JPEG影象原理

1  解壓縮操作過程

1.        為JPEG物件分配空間並初始化

2.        指定解壓縮資料來源

3.        獲取檔案資訊

4.        為解壓縮設定引數,包括影象大小,顏色空間

5.        開始解壓縮

6.        取出資料

7.        解壓縮完畢

8.        釋放資源

為JPEG物件分配空間並初始化

解壓縮過程中使用的JPEG物件是一個jpeg_decompress_struct的結構體。同時還需要定義一個用於錯誤處理的結構體物件,IJG中標準的錯誤結構體是jpeg_error_mgr。

     struct jpeg_decompress_struct cinfo;

     struct jpeg_error_mgr jerr;

       然後是將錯誤處理結構物件繫結在JPEG物件上。

       cinfo.err = jpeg_std_error(&jerr);

這個標準的錯誤處理結構將使程式在出現錯誤時呼叫exit()退出程式,如果不希望使用標準的錯誤處理方式,則可以通過自定義退出函式的方法自定義錯誤處理結構,詳情見文章後面的專門章節。

     初始化cinfo結構。

     jpeg_create_decompress(&cinfo);

     指定解壓縮資料來源

利用標準C中的檔案指標傳遞要開啟的jpg檔案。

     FILE * infile;

     if ((infile = fopen("sample.jpg", "rb")) == NULL)

     {

         return 0;

     }

     jpeg_stdio_src(&cinfo, infile);

     獲取檔案資訊

    IJG將影象的預設資訊填充到cinfo結構中以便程式使用。

     (void) jpeg_read_header(&cinfo, TRUE);

此時,常見的可用資訊包括影象的寬cinfo.image_width,高cinfo.image_height,色彩空間cinfo.jpeg_color_space,顏色通道數cinfo.num_components等。

     為解壓縮設定引數

在完成jpeg_read_header呼叫後,開始解壓縮之前就可以進行解壓縮引數的設定,也就是為cinfo結構的成員賦值。

比如可以設定解出來的影象的大小,也就是與原圖的比例。使用scale_num和scale_denom兩個引數,解出來的影象大小就是scale_num/scale_denom,但是IJG當前僅支援1/1, 1/2, 1/4,和1/8這幾種縮小比例。

比如要取得1/2原圖的影象,需要如下設定:

     cinfo.scale_num=1;

     cinfo.scale_denom=2;

也可以設定輸出影象的色彩空間,即cinfo.out_color_space,可以把一個原本彩色的影象由真彩色JCS_RGB變為灰度JCS_GRAYSCALE。如:

     cinfo.out_color_space=JCS_GRAYSCALE;

     開始解壓縮

根據設定的解壓縮引數進行影象解壓縮操作。

     (void) jpeg_start_decompress(&cinfo);

在完成解壓縮操作後,IJG就會將解壓後的影象資訊填充至cinfo結構中。比如,輸出影象寬度cinfo.output_width,輸出影象高度cinfo.output_height,每個畫素中的顏色通道數cinfo.output_components(比如灰度為1,全綵色為3)等。

一般情況下,這些引數是在jpeg_start_decompress後才被填充到cinfo中的,如果希望在呼叫jpeg_start_decompress之前就獲得這些引數,可以通過呼叫jpeg_calc_output_dimensions()的方法來實現。

      取出資料

解開的資料是按照行取出的,資料畫素按照scanline來儲存,scanline是從左到右,從上到下的順序,每個畫素對應的各顏色或灰度通道資料是依次儲存,比如一個24-bitRGB真彩色的影象中,一個scanline中的資料儲存模式是R,G,B,R,G,B,R,G,B,...,每條scanline是一個JSAMPLE型別的陣列,一般來說就是unsigned char,定義於jmorecfg.h中。

除了JSAMPLE,IJG還定義了JSAMPROW和JSAMPARRAY,分別表示一行JSAMPLE和一個2D的JSAMPLE陣列。

在此,我們定義一個JSAMPARRAY型別的緩衝區變數來存放影象資料。

     JSAMPARRAY buffer;                                                  //  JSAMPARRAY 等價於 unsigned char * *  型別, 在jpeglib.h中定義

然後是計算每行需要的空間大小,比如RGB影象就是寬度×3,灰度圖就是寬度×1

     row_stride = cinfo.output_width * cinfo.output_components;

為緩衝區分配空間,這裡使用了IJG的記憶體管理器來完成分配。

JPOOL_IMAGE表示分配的記憶體空間將在呼叫jpeg_finish_compress,jpeg_finish_decompress,jpeg_abort後被釋放,而如果此引數改為JPOOL_PERMANENT則表示記憶體將一直到JPEG物件被銷燬時才被釋放。

row_stride如上所說,是每行資料的實際大小。

最後一個引數是要分配多少行資料。此處只分配了一行。

     buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

output_scanline表示當前已經讀取的行數,如此即可依次讀出影象的所有資料,並填充到緩衝區中,引數1表示的是每次讀取的行數。

     while (cinfo.output_scanline < cinfo.output_height)

     {

         (void) jpeg_read_scanlines(&cinfo, buffer, 1);

         //do something

     }

    解壓縮完畢

     (void) jpeg_finish_decompress(&cinfo);

     釋放資源

     jpeg_destroy_decompress(&cinfo);

     fclose(infile);

     退出程式

如果不再需要JPEG物件,則使用

     jpeg_destroy_decompress(&cinfo);

     jpeg_destroy(&cinfo);

而如果還希望繼續使用JPEG物件,則可使用

     jpeg_abort_decompress(&cinfo);

     jpeg_abort(&cinfo);

1.1  完整例程

       //變數定義

     struct jpeg_decompress_struct cinfo;

     struct jpeg_error_mgr jerr;

     FILE * infile;

     JSAMPARRAY buffer;

     int row_stride;        

     //繫結標準錯誤處理結構

     cinfo.err = jpeg_std_error(&jerr);  

     //初始化JPEG物件

     jpeg_create_decompress(&cinfo);

     //指定影象檔案

     if ((infile = fopen("sample.jpg", "rb")) == NULL)

     {

         return;

     }

     jpeg_stdio_src(&cinfo, infile);

     //讀取影象資訊

     (void) jpeg_read_header(&cinfo, TRUE);

     //設定解壓縮引數,此處我們將影象長寬縮小為原圖的1/2

     cinfo.scale_num=1;

     cinfo.scale_denom=2;

     //開始解壓縮影象

     (void) jpeg_start_decompress(&cinfo);

     //本程式功能是應用GDI+在客戶區繪製圖像

     CClientDC dc(this);

     Bitmap bm( cinfo.output_width , cinfo.output_height); 

     Graphics graphics(dc.GetSafeHdc());

     Graphics gdc(&bm);

     //分配緩衝區空間

     row_stride = cinfo.output_width * cinfo.output_components;

     buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

     //讀取資料

     while (cinfo.output_scanline < cinfo.output_height)

     {

         (void) jpeg_read_scanlines(&cinfo, buffer, 1);

         //output_scanline是從1開始,所以需要減1

         int line=cinfo.output_scanline-1;

         for(int i=0;i<cinfo.output_width;i++)

         {

     //繪製點陣圖,本例中假設讀取的sample.jpg影象為RGB真彩色影象

     //因此,實際上cinfo.output_components就等於3,灰度圖則需另作處理

     bm.SetPixel(i,line,Color(255,(BYTE)buffer[0][i*3],(BYTE)buffer[0][i*3+1],(BYTE)buffer[0][i*3+2]));

         }

     }

     //結束解壓縮操作

     (void) jpeg_finish_decompress(&cinfo);

     //釋放資源

     jpeg_destroy_decompress(&cinfo);

     fclose(infile);

     //在客戶區繪製點陣圖

     graphics.DrawImage(&bm,0,0);

2 壓縮操作過程

1.         為JPEG物件分配空間並初始化

2.         指定影象輸出目標

3.         為壓縮設定引數,包括影象大小,顏色空間

4.        開始壓縮

5.        寫入資料

6.        壓縮完畢

7.        釋放資源

     為JPEG物件分配空間並初始化

壓縮過程中使用的JPEG物件是一個jpeg_compress_struct的結構體。同時還需要定義一個用於錯誤處理的結構體物件,IJG中標準的錯誤結構體是jpeg_error_mgr。

     struct jpeg_compress_struct cinfo;

     struct jpeg_error_mgr jerr;

然後是將錯誤處理結構物件繫結在JPEG物件上。

       cinfo.err = jpeg_std_error(&jerr);

這個標準的錯誤處理結構將使程式在出現錯誤時呼叫exit()退出程式,如果不希望使用標準的錯誤處理方式,則可以通過自定義退出函式的方法自定義錯誤處理結構,詳情見文章後面的專門章節。

     初始化cinfo結構。

     jpeg_create_compress(&cinfo);

     指定影象輸出目標

利用標準C中的檔案指標傳遞要輸出的jpg檔案。

     FILE * outfile;   

     if ((outfile = fopen(filename, "wb")) == NULL)

     {

         return 0;

     }

     jpeg_stdio_dest(&cinfo, outfile);

     為壓縮設定引數

在開始壓縮資料之前需要為壓縮指定幾個引數和預設引數。

設定預設引數之前需要指定的幾個引數是:影象寬度cinfo.image_width,影象高度cinfo.image_height,影象的顏色通道數cinfo.input_components(比如RGB影象為3,灰度圖為1),影象顏色空間cinfo.in_color_space(比如真彩色JCS_RGB,灰度圖JCS_GRAYSCALE)。

如:

     cinfo.image_width = 800;

     cinfo.image_height = 600;

     cinfo.input_components = 3;

     cinfo.in_color_space = JCS_RGB;

然後是設定預設設定

     jpeg_set_defaults(&cinfo);

注意此處,在set default之前,必須設定in_color_space,因為某些預設引數的設定需要正確的color space值。

在此之後還可以對其他的一些引數進行設定。具體有哪些引數可以查詢libjpeg.doc文件。

比如最常用的一個引數就是壓縮比。

jpeg_set_quality(&cinfo, quality, TRUE);

quality是個0~100之間的整數,表示壓縮比率。

      開始壓縮

根據設定的壓縮引數進行影象壓縮操作。

     jpeg_start_compress(&cinfo, TRUE);

開始壓縮過程後就不可以修改cinfo物件引數。

      寫入資料

     row_stride = image_width * 3;    //假設用到的圖示RGB真彩色三通道

同上文介紹的解壓縮操作中介紹的,要寫入的資料是按照行寫入的,資料畫素按照scanline來儲存,與讀取資料的不同是使用jpeg_write_scanlines。

類似於解壓縮操作中的cinfo.output_scanline < cinfo.output_height機制,壓縮過程使用的cinfo.next_scanline < cinfo.image_height來判斷是否完成寫入資料。

在此,假設image_buffer是個JSAMPARRAY型別變數,其中儲存的是要輸出的影象資料,比如可以是用上文中的解壓縮操作從某JPEG檔案中獲得的資料。

     JSAMPROW row_pointer;

     while (cinfo.next_scanline < cinfo.image_height)

     {

         //找到影象中的某一行,寫入目標檔案

         row_pointer = image_buffer[cinfo.next_scanline];

         (void) jpeg_write_scanlines(&cinfo, &row_pointer, 1);

     }

      壓縮完畢

     jpeg_finish_compress(&cinfo);

     釋放資源

     fclose(outfile);

     jpeg_destroy_compress(&cinfo);

     退出程式

如果不再需要JPEG物件,則使用

     jpeg_destroy_compress(&cinfo);

     jpeg_destroy(&cinfo);

而如果還希望繼續使用JPEG物件,則可使用

     jpeg_abort_compress(&cinfo);

     jpeg_abort(&cinfo);

2.1  完整例程

       //變數定義

     struct jpeg_compress_struct cinfo;

     struct jpeg_error_mgr jerr;

     FILE * outfile;       

     JSAMPROW row_pointer; 

     int row_stride;       

     //繫結標準錯誤處理結構

     cinfo.err = jpeg_std_error(&jerr);

     //初始化JPEG物件

     jpeg_create_compress(&cinfo);

     //指定目標影象檔案

     if ((outfile = fopen("dest.jpg", "wb")) == NULL)

     {

         return;

     }

     jpeg_stdio_dest(&cinfo, outfile);

     //設定壓縮引數

     cinfo.image_width = image_width;

     cinfo.image_height = image_height;

     cinfo.input_components = 3;

     cinfo.in_color_space = JCS_RGB;

     jpeg_set_defaults(&cinfo);

     //此處設壓縮比為90%

     jpeg_set_quality(&cinfo, 90, TRUE);

     //開始壓縮

     jpeg_start_compress(&cinfo, TRUE);

     //假設使用的是RGB影象

     row_stride = image_width * 3;   

     //寫入資料

     while (cinfo.next_scanline < cinfo.image_height)

     {

         row_pointer = image_buffer[cinfo.next_scanline];

         (void) jpeg_write_scanlines(&cinfo, &row_pointer, 1);

     }

     //壓縮完畢

     jpeg_finish_compress(&cinfo);

     //釋放資源

     fclose(outfile);

     jpeg_destroy_compress(&cinfo);

3  錯誤處理

在使用預設錯誤處理結構jpeg_error_mgr的情況下,程式在遇到錯誤後將呼叫exit直接退出程式,使用者如果不希望使用這種直接退出的方式處理錯誤的話就需要自定義錯誤處理結構。

依照example.c中的例子,IJG推薦使用C語言的setjmp和longjmp機制來重寫錯誤處理結構。

首先,需要定義一個包含標準錯誤處理結構型別變數的自定義結構。

同時,程式將需要引入標頭檔案setjmp.h。

#include <setjmp.h>

     struct my_error_mgr

     {

         struct jpeg_error_mgr pub;

         jmp_buf setjmp_buffer;

     };

     typedef struct my_error_mgr * my_error_ptr;

以及一個錯誤處理函式。在出現錯誤時程式將跳轉到本函式中,而本函式將跳轉到setjmp設定的程式位置。

     METHODDEF(void) my_error_exit (j_common_ptr cinfo)

     {

         my_error_ptr myerr = (my_error_ptr) cinfo->err;

         (*cinfo->err->output_message) (cinfo);

         longjmp(myerr->setjmp_buffer, 1);

     }

以解壓縮過程為例,原程式將被修改為如下形式。

     struct jpeg_decompress_struct cinfo;

     struct jpeg_error_mgr jerr;

     //此處做了修改    

     //struct jpeg_error_mgr jerr;   

     struct my_error_mgr jerr;

     ////////////////////////////////////////////////////

     FILE * infile;

     JSAMPARRAY buffer;

     int row_stride;       

     //此處做了修改    

     //cinfo.err = jpeg_std_error(&jerr);

     cinfo.err = jpeg_std_error(&jerr.pub);

     jerr.pub.error_exit = my_error_exit;

     if (setjmp(jerr.setjmp_buffer))

     {

     //在正常情況下,setjmp將返回0,而如果程式出現錯誤,即呼叫my_error_exit

     //然後程式將再次跳轉於此,同時setjmp將返回在my_error_exit中由longjmp第二個引數設定的值1

     //並執行以下程式碼

         jpeg_destroy_decompress(&cinfo);

         fclose(infile);

         return;

     }

     ////////////////////////////////////////////////////

     jpeg_create_decompress(&cinfo);

     if ((infile = fopen("sample.jpg", "rb")) == NULL)

     {

         return;

     }

     //以下的程式碼與上文解壓縮操作章節中相同,不再贅述

相關推薦

libjpeg-turbo壓縮JPEG影象原理

1  解壓縮操作過程 1.        為JPEG物件分配空間並初始化 2.        指定解壓縮資料來源 3.        獲取檔案資訊 4.        為解壓縮設定引數,包括影象大小,顏色空間 5.        開始解壓縮 6.        取出

Mac: Command: 壓縮

#rar 稍微麻煩一些,需要brew install一下 unrar e extracting-file 壓縮還沒找到 #zip zip -r archive_name.zip direct

Linux學習之壓縮

一、linux中常用的壓縮格式 .zip .gz .bz2 .tar.gz .tar.bz2 二、.zip 壓縮格式 zip 壓縮名 原始檔名 :壓縮檔案 zip -r 壓縮名 源目錄 : 壓縮目錄 例如:zip abc.zip abc 解壓: unzip 壓縮名 三、

C++ zip檔案的壓縮

2、在專案中新增相關檔案 在Debug中新增zlib1.dll檔案,其他檔案載入到專案中 3、實現的程式碼 #pragma comment(lib,"zlib1") //壓縮檔案 int Compress(char * DestName, const char *SrcN

Linux:檔案壓縮

檔案打包與壓縮 常見壓縮檔案格式: |檔案字尾名 |說明| |.zip |zip程式打包壓縮的檔案| |.rar |rar程式壓縮的檔案| |.7z |7zip程式壓縮的檔案| |.tar |tar程式打包,未壓縮的檔案| |.gz |gzip程式(GNU zip)壓縮的檔案| |.xz |xz程式壓縮的檔案

C# 使用libjpeg-turbo圖片的程式碼

1、下載: 2、使用方法: (1)tjInitDecompress 獲取解壓控制代碼 (2)tjDecompressHeader3 獲取jpg圖片資訊 (3)分配記憶體以便放置圖片 (4)tjDecompress2 解壓圖片 (5)tjDestroy 釋放控制

壓縮ramdisk.img檔案,生成uramdisk.img檔案

1. 在myandroid/out/target/product/imx51_bbg目錄下可以找到要解壓的ramdisk.img檔案。       通過file命令可以檢視ramdisk.img檔案的型別:       [email protected]

指令篇:文件文件系統的壓縮打包(歸檔)___gzip、zcat;bzip2、bzcat;zip; tar

roo 壓縮命令 過時 bsp 演示 組合 localhost 打包 mov 一、文件與文件系統的壓縮:   1、單文件的四種壓縮命令:     ①、Compress  (過時了,不做過多說明)     ②、gzip,zcat   (gzip:壓縮。zcat:查看)    

python壓縮,以及存數據庫的相關操作

alt zipfile hide end tdi hid write port pat zipfile實現壓縮整個目錄和子目錄 import os,shutil,zipfile,glob def dfs_get_zip_file(input_path,result):

Java zip壓縮

ice gin inpu ret zipentry .get get next exce 因為最近項目需要批量上傳文件,而這裏的批量就是將文件壓縮在了一個zip包裏,然後讀取文件進行解析文件裏的內容。 因此需要先對上傳的zip包進行解壓。以下直接提供代碼供參考: 1.第一個

python程序rar壓縮包報錯

ould gcc-c++ root 產生 pytho oot exp import 依賴包 運行如下python程序報錯Couldn't find path to unrar library的解決辦法:#!/usr/bin/python #-*- coding:ut

DoNetZip類庫壓縮文件

tel direct cep ima tor pre style div cat using Ionic.Zip; public class ZipHelper { public static void ZipSingleFile(string

C++zip壓縮包(2)

由於上一篇部落格寫的比較急,沒有給出直接執行的程式碼跟演示,這裡我重新寫了一個專案 上一篇部落格C++解壓壓縮檔案 上一篇部落格中有ziputils官網的連結更下載地址,使用方法還是一樣,向專案中新增檔案 #include <Windows.h> //新

C++zip壓縮檔案

前言 最近做專案需要用到網路下載壓縮檔案並解壓至指定資料夾,本意是使用zlib庫,但是花費許久時間仍沒有編譯通過官網的檔案,於是放棄,轉而尋求其他方法,在之前的部落格中有說道用system呼叫shell命令的方式使用winrar解壓,但是這種方法有一個弊端就是要求客戶端必須安裝winrar,

Hadoop--使用JavaAPI對檔案壓縮

GitHub:https://github.com/GYT0313/Hadoop-JavaAPI-Code/tree/master/chapter5/src/org/gyt/hadoopCompression 前言 本次示例做了如下工作: 將 bzip2 格式解

Mac上在終端上解壓縮

需要 unrar 示例 ast mac search pro download bre 1、安裝rar 1.brew install unrar 2.unrar -version 3.進入需要解壓的文件目錄下,unrar x 文件夾名.rar 1.tar -xvf [fil

python zip壓縮

  在當前路徑解壓zip壓縮包,生成同名資料夾,內部目錄結構與壓縮包一致 import zipfile import os def un_zip(file_name): """unzip zip file""" zip_file = zipfile.ZipFile(

Android Zip 壓縮檔案

文章目錄 1、簡介 2、程式碼結構 3、AndroidManifest.xml 新增 sd 卡讀寫許可權 4、MainActivity 檔案 5、效果檢視

android全平臺編譯libjpeg-turbo並基於ANativeWindow載入JPEG圖片

圖形影象實踐 android全平臺編譯libjpeg-turbo並基於ANativeWindow載入JPEG圖片 android全平臺編譯libpng並基於ANativeWindow載入PNG圖片 概述 libjpeg - turbo是一個JPEG影象編解

使用微軟自帶壓縮資料夾

前言 因為即時通訊專案中,需要同步OA中的使用者頭像,使用者頭像是通過資料夾儲存的,資料夾內結構比較複雜。在即時通訊中需要先將OA伺服器上儲存的使用者頭像資料夾下載下來,因為直接下載資料夾方法很難,所以需要先將資料夾壓縮一下,然後在直接下載壓縮檔案。 他人雅慧 在網上找了不少了例子,幾乎都是使用SharpZ