【Linux開發】jpeglib使用指南
您可以到www.ijg.org網站下載libjpeg的原始碼, IJG JPEG Library就是jpeg壓縮庫,是以原始碼的形式提供給軟體開發人員的,當然在軟體包裡也有編譯好的庫檔案,我們這裡就只用到其中的libjpeg.lib,jconfig.h,jmorecfg.h,jpeglib.h這幾個檔案,下面我就介紹一下怎樣在自己的程式裡嵌入影象壓縮功能。
一、建立編譯環境
所謂建立編譯環境,其實非常簡單,就是把上面提到的4個檔案拷貝到你的專案資料夾下,把libjpeg.lib新增到你的專案中,然後在你完成壓縮功能的那個檔案里加入#include "jpeglib.h",需要注意的是,libjpeg.lib是用c語言開發的,如果要用在你的C++程式裡,需要用到extern "C",如下:
// TestLibjpeg.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "memory.h"
extern "C" {
#include "jpeglib.h"
}
二、壓縮步驟
1、申請並初始化jpeg壓縮物件,同時要指定錯誤處理器
struct jpeg_compress_struct jcs;
// 宣告錯誤處理器,並賦值給jcs.err域
struct jpeg_error_mgr jem;
jcs.err = jpeg_std_error(&jem);
jpeg_create_compress(&jcs);
2、指定壓縮後的影象所存放的目標檔案,注意,目標檔案應以二進位制模式開啟
f=fopen("03.jpg","wb");
if (f==NULL)
{
delete [] data;
delete [] pDataConv;
return 0;
}
jpeg_stdio_dest(&jcs, f);
3、設定壓縮引數,主要引數有影象寬、高、色彩通道數(1:索引影象,3:其他),色彩空間(JCS_GRAYSCALE表示灰度圖,JCS_RGB表示彩色影象),壓縮質量等,如下:
jcs.image_width = nWidth; // 為圖的寬和高,單位為畫素
jcs.image_height = nHeight;
jcs.input_components = 1; // 在此為1,表示灰度圖, 如果是彩色點陣圖,則為3
jcs.in_color_space = JCS_GRAYSCALE; //JCS_GRAYSCALE表示灰度圖,JCS_RGB表示彩色影象
jpeg_set_defaults(&jcs);
jpeg_set_quality (&jcs, 80, true);
需要注意的是,jpeg_set_defaults函式一定要等設定好影象寬、高、色彩通道數計色彩空間四個引數後才能呼叫,因為這個函式要用到這四個值,呼叫jpeg_set_defaults函式後,jpeglib庫採用預設的設定對影象進行壓縮,如果需要改變設定,如壓縮質量,呼叫這個函式後,可以呼叫其它設定函式,如jpeg_set_quality函式。其實影象壓縮時有好多引數可以設定,但大部分我們都用不著設定,只需呼叫jpeg_set_defaults函式值為預設值即可。
4、上面的工作準備完成後,就可以壓縮了,壓縮過程非常簡單,首先呼叫jpeg_start_compress,然後可以對每一行進行壓縮,也可以對若干行進行壓縮,甚至可以對整個的影象進行一次壓縮,壓縮完成後,記得要呼叫jpeg_finish_compress函式,如下:
jpeg_start_compress(&jcs, TRUE);
JSAMPROW row_pointer[1]; // 一行點陣圖
int row_stride; // 每一行的位元組數
row_stride = jcs.image_width; // 如果不是索引圖,此處需要乘以3
// 對每一行進行壓縮
while (jcs.next_scanline < jcs.image_height) {
row_pointer[0] = & pDataConv[jcs.next_scanline * row_stride];
jpeg_write_scanlines(&jcs, row_pointer, 1);
}
jpeg_finish_compress(&jcs);
5、最後就是釋放壓縮工作過程中所申請的資源了,主要就是jpeg壓縮物件,由於在本例中我是直接用的區域性變數,所以只需呼叫jpeg_destroy_compress這個函式即可,如下:
jpeg_destroy_compress(&jcs);
三、解壓縮步驟
解壓縮步驟與壓縮步驟非常相似,只是解壓縮物件為jpeg_decompress_struct型別,步驟如下:
1、宣告並初始化解壓縮物件,同時制定錯誤資訊管理器
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
2、開啟jpg影象檔案,並指定為解壓縮物件的原始檔
FILE *f = fopen(strSourceFileName,"rb");
if (f==NULL)
{
printf("Open file error!/n");
return;
}
//
jpeg_stdio_src(&cinfo, f);
3、讀取影象資訊
jpeg_read_header(&cinfo, TRUE);
4、根據影象資訊申請一個影象緩衝區
data = new BYTE cinfo.image_width*cinfo.image_height*cinfo.num_components];
5、開始解壓縮
jpeg_start_decompress(&cinfo);
JSAMPROW row_pointer[1];
while (cinfo.output_scanline < cinfo.output_height)
{
row_pointer[0] = &data[(cinfo.output_height - cinfo.output_scanline-1)*cinfo.image_width*cinfo.num_components];
jpeg_read_scanlines(&cinfo,row_pointer ,
1);
}
jpeg_finish_decompress(&cinfo);
6、釋放資源
jpeg_destroy_decompress(&cinfo);
fclose(f);
好了,利用IJG JPEG Library進行影象壓縮就介紹到這裡,希望對大家有所幫助,例項程式碼已經實現了影象的壓縮和解壓縮的全部功能
Embedded VC
Jpeg影象處理程式和程式碼(使用Indepedent JPEG Group的JpegLib)
用Independent JPEG Group發行的JpegLib進行Jpeg影象的讀取與儲存。
這裡只加了一個簡單的處理示例——負片。其他的處理可以用與這個類似的方法,有了處理的演算法對畫素資料進行操作。或者加上滑鼠事件的處理來完成繪畫功能等等,這裡主要是對JPEG檔案進行操作的部分。
注意:程式中的CTScreenBuffer並未使用,原因是使用它載入後有段記憶體沒有釋放,加上BMP資料本來就比較好處理,所以自己寫一段,將BMP資料加上頭資訊就可以CreateDIBSection了。
儲存的預設質量Q=85,大家在使用時可以按照要求改變。
詳細的使用方法以及JpegLib庫
MacintoshM 2006-05-14, 11:38
詳細的使用方法:
1.系統需求
Microsoft eMbedded Visual C++ 4.0 + Pocket PC 2003 SDK
Pocket PC的JpegLib庫(在本帖的附件中)
2.背景
Jpeg庫的由以下兩個檔案配置:jconfig.h和jmorecfg.h。一般使用時是不需要改變jmorecfg.h的,但這樣可能在Pocket PC中執行時遇到問題,所以這裡還是對jmorecfg.h進行了修改。下面將討論這個修改,以使這個庫在Pocket PC上正常使用。不過這裡能夠下載的附件已經做好了這個修改。
3.將JpegLib引入Pocket PC
jmorecfg.h檔案含有與Embedded Visual Studio衝突的定義.需要做以下修改:
(1)程式碼:
#ifndef XMD_H
typedef long INT32;
#endif
改為:
#if !defined(XMD_H) && !defined(_BASETSD_H_)
typedef long INT32;
#endif
(2)程式碼:
#ifdef NEED_FAR_POINTERS
#define FAR far
#else
#define FAR
#endif
用下面的ifdef代替
#ifndef FAR
....
#endif
4.使用JpegLib載入Jpeg圖片
在前面的附件的程式中,已經有這個程式的框架,這裡不再贅述。只講主要的部分。
(1)載入Jpeg圖片的函式:
void CImageView::LoadImage(const CString &strFileName)
{
FILE * pFile;
struct jpeg_error_mgr jerr;
struct jpeg_decompress_struct cinfo;
int i,start;
start=0;
if ((pFile = _tfopen(strFileName, _T("rb")) == NULL) {
CString strError;
strError.Format(_T("無法開啟檔案 '%s'", strFileName);
AfxMessageBox(strError);
}
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, pFile);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
nRowSize = cinfo.output_width * cinfo.output_components;
Width=cinfo.output_width;
Height=cinfo.output_height;
if(bmpLoaded)
{
delete bmpBuffer;
}
bmpBuffer=new BYTE[(Height+1)*Width*3]; //這裡多申請一行,是因為在模擬器執行時,會出現無法載入的錯誤,但在機器上正常
bmpLoaded=TRUE;
pBuffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, nRowSize, 1);
while(cinfo.output_scanline < cinfo.output_height)
{
jpeg_read_scanlines(&cinfo, pBuffer, 1);
start=nRowSize*cinfo.output_scanline;
for(i=0;i<nRowSize;i++)
{
bmpBuffer[start+i]=pBuffer[0];
}
}
CreateBitmap();
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(pFile);
}
(2)將載入後的畫素資料建立一個HBITMAP物件的函式
注意這裡沒有使用CSTScreenBuffer,因為在我實驗時,用這個函式載入後,記憶體資源沒有釋放,這樣每載入一幅圖或做一次處理,就會多消耗幾兆記憶體,Pocket PC記憶體很快就會被耗盡。所以這裡將畫素資料加上頭資訊就可以CreateDIBSection了。
void CImageView::CreateBitmap()
{
int m_nCorrectedWidth,m_nWidth,m_nHeight;
m_nCorrectedWidth = ( ( Width + 3 ) / 4 ) * 4;
m_nWidth = Width;
m_nHeight = Height;
DIBINFO dibInfo;
BGRColor *m_pBuffer;
dibInfo.bmiHeader.biBitCount = 24;
dibInfo.bmiHeader.biClrImportant = 0;
dibInfo.bmiHeader.biClrUsed = 0;
dibInfo.bmiHeader.biCompression = 0;
dibInfo.bmiHeader.biHeight = m_nHeight;
dibInfo.bmiHeader.biPlanes = 1;
dibInfo.bmiHeader.biSize = 40;
dibInfo.bmiHeader.biSizeImage = m_nCorrectedWidth*m_nHeight*3;
dibInfo.bmiHeader.biWidth = m_nCorrectedWidth;
dibInfo.bmiHeader.biXPelsPerMeter = 3780;
dibInfo.bmiHeader.biYPelsPerMeter = 3780;
dibInfo.bmiColors[0].rgbBlue = 0;
dibInfo.bmiColors[0].rgbGreen = 0;
dibInfo.bmiColors[0].rgbRed = 0;
dibInfo.bmiColors[0].rgbReserved = 0;
HDC hDC = ::GetDC(NULL);
if(m_hBitmap)
{
:eleteObject(m_hBitmap);
m_hBitmap=0;
}
m_hBitmap = CreateDIBSection(hDC, (const BITMAPINFO*)dibInfo, DIB_RGB_COLORS, (void**)&m_pBuffer, NULL, 0);
::ReleaseDC(NULL,hDC);
int nPosition = 0;
int nDataPosition = 0;
for (int y=0; y<Height; y++) {
nPosition = m_nCorrectedWidth*(m_nHeight-y-1);
nDataPosition = Width*3*y;
for (int x=0; x<Width; x++) {
m_pBuffer[nPosition].m_R = bmpBuffer[nDataPosition++];
m_pBuffer[nPosition].m_G = bmpBuffer[nDataPosition++];
m_pBuffer[nPosition].m_B = bmpBuffer[nDataPosition++];
nPosition++;
}
}
}
(3)在繪畫函式中,這裡是OnDraw(),加入如下程式碼:
if (m_hBitmap)
{
CDC memDc;
VERIFY(memDc.CreateCompatibleDC(&dc));
HBITMAP hOldBitmap = (HBITMAP)::SelectObject(memDc.GetSafeHdc(), m_hBitmap);
VERIFY(dc.BitBlt(-LeftTop.x, -LeftTop.y, Width, Height, &memDc, 0, 0, SRCCOPY));
::SelectObject(memDc.GetSafeHdc(), hOldBitmap);
VERIFY( memDc.DeleteDC() );
}
5.使用JpegLib庫儲存Jpeg圖片
這裡是一種實現方法:
void CImageView::SaveImage(const CString &strFileName)
{
int nQuality=85; //儲存質量Q值為85
if(!::WriteRGBBytesIntoJpegFile(strFileName,Width,Height,nQuality,bmpBuffer))
{
::AfxMessageBox(GetJpegWriterError());
}
}
其他的更詳細內容的看例程中的其他訊息處理函式就可以了。
1.問題的由來
Jpeg圖片在影象處理領域已經用的相當廣泛了。但在程式設計領域,尤其是嵌入式程式設計領域使用的還不是很廣。主要的原因是Jpeg的資料結構和演算法遠較bmp複雜,非影象演算法的專業人士,通常是無法理解這些的。(我有個同事,他曾經花了半個月,研究過Jpeg的原理,還為此在team內部舉辦了一個講座,講了足足有一個小時,令眾人對之仰慕萬分,但他卻說,僅得皮毛,未得要害。從此我對Jpeg的原理近而遠之。)
近來,因工作需要,要在wince平臺下,為程式新增Jpeg支援。一般來說,此類問題最容易的解決方案是使用OS提供的相關庫,畢竟現在不支援Jpeg的OS幾乎沒有。但在wince下這裡出了個問題。目前絕大多數的wince裝置是wince 4.X或5.X的,不過碰巧在影象處理這塊,5.X和4.X的方法是不一樣的。4.X提供的方法在IMGDECMP.DLL中,而5.X提供的方法在Imaging.DLL中,而且不止是連結庫不同,庫的介面也是完全兩樣的。因此,如果軟體採用OS提供的相關庫的話,在這裡就要準備兩套介面,非常的麻煩。況且,我們的軟體不僅要在wince下部署,將來還打算推廣到其他的平臺,因此這種方案顯然是不太好的。
這時我想到了JpegLib。JpegLib是Independent JPEG Group——一個非Jpeg官方組織推出的Jpeg庫。這是個開源免費的庫,支援多種平臺和編譯器,你可以在www.ijg.org中獲得它的最新版本。在PC上它的效率比不了intel的Jpeg庫,因為後者進行了彙編優化。但在wince上應該和系統庫的效率相當。事實上如果檢視系統的版權資訊的話,Microsoft在wince中也使用了這個庫。RISC的晶片也基本沒有彙編優化的問題。
網上的中文資料以以下兩篇文章最為詳盡。
http://mobile.winfans.net/ccs/forums/516/PrintPost.aspx (文獻A)
http://blog.csdn.net/zhao3728/archive/2007/08/22/1754877.aspx (文獻B)
所以我的這篇文章主要作為補遺之用。
2.編譯JpegLib庫
文獻A和B都是使用現成的JpegLib庫,這樣的庫只在特定的平臺下才能用,完全體現不出JpegLib的優勢。所以掌握編譯JpegLib庫的方法,是很有必要的。
JpegLib庫原始碼的組成
1)makefile。JpegLib庫中有很多檔名為makefile的檔案。它的字尾名錶示它所用於的平臺或用途。隨便開啟一個,就可以看到JpegLib庫原始碼的組成。
2)makefile中LIBSOURCES變數所代表的檔案是JpegLib的核心演算法部分。
3)makefile中SYSDEPSOURCES變數所代表的檔案是JpegLib中負責記憶體分配的部分。這部分是系統相關的,所以根據需要選用一個就行了。
4)makefile中APPSOURCES變數所代表的檔案是JpegLib中提供諸如命令列壓縮Jpeg之類的功能的部分,實際上就是一些小工具。不過在編譯JpegLib庫時,不要將之新增到工程中。
5)makefile中INCLUDES變數所代表的檔案是原始碼中用到的標頭檔案。不是所有的標頭檔案都被核心演算法部分使用到,因此有些標頭檔案在編譯JpegLib庫時,是不必要的,當然將之新增到工程中也不會出錯。
6)makefile中DOCS變數所代表的檔案是一些文件、測試用例之類的東西。其中最有用的是example.c,文獻A和B的示例最重要的部分,就來自這裡。
7)jconfig。JpegLib庫中還有很多和makefile類似的jconfig,這是針對不同平臺的型別宣告。在使用時,應根據需要,將適當的jconfig檔案的字尾名改為h。
以上這些內容更詳細的說明見JpegLib庫的文件,以及
http://blog.csdn.net/axlrosek/archive/2007/03/29/1545496.aspx
VC:不同版本的VC、EVC都差不多,參照文獻A的步驟修改相關檔案,然後新建一個空工程,然後開啟JpegLib中的makefile.vc檔案,將2)和3)、5)、7)中的檔案新增到工程中。將生成檔案的型別定為lib就行了。此外,還需要針對呼叫的情況選擇適當的執行時庫,否則可能會出LNK2005錯誤。
3.使用JpegLib庫
JpegLib庫的使用參照文獻A和B,以及example.c就可以了。但文獻A的示例是有問題的。
bmpBuffer[start+i]=pBuffer[0];
應改為
bmpBuffer[start+i]=pBuffer[0][i];
jpeg_read_scanlines(&cinfo, pBuffer, 1)執行之後,掃描線的內容被放到pBuffer[0],而不是pBuffer中,用memcpy將掃描線的內容轉移到其他的緩衝區就行了,無需用迴圈語句。
4.流輸入的Jpeg的處理
上面的示例處理的都是從檔案讀入的Jpeg,在現實中,還存在流輸入的Jpeg。在很多PC遊戲中,程式的資料都被集中起來放入一個大檔案之中,最典型的當屬暴雪的mpq檔案。當這些檔案被讀入記憶體時,就形成了位元組流形式的檔案。將之存為臨時檔案,然後再用之前的方法,顯然是笨方法。這時可以採用jpeg_arr_src函式來替換jpeg_stdio_src函式。
這一點還可以參考以下文件,不過該文針對的好像是舊版本,使用時要注意。
http://blog.china.com/u/060803/5544/200608/15355.html