こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

C++ Builderにおける画像データの取得について

C++での画像処理プログラムの研究をしている者です。
文字数制限がありますので簡潔になりますがよろしくお願いします。
担当教官から基本となるプログラムをいただいたのですが実行するとエラーが出てしまいます。

プログラム内容:
void TImg::ImgMemToBmp(Graphics::TBitmap *bmp, BYTE *img)
{
GetBmpHdr(); // ビットマップヘッダー取得
String fileName = StartDir + "\\tmp.bmp";

FILE *fp;if (NULL == (fp = fopen(fileName.c_str(), "wb"))) return;
fwrite(BmpHeader, 54, 1, fp); // ヘッダー

//******** 画像書き込み ********
BYTE *p, *img1 = (BYTE *)malloc(Pixel_X_Max*3);
for (int y=0; y<Pixel_Y_Max; y++) {
p = img + (Pixel_Y_Max-y-1) * Pixel_X_Max;
for (int x=0; x<Pixel_X_Max; x++) {
*(img1+3*x) = *(img1+3*x+1) = *(img1+3*x+2) = *(p+x);
}
fwrite(img1, Pixel_X_Max*3, 1, fp);
}
fclose(fp);

bmp->LoadFromFile(fileName); // BMPファイル読み込み
DeleteFile(fileName); free(img1); // 不要ファイル、メモリ削除
}

問題点:下から3行目の
bmp->LoadFromFile(fileName);
でクラスの例外が発生しました。"ビットマップが不正です"とのエラーが出ます。

私なりにプログラムを解析してみたところ
で、tmp.bmpというファイルを作りそこに白黒画素値を書き込んでいるように思うのですが
途中で止めてtmp.bmpのサイズを確認してみたところ
原画像が640×480の画像でtmp.bmpのサイズは20971840×15728640となっていました。
これが原因でエラーと出たのではないかと考えたのですがいかがでしょうか?
補足要求していただければ、適宜補足いたしますので
どうかご助力をよろしくお願いいたします。

投稿日時 - 2007-02-24 23:58:25

QNo.2781560

困ってます

質問者が選んだベストアンサー

> 原画像が640×480の画像でtmp.bmpのサイズは20971840×15728640となっていました。

これはどうやって調べたんでしょうか?
信頼できる手段で確認したんでしょうか?
tmp.bmp の16進ダンプを見たんでしょうか?
それとも (バグがあるかもしれない) 自作プログラムですか?

ヘッダがちゃんと書けていないんだったら,
普通の画像表示・編集ソフトで読もうとしてもエラーになるはずですよね?

(1) tmp.bmp が正しく書けていないのか,それとも LoadFromFile() に
  問題があるのかを切り分けるため,tmp.bmp が画像表示・編集ソフト
  (ペイントなど) でちゃんと表示できるかどうか確認してください.

(2) tmp.bmp がちゃんと書けていないのであれば,GetBmpHdr() に問題が
  ありそうなので,そのソースを示してください.それと,できれば
  tmp.bmp のヘッダ部 (ファイルの先頭54バイト) の16進ダンプも.

> 白黒画素値を書き込んでいるように思うのですが

元画像は8ビットグレースケールですが,
RGB 24ビットに変換して書き込んでいますね.

画像書き込み部のコードにもバグがあります.
BMP ファイルの1本の走査線は,4バイトの倍数に切り上げなければなりません.
(担当の先生は BMP ファイルのフォーマットを十分理解されていないようです.)
しかし今回は原画像の幅が480ドット (4の倍数) なので,
たまたまうまく動くはずです.したがって今回のエラーの原因ではありません.

↓参考
BMPファイルのフォーマット
http://www5d.biglobe.ne.jp/~noocyte/Programming/Windows/BmpFileFormat.html

投稿日時 - 2007-02-25 02:45:06

補足

以下、GetBmpHdrのソースです。

void TImg::GetBmpHdr(void)
{
// BitmapHeaderが読みこまれていない場合読み込む
if (BmpHFlag == 0) MakeBmpHeaderFile(Pixel_X_Max, Pixel_Y_Max);
}
//---------------------------------------------------------------------------
// MakeBmpHeaderFile ビットマップヘッダーファイルを作成する
//---------------------------------------------------------------------------
int TImg::MakeBmpHeaderFile(int w, int h)
{
// 画像サイズ設定
if (w == 0) { w = Pixel_X_Max; h = Pixel_Y_Max; }

// ビットマップ画像の作成と保存
String fileHdr = StartDir + "\\BmpHeader.dat";
String fileName = StartDir + "\\template.bmp";
Graphics::TBitmap *bitmap;
bitmap = new Graphics::TBitmap; // ビットマップオブジェクト用のメモリ確保
bitmap->Width = w;
bitmap->Height = h;
bitmap->SaveToFile(fileName);
delete bitmap; // ビットマップの開放

BITMAPFILEHEADER bfHdr; BITMAPINFOHEADER biHdr;

// ビットマップファイルよりヘッダー情報の読み取り
FILE *fp;
if (NULL == (fp = fopen(fileName.c_str(), "rb"))) return NG;
fread(&bfHdr, sizeof(BITMAPFILEHEADER), 1, fp);
fread(&biHdr, sizeof(BITMAPINFOHEADER), 1, fp);
biHdr.biHeight = h;
biHdr.biWidth = w;
biHdr.biBitCount = 24;
biHdr.biCompression = 0;
bfHdr.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + w*h*3;
fclose(fp);

// ヘッダー情報の保存
if (NULL == (fp = fopen(fileHdr.c_str(), "wb"))) return NG;
fwrite(&bfHdr, sizeof(BITMAPFILEHEADER), 1, fp);
fwrite(&biHdr, sizeof(BITMAPINFOHEADER), 1, fp);
fclose(fp);

// ビットマップファイルよりヘッダー情報の読み取り
if (NULL == (fp = fopen(fileHdr.c_str(), "rb"))) return NG;
fread(BmpHeader, 54, 1, fp);
fclose(fp);
BmpHFlag = 1;

// ビットマップファイルの削除
DeleteFile(fileName);
DeleteFile(fileHdr);
return OK;
}

すみません。無知なもので16進ダンプの正確な定義がわからないですが
フリーの16進ダンプリストを表示するソフトにtmp.bmpを入れた結果の先頭64ビットを以下に示します。
もし違いましたらご指摘お願いします。
00000000 42 4D 36 B0 38 84 03 00 00 00 36 00 00 00 28 00
00000010 00 00 40 01 40 01 00 00 F0 00 00 00 20 00 18 00
00000020 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

投稿日時 - 2007-02-25 02:50:29

お礼

tmp.bmpは途中でShowMessageを使ってプログラムを止めてエクスプローラで確認した結果です。
tmp.bmpファイルをペインタで開いてみたところ、ペインタでは開けませんでしたので
ソースと16進ダンプを補足させていただきました。

画像書き込み部のコードにもバグがあるようで…
なかなかこのプログラムには苦戦させられそうです。

なにはともあれ回答していただきありがとうございました。
参考URLも参考にさせていただきます。

投稿日時 - 2007-02-25 03:07:26

ANo.1

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.3

画像書き込み部の修正版も作ってみました.これもコンパイルは通してません.


 //******** 画像書き込み ********
 const size_t bitsPerPixel = 24; // 1画素のビット数
 const size_t bytesPerPixel = bitsPerPixel / 8; // 1画素のバイト数

 // 走査線1本のバイト数 lineSize を計算する.
 size_t lineSize = bytesPerPixel * Pixel_X_Max; // 正味のバイト数

 // lineSize を4の倍数に切り上げる (BMPファイルの仕様).
 lineSize = (lineSize + 3) & ~(size_t)3;

 // 走査線1本分のバッファを確保する.
 // calloc() を用いるのは,lineBuf の最後の
 // 0~3バイトを0クリアするのが目的.
 BYTE *lineBuf = (BYTE*)calloc(lineSize, 1);
 if(lineBuf == NULL) {
Error:
  if(lineBuf != NULL) free(lineBuf);
  fclose(fp);
  DeleteFile(fileName); // 出来損ないのファイルを削除する.
  return; // 本当は NG を返すべき.
 }

 // 画素データを書く.
 const BYTE *srcLine = img + lineSize * Pixel_Y_Max; // img[] の直後を指す.
 for(int y = 0; y < Pixel_Y_Max; y++) {
  // srcLine, src ← img[] の現在の走査線の先頭
  srcLine -= lineSize;
  const BYTE *src = srcLine;
  BYTE *dest = lineBuf;
  for(int x = 0; x < Pixel_X_Max; x++) {
   // 元画像の画素 *src (グレースケール8ビット) を
   // RGB 24ビットに変換して lineBuf に格納する.
   dest[2] = dest[1] = dest[0] = *src++;
   dest += bytesPerPixel;
  }
  if(fwrite(lineBuf, 1, lineSize, fp) != lineSize) goto Error;
 }
 fclose(fp);
 free(lineBuf);
 return; // 本当は OK を返すべき.

投稿日時 - 2007-02-25 05:46:57

お礼

研究の発表がありお礼が遅くなってしまい申し訳ありません。
ここまで、詳しく回答していただいてお礼のお言葉が思い浮かびません!

まだまだ私には難しいところがありますがこれも見識を広めるいい機会だと思います。
No.1の回答にあるサイトとあわせて勉強させていただきます。
本当に回答ありがとうございました。

投稿日時 - 2007-03-03 00:25:59

ANo.2

#1 です.

> int TImg::MakeBmpHeaderFile(int w, int h)

うわ~,このメンバ関数は意図不明なことをやってますね.
処理の内容はわかりますが,わざわざこういう複雑怪奇な
処理をする意図がさっぱりわかりません.

Graphics::TBitmap は C++ Builder のクラスライブラリが提供している
クラスでしょうか? だとすれば,おそらくこのメソッドの作者
(JulesVerne さんの先生か先輩?) は,ヘッダの各メンバの計算方法が
よくわからなかったかサボりたかったので,出来合いの TBitmap::SaveToFile()
が書き出してくれるヘッダを流用しようとしたのではないでしょうか?
でもいくつかのメンバは簡単なので自分で設定したと….(笑)

でも,できたヘッダの16進ダンプを見ると,ほとんどデタラメですね.
ソースを見る限り,設定内容は悪くなさそうなので,
ファイルの読み書きがちゃんとできていないのかもしれません.
(エラーチェックをほとんどしていないので,そうであっても報告されない.)
もしかしたら StartDir の設定が間違っているのではないでしょうか.


┌複雑怪奇な TImg::MakeBmpHeaderFile() の改良版を作ってみましたので
│ご参考まで.(コンパイルは通していません.またインデントが潰れるのを
│防ぐため,半角空白を全角空白に置き換えています.)

int TImg::MakeBmpHeaderFile(int w, int h)
{
 BITMAPFILEHEADER bfHdr;
 BITMAPINFOHEADER biHdr;
 const size_t bitsPerPixel = 24; // 1画素のビット数
 const size_t bytesPerPixel = bitsPerPixel / 8; // 1画素のバイト数

 // 画像サイズ設定
 if (w == 0) { w = Pixel_X_Max; h = Pixel_Y_Max; }

 // 走査線1本のバイト数 lineSize を計算する.
 size_t lineSize = bytesPerPixel * w; // 正味のバイト数

 // lineSize を4の倍数に切り上げる (BMPファイルの仕様).
 lineSize = (lineSize + 3) & ~(size_t)3;

 // 画像データ本体のバイト数
 size_t imageSize = lineSize * h;

 // ヘッダサイズの合計
 size_t headerSize = sizeof(bfHdr) + sizeof(biHdr);

 // ヘッダの内容を設定する.

 bfHdr.bfType = 'B' | ('M' << 8);
 bfHdr.bfSize = (DWORD)(headerSize + imageSize);
 bfHdr.bfReserved1 = 0;
 bfHdr.bfReserved2 = 0;
 bfHdr.bfOffBits = (DWORD)headerSize;

 biHdr.biSize = sizeof(biHdr);
 biHdr.biWidth = (LONG)w;
 biHdr.biHeight = (LONG)h;
 biHdr.biPlanes = 1;
 biHdr.biBitCount = (WORD)bitsPerPixel;
 biHdr.biCompression = BI_RGB;
 biHdr.biSizeImage = (DWORD)imageSize;
 biHdr.biXPelsPerMeter = 0;
 biHdr.biYPelsPerMeter = 0;
 biHdr.biClrUsed = 0;
 biHdr.biClrImportant = 0;

 // ヘッダを BmpHeader にコピーする.
 memcpy(&BmpHeader, &bfHdr, sizeof(bfHdr));
 memcpy((char*)&BmpHeader + sizeof(bfHdr), &biHdr, sizeof(biHdr));

 BmpHFlag = 1;
 return OK;
}

投稿日時 - 2007-02-25 05:02:23

あなたにオススメの質問