【環境:Win98 SE, VC++6.0 MFC, View/Doc】
【その他: http://www.ijg.org/ からDLした jpegsr6.zip を使用】
いつもお世話になっております。
現在、IJGを使用してJPEGを画面に表示/保存を実現しようとしています。
画面への表示は上手くいきましたが、JPEG形式での保存でつまずいています。
どなたか分かる方がいらっしゃいましたら、助言を頂けると幸いです。
<現象>
(1)fopenでエラー
存在しないファイルを指定するとfopenで失敗します。
既存のファイルを指定した場合は成功します。
(2)作成したJPEGファイルが空(0KB)
保存を実行すると、エラーなどは発生せず、正常に終了するのですが、
JPEGファイルの中身が空となります。
<ソース>
fp = fopen( csPath, wb);
if( fp == NULL){
//エラー処理
}
pbmp = m_dcMem.GetCurrentBitmap();
・・ 略 ・・
//データ取得
if(!(hbm = ::CreateDIBSection(m_dcMem.GetSafeHdc(),&bmi,
DIB_RGB_COLORS,(void**)&imagedata,0,0))) throw 0;
//オブジェクトの初期化
jpeg_create_compress( &cinfo);
//パラメータ設定
cinfo.image_width = image_width; //=bmp.bmWidth
cinfo.image_height = image_height; //=bmp.bmHeight
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
//圧縮開始
jpeg_stdio_dest(&cinfo, fp);
jpeg_start_compress(&cinfo, TRUE);
JSAMPLE * image_buffer;
int row_stride;
int bmp_row_bytes;
BYTE * bmp_image;
BYTE * src, * dest;
// JPEG1ラインのバイト数
row_stride = nWidth * 3;
image_buffer = (unsigned char*)malloc( row_stride );
bmp_row_bytes = (nWidth * 3 + 3) / 4 * 4;
bmp_image = imagedata;
// 1ラインのピクセル・データ書き込み用バッファの準備
JSAMPROW row_pointer[1];
i = nHeight - 1;
while (cinfo.next_scanline < cinfo.image_height) {
// BMP は格納形式が上下逆。
dest = image_buffer;
src = &bmp_image[ i * bmp_row_bytes ];
for( j = 0; j < nWidth; j++ ) {
*dest++ = src[ 2 ];
*dest++ = src[ 1 ];
*dest++ = src[ 0 ];
src += 3;
}
i--;
row_pointer[0] = image_buffer;
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
//圧縮終了
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
・・ 略 ・・
以上、長くなり申し訳ありませんが、宜しくお願い致します。
fopen はjpeg ライブラリと関係ないので他の不具合があるのでしょう。
jpeg ライブラリの使かいかたでは、
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress( &cinfo);
とエラーアドレスを初期化していないと
jpeg_start_compress(&cinfo, TRUE);
の中で落ちるはずです。
また、
jpeg_set_quality(&cinfo, nQuality, TRUE );
関数を発行していないのも気になります。
簡単にみてきがついた点だけ
汎様、ありがとうございます。
返事が遅くなり、申し訳ありません。
> fopen はjpeg ライブラリと関係ないので他の不具合があるのでしょう。
ライブラリと関係ないということなので、別途調べてみます。
>エラーアドレスを初期化していないと
>jpeg_start_compress(&cinfo, TRUE);
>の中で落ちるはずです。
初期化しておりましたが、ソースを転載する際、
抜かしていました。すみません。
>jpeg_set_quality(&cinfo, nQuality, TRUE );
>関数を発行していないのも気になります。
この一文をjpeg_start_compress()の直前に
jpeg_set_quality(&cinfo, 1, TRUE);
として入れてみましたが、jpegファイルは0KB空のままでした。
しかし、jpegファイルを開いてみると、
保存対象イメージとほぼおなじ幅、高さの
空イメージ(下図のようなもの)が表示されました。
---------
|x |
| |
| |
---------
ということは、while (cinfo.next_scanline < cinfo.image_height)
内の処理が悪いのでは、と検討をつけ、見直しておりましたが、
解決の糸口が見えておりません。
分かる方がいらっしゃいましたら、ご教授願います。
> bmp_row_bytes = (nWidth * 3 + 3) / 4 * 4;
bmp_row_bytes = (int)((nWidth * 3 + 3) / 4) * 4;
と一度小数点以下を捨てないとまずいのでは。
> bmp_row_bytes = (nWidth * 3 + 3) / 4 * 4;
が正しい処理だという事を説明してください
JPEGライブラリの使い方はよいみたいです。
CreateDIBSectionを使っていますが、これは正しい?
pbmp->GetBitmap(&bmp) ;のBITMAP 構造体の
bmp.bmBits でとれるのではないでしょうか?
fopen は別といいましたが、ただしくオープンされていなければ
書き込めません。
jpeg_set_qualityは出力画質を表し標準的な値は75(%)です。
1では画像になりません。
以下は元ソースをベースに実際に動かしてみた関数
画像が上下逆になりましたが保存はできました。
BOOL CJPEGFile::WriteFile2(CString csPath, int nJpegQuality, CBitmap* pbmp)
{
FILE * fp;
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
BITMAP bmp ;
fp = fopen( csPath, wb);
if(fp == NULL){
return FALSE ;
}
pbmp->GetBitmap(&bmp) ;
int image_width = bmp.bmWidth ;
int image_height = bmp.bmHeight ;
BYTE* imagedata = (BYTE*)bmp.bmBits ;
//データ取得
// if(!(hbm = ::CreateDIBSection(pDC->GetSafeHdc(),&bmi,
// DIB_RGB_COLORS,(void**)&imagedata,0,0))) throw 0;
//オブジェクトの初期化
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress( &cinfo);
//パラメータ設定
cinfo.image_width = image_width; //=bmp.bmWidth
cinfo.image_height = image_height; //=bmp.bmHeight
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
//圧縮開始
jpeg_stdio_dest(&cinfo, fp);
jpeg_set_quality(&cinfo, nJpegQuality, TRUE );
jpeg_start_compress(&cinfo, TRUE);
JSAMPLE * image_buffer;
int row_stride;
int bmp_row_bytes;
BYTE * bmp_image;
BYTE * src, * dest;
// JPEG1ラインのバイト数
row_stride = image_width * 3;
image_buffer = (unsigned char*)malloc( row_stride );
bmp_row_bytes = (image_width * 3 + 3) / 4 * 4;
bmp_image = imagedata;
// 1ラインのピクセル・データ書き込み用バッファの準備
JSAMPROW row_pointer[1];
int i = image_height - 1;
while (cinfo.next_scanline < cinfo.image_height) {
// BMP は格納形式が上下逆。
dest = image_buffer;
src = &bmp_image[ i * bmp_row_bytes ];
for(int j = 0; j < image_width; j++ ) {
*dest++ = src[ 2 ];
*dest++ = src[ 1 ];
*dest++ = src[ 0 ];
src += 3;
}
i--;
row_pointer[0] = image_buffer;
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
//圧縮終了
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(fp);
return TRUE ;
}
結果から言いますと、汎様の方法でjpeg形式で保存することができました。
Gak 様
>bmp_row_bytes = (int)((nWidth * 3 + 3) / 4) * 4;
と
bmp_row_bytes = (nWidth * 3 + 3) / 4 * 4;
の両方でjpeg保存してみましたが、どちらも保存することができました。
保存した画像を目視しましたが、特に違うところは見受けられませんでした。
島 様
> bmp_row_bytes = (nWidth * 3 + 3) / 4 * 4;
>が正しい処理だという事を説明してください
この式は、他のページからコピーしたもので、深く理解しておりません。
ただ、
・画像の横ラインデータは4バイト境界でないといけない。
・3つの色を格納するので「幅*3」となる。
ということから、上記のような式がでてくるのでは、、と解釈しておりました。
汎 様
ご指摘頂いた箇所を修正することで、保存することができました。
詳細は以下の通りです。
(1)保存ファイル名
保存するファイル名を
CString csPath = ar.GetFile()->GetFilePath();
のように取得しておりましたが、ファイル名を固定にしてテストすると、
真っ黒の画像ですが、保存することができました。
(デバッグでは期待通りのファイル名が取得できていたので問題ないと
思っていたのですが、、)
(2)CreateDIBSection()を使わない -> (1)の真っ黒画像を解決
CreateDIBSection()からbmp.bmBits に修正することで、
とても悪い画質ですが、保存することができました。
(3)出力画質を修正 -> (2)の画質の問題を解決
jpeg_set_quality(&cinfo, 75, TRUE) に修正することで、
期待通りの画像を保存することができました。
libjpeg.docに
「The quality value is expressed on the 0-100 scale recommended by IJG」
とあったので、1でも問題ないかと思っておりました。
(保存はできるから問題ないと言えば問題ないですかね??)
>画像が上下逆になりましたが保存はできました。
私の場合、上下逆にはなりませんでした。
まだ、ファイル名の問題が残っておりますが、本題であるjpegの保存は
上手く出来ましたので、解決とさせて頂きます。
情報をご提供頂いた皆様、有難う御座いました。