2010年10月11日

JPEGファイルの書き込み



今回は、DIB画像「Independent JPEG Group」のライブラリーを使用して、JPEGファイルに書き込む方法を解説します。尚JPEGライブラリーの扱いについては、前々回の「libjpegのインストール」をご参照ください。




libjpegをインクルードするための定義です。

extern "C" {
#define XMD_H // 再定義エラーを防ぐ
#include "jpeglib.h"
}



前々回の「libjpegのインストール」でダウンロードした「jpeg-8b」フォルダーから、以下のファイルを今回のプログラム本体を格納した「JpegFile.cpp」ファイルのあるフォルダーにコピーしておきます。

jpeg-8b」フォルダーからコピーするファイルです。

jpeglib.h
jconfig.h
jmorecfg.h


ユーザー定義ファイル書き込み関数フラッシュ関数です。

//JPEGファイル書き込み関数
unsigned int _JpegWriteFunc(void* io_ptr, unsigned char* buf, unsigned int size)
{
((CFile*)io_ptr)->Write(buf,(UINT)size);
return size;
}

//JPEGファイルフラッシュ関数
void _JpegFlushFunc(void* io_ptr)
{
((CFile*)io_ptr)->Flush();
}


まずは指定されたパス名のファイルを開いて、_JpegStreamOut関数を呼び出します。

//DIBをJPEGファイルに書き出す
BOOL __stdcall _SaveJPG(LPCTSTR lpszFileName,HBITMAP hbmDIB,BOOL bColor,int iQuality)
{
CFile outfile;
if (!outfile.Open(lpszFileName,CFile::modeCreate|CFile::modeWrite)) return FALSE;
BOOL bResult=_JpegStreamOut(&outfile,hbmDIB,bColor,iQuality);
outfile.Close();
return bResult;
}


DIBJPEGストリームに書き出します。

//DIBをJPEGストリームに書き出す
BOOL __stdcall _JpegStreamOut(CFile* outfile,HBITMAP hbmDIB,BOOL bColor,int iQuality)
{
ASSERT(AfxIsValidAddress(outfile,sizeof(CFile),0));

//DIBSECTION構造体の取得
//取得に失敗したりDIB出ない場合、FALSEを返して終了する。

DIBSECTION ds={0};
if ((!::GetObject(hbmDIB,sizeof(DIBSECTION),&ds))||
(ds.dsBmih.biSize!=sizeof(BITMAPINFOHEADER))) return FALSE;

UINT width =ds.dsBmih.biWidth; //画像の幅
UINT height=abs(ds.dsBmih.biHeight);//画像の高さ

//JPEGエンコードオブジェクトの配置
struct jpeg_compress_struct cinfo;

//JPEG内部で発生したエラーを処理する処理関数を定義する。
struct my_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;

//これ以降の内部エラーはここに戻り、エラーとして返される。
RGBQUAD* pColorTable=0;
if (setjmp(jerr.setjmp_buffer)) {
if (pColorTable) delete[]pColorTable;
jpeg_destroy_compress(&cinfo);
return FALSE;
}

//JPEGエンコードオブジェクトの初期化
jpeg_create_compress(&cinfo);

//出力ストリームを設定する
jpeg_set_write_fn(&cinfo,outfile,_JpegWriteFunc,_JpegFlushFunc);

//エンコードのための変数を設定する
cinfo.image_width =width; //画像幅を設定
cinfo.image_height=height; //画像高さを設定
if (bColor) {
cinfo.input_components = 3; //ピクセル当りの色バイト数
cinfo.in_color_space = JCS_RGB; //色の入力イメージ
}
else {
cinfo.input_components = 1; //ピクセル当りの色バイト数
cinfo.in_color_space = JCS_GRAYSCALE;//色の入力イメージ
}

//エンコード変数を設定する。
jpeg_set_defaults(&cinfo);

//データ圧縮品質を設定する。
jpeg_set_quality(&cinfo, iQuality, TRUE);

//エンコード開始
jpeg_start_compress(&cinfo, TRUE);

//ラインバッファのバイト数を算出する
int row_stride = cinfo.image_width * cinfo.input_components;

//ラインバッファの領域を確保する
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

//1行ごとに書き込む
BYTE r,g,b;
WORD w; //16ビットピクセルを一時格納する変数
int shg,shr,shb; //16ビットピクセルから色を取り出す時の各RGBのシフト値
switch(ds.dsBmih.biBitCount){
case 24://ビット深度が24ビットの時は優先して処理する
if (bColor){
while (cinfo.next_scanline < cinfo.image_height) {
UINT row=(ds.dsBmih.biHeight<0)?cinfo.next_scanline:(height-cinfo.next_scanline-1);
PBYTE src=(PBYTE)ds.dsBm.bmBits+ds.dsBm.bmWidthBytes*row;
PBYTE dst=(PBYTE)buffer[0];
for(UINT j=0;j<width;j++){
//RGBカラーデータを取り出す
b=*src++;
g=*src++;
r=*src++;
*dst++=r;
*dst++=g;
*dst++=b;
}
(void) jpeg_write_scanlines(&cinfo, &buffer[0], 1);
}
}
else{
while (cinfo.next_scanline < cinfo.image_height) {
UINT row=(ds.dsBmih.biHeight<0)?cinfo.next_scanline:(height-cinfo.next_scanline-1);
PBYTE src=(PBYTE)ds.dsBm.bmBits+ds.dsBm.bmWidthBytes*row;
PBYTE dst=(PBYTE)buffer[0];
for(UINT j=0;j<width;j++){
//RGBカラーデータを取り出す
b=*src++;
g=*src++;
r=*src++;
//白黒画像にする
*dst++=(BYTE)( .299 * (double)r + .587 * (double)g + .114 * (double)b);
}
(void) jpeg_write_scanlines(&cinfo, &buffer[0], 1);
}
}
break;

case 32: //ビット深度が32ビットの時
while (cinfo.next_scanline < cinfo.image_height) {
UINT row=(ds.dsBmih.biHeight<0)?cinfo.next_scanline:(height-cinfo.next_scanline-1);
PBYTE src=(PBYTE)ds.dsBm.bmBits+ds.dsBm.bmWidthBytes*row;
PBYTE dst=(PBYTE)buffer[0];
for(UINT j=0;j<width;j++){
//RGBカラーデータを取り出す
b=*src++;
g=*src++;
r=*src++;
src++;
if (bColor){
*dst++=r;
*dst++=g;
*dst++=b;
}
else//白黒画像にする
*dst++=(BYTE)( .299 * (double)r + .587 * (double)g + .114 * (double)b);
}
(void) jpeg_write_scanlines(&cinfo, &buffer[0], 1);
}
break;

case 16: //ビット深度が16ビットの時
//RGB565の時
if (ds.dsBitfields[0]==0xF800){
shr=8;
shg=3;
shb=3;
}
else{//RGB555の時
shr=7;
shg=2;
shb=3;
}
while (cinfo.next_scanline < cinfo.image_height) {
UINT row=(ds.dsBmih.biHeight<0)?cinfo.next_scanline:(height-cinfo.next_scanline-1);
WORD* src=(WORD*)((PBYTE)ds.dsBm.bmBits+ds.dsBm.bmWidthBytes*row);
PBYTE dst=(PBYTE)buffer[0];
for(UINT j=0;j<width;j++){
//RGBカラーデータを取り出す
w=*src++;
r=(BYTE)((w&ds.dsBitfields[0])>>shr);
g=(BYTE)((w&ds.dsBitfields[1])>>shg);
b=(BYTE)((w&ds.dsBitfields[2])<<shb);
if (bColor){
*dst++=r;
*dst++=g;
*dst++=b;
}
else//白黒画像にする
*dst++=(BYTE)( .299 * (double)r + .587 * (double)g + .114 * (double)b);
}
(void) jpeg_write_scanlines(&cinfo, &buffer[0], 1);
}
break;

case 8:
case 4:
case 1: //パレットDIBの時
{
//カラーテーブルを取得する
UINT uColors=(ds.dsBmih.biClrUsed)? ds.dsBmih.biClrUsed:1<<ds.dsBmih.biBitCount;
pColorTable=new RGBQUAD[uColors];
HDC hMemDC=::CreateCompatibleDC(0);
HGDIOBJ hGdiObj=::SelectObject(hMemDC,hbmDIB);
uColors=::GetDIBColorTable(hMemDC,0,uColors,pColorTable);
::SelectObject(hMemDC,hGdiObj);
::DeleteDC(hMemDC);
}
while (cinfo.next_scanline < cinfo.image_height) {
UINT row=(ds.dsBmih.biHeight<0)?cinfo.next_scanline:(height-cinfo.next_scanline-1);
PBYTE src=(PBYTE)ds.dsBm.bmBits+ds.dsBm.bmWidthBytes*row;
PBYTE dst=(PBYTE)buffer[0];
for(UINT j=0;j<width;j++){
int indexColor=0;
switch(ds.dsBmih.biBitCount){
case 1:
indexColor=((j%8==7)? *src++:*src>>(7-(j%8)))&1;
break;
case 4:
indexColor=((j%2==0)? *src>>4:*src++)&0x0F;
break;
case 8:
indexColor=*src++;
break;
}
if (bColor){
//RGBカラーデータを取り出す
*dst++=pColorTable[indexColor].rgbRed;
*dst++=pColorTable[indexColor].rgbGreen;
*dst++=pColorTable[indexColor].rgbBlue;
}
else//白黒画像にする
*dst++=(BYTE)(.299*(double)pColorTable[indexColor].rgbRed +
.587*(double)pColorTable[indexColor].rgbGreen +
.114*(double)pColorTable[indexColor].rgbBlue);
}
(void) jpeg_write_scanlines(&cinfo, &buffer[0], 1);
}
delete[]pColorTable;
pColorTable=0;
break;
}

//エンコード終了後の後片付け
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);

return TRUE;
}


戻り値がTRUEなら、JPEGファイルへの書き込みは成功です。

サンプルプログラム(VisualC++net2003ソリューション)JpegFileTest02.zip

上記のzipファイルをダウンロードして、前々回の「libjpegのインストール」で作成した「jpeg-8b」フォルダーと「libJpeg」フォルダーがある同じフォルダー内にWinRAR等を使って解凍すると、「JpegFileTest02」と言う名のフォルダーが作成されます。




「JpegFileTest02」フォルダー内にある「JpegFileTest.sln」を開いて「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。ビルドエラーが出てどうしてもlibjpegのビルドが上手くいかない方は、「JpegFileTest」プロジェクト内にある「JpegFile.cpp」ファイルを開いて、下記の赤字の行にあるコメントアウトを外して、フォルダー内にあるライブラリーファイルを直接読み込むようにします。修正後、再度「F5」キーでビルドしてみて下さい。

// JpegFile.cpp : implementation file
//


#include "stdafx.h"
#include "JpegFileTest.h"
#include "JpegFile.h"

extern "C" {
#define XMD_H // 再定義エラーを防ぐ
#include "jpeglib.h"
}
//#pragma comment(lib,"ImageLib\\libjpeg") ←コメントアウトを外してください。
↓   ↓   ↓

#pragma comment(lib,"ImageLib\\libjpeg") //修正後


ビルドが終わると、直ちにプログラムが自動起動してウィンドウが表示されます。ウィンドウ内にあるトラッカーの範囲内で、右クリックしてポップアップメニューで「開く」を選択して、「ファイルを開く」ダイヤログを表示させ、resフォルダー内の「200_imola_81.jpg」を読み込みます。



画像が表示されたところで「ファイル」/「名前を付けて保存」で「ファイル名を付けて保存」ダイヤログを表示させ、「200_imola_81Color.jpg」と名づけて「保存」ボタンを押します。



「JPEG圧縮の設定」ダイヤログが表示されるので、「グレイスケールで圧縮する」チェックボタンにはチェックを入れずに「適用」ボタンを押します。



ツールバーの「新規」で画像を一旦を消しておいてから、先ほど作成した「200_imola_81Color.jpg」を読み込んで、元の画像と同じ画像が表示されるか確認します。



画像が表示されたところで再び「ファイル」/「名前を付けて保存」で「ファイル名を付けて保存」ダイヤログを表示させ、「200_imola_81Gray.jpg」と名づけて「保存」ボタンを押します。
「JPEG圧縮の設定」ダイヤログが表示されるので、今度は「グレイスケールで圧縮する」チェックボタンにチェックを入れて「適用」ボタンを押します。



表示されている画像が白黒画像に変化したか確認します。



他に「res」フォルダー内にアイコン画像を数種用意しましたので、ファイル名の拡張子を「bmp」や「png」、「cur」に書き換えて、違う画像フォーマットへの変換が出来るか確認してください。





posted by ひろし at 01:14| Comment(0) | JPEGファイルフォーマット | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。