• ベストアンサー

TIFFを開いてBITMAPで保存する方法

■開発環境 VC++6.0,Windows2000 Pro,+ libtiff(TIFFを扱うライブラリ) で以下のようなコードを書きました. ************************************************* BOOL CTiff::OpenTiffAndSaveBMP(CString path) { HDIB hDIB; hDIB = LoadTIFFinDIB(path); BITMAPFILEHEADER BMPFILEHEADER; LPBITMAPINFOHEADER BMPINFOHEADER; if(!hDIB) return FALSE; CFile file; path.Replace("tif", "bmp"); if( !file.Open( path, CFile::modeWrite|CFile::modeCreate) ) return FALSE; BMPINFOHEADER = (LPBITMAPINFOHEADER)hDIB; int nColors = 1 << BMPINFOHEADER->biBitCount; // BITMAPFILEHEADERのフィールドを初期化 BMPFILEHEADER.bfType = ((WORD) ('M' << 8) | 'B'); // is always "BM" BMPFILEHEADER.bfSize = GlobalSize (hDIB) + sizeof( BMPFILEHEADER ); BMPFILEHEADER.bfReserved1 = 0; BMPFILEHEADER.bfReserved2 = 0; BMPFILEHEADER.bfOffBits = (DWORD)(sizeof( BMPFILEHEADER ) + BMPINFOHEADER-> biSize + nColors * sizeof(RGBQUAD)); // ファイルヘッダを書き込む file.Write( &BMPFILEHEADER, sizeof(BMPFILEHEADER) ); // DIBのヘッダとビット列を書き込む DWORD nSize = GlobalSize(hDIB); file.Write( BMPINFOHEADER, nSize ); file.Close(); return TRUE; } ************************************************ しかし,OpenTiffAndSaveBMP()の下から3行目の >file.Write( BMPINFOHEADER, nSize ); でDebug Assertion Failed!なるエラーで止まってしまいます. そこで「無視」ボタンを押すと「ファイルにアクセス中に不明なエラーが発生しまし た」となります. 文字数の制限上LoadTIFFinDIB()のソースは無いのですが, 原因ならびに解決策の分かる方ご教授ください.

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

  • ベストアンサー
noname#9119
noname#9119
回答No.2

GlobalSize(hDIB)としているならグローバルメモリなんでしょう。 hDIBがポインタとは限らないので、GlobalLock() でメモリブロックへのポインタを得てからキャストする。 BMPINFOHEADER = (LPBITMAPINFOHEADER)GlobalLock(hDIB);

mkmarimo
質問者

補足

ご回答ありがとうございます. 上記のやり方でエラーで止まることなくプログラムは走りました. しかし,保存されたビットマップはペイントで開くと, 元がどんなTIFF画像であろうと真っ白になってしまいます. これの原因について分かる方ご教授お願いします.

その他の回答 (6)

  • mcWalker
  • ベストアンサー率69% (27/39)
回答No.7

LoadTIFFinDIB() の BitsPerSample, PhotometricInterpretation の定義は uint16 でないといけないのに、VC++ default では、 32bits になりますね。 tiff2dib.c の作者は自分の tiff data 向けに作ったものでしょう。 canon の tiff を処理したいのなら、スペックを把握して 範囲を限定して進めた方がよさそうです。 因みに tiff2dib.c でうまく行くのは、pics フォルダの jello.tif のみでした。

mkmarimo
質問者

お礼

わざわざそのようなことまで調べて、アドバイスしていただきありがとうございます。 16ビットのTIFFを読みたかったんですけど、難しそうですね。 もうすこしがんばってみます。

  • mcWalker
  • ベストアンサー率69% (27/39)
回答No.6

libtiff3.dll の入った libtiff で、tiff2dib.c を libtiff ソースの pics フォルダの .tif ファイルで試したのですが、 .bmp ファイルで取り出せない例が出ます。 取り出せても、真っ黒あるいは真っ白。 libtiff3.dll は、2004.2.11 付けで、 tiff2dib.c は、1995.4.12 付けです。 tiff2dib.c はフランスの方が win95 向けに作ったものですが、 何かが、整合していないようです。LoadTIFFinDIB() の中の memcpy() で落ちる例がありました。 仕様書に立ち戻った精査が必要な予感がします。

mkmarimo
質問者

お礼

mcWalkerさん、いろいろ調べていただき本当に感謝です。 お陰で大分前進した気がします。ありがとうございます。

mkmarimo
質問者

補足

今自分でもやってみたところ,PhotoShopを使って作成したTIFFなら読めますが, Canonのカメラで撮ってCanonのソフトでTIFFにしたものでは確かにmemcpy()で落ちました. 後者のCanonのものはPhotoShopでも開くことができなかったです. 「書類を開けません.これはTIFFファイルではありません,」 というエラーが出ます.けど,イメージングでは開きます. TIFFフォーマットは共通ではないのですかねぇ・・・

  • kmb01
  • ベストアンサー率45% (63/138)
回答No.5

libtiffは触ったことないのですが… No.2補足より >しかし,保存されたビットマップはペイントで開くと, >元がどんなTIFF画像であろうと真っ白になってしまいます. バイナリエディタで開いて正しいBmpファイルが書き込まれているか 確認してください。 No.1補足より、 >nSizeには値は入ってました. >640×480の画像でnSize = 921640でした. >これは多分あってますよね…?? 640*480 =307200 640*480*3=921600 このことからおそらくsizeof(BITMAPINFOHEADER)=40で 各ピクセルに3バイト(24ビット)、 カラーテーブルなしのビットマップを LoadTIFFinDIBが作成したと推測しました。 カラーテーブルなしのため、 >BMPFILEHEADER.bfOffBits = (DWORD)(sizeof( BMPFILEHEADER ) + >BMPINFOHEADER-> >biSize + nColors * sizeof(RGBQUAD)); のnColors * sizeof(RGBQUAD))は余分です。 #通常の24ビットBmpファイルのbfOffBits部分に2^24*4を 加算するとペイントでは真っ白の画面が出力されるようになります。

mkmarimo
質問者

お礼

ご回答ありがとうございます. ご指摘のとおりやってみたらうまくいきました. ありがとうございました.

noname#9119
noname#9119
回答No.4

sizeof(BMPFILEHEADER)は4になるような。

  • mcWalker
  • ベストアンサー率69% (27/39)
回答No.3

様子が分からないので、探してやっと tiff2dib.c を落としてきました。 #2さんがご指摘の BMPINFOHEADER へ入れるところで、 GlobalLock() がないと、hDIB が示す場所が不定です。 tiff2dib.c の CreateDIB() をご覧になると、hDIB の 内容が分かると思います。ヘッダを格納するところで、 GlobalLock() やってますよね。 尚、この関数の最後に hDIB を解放しないといけません。

mkmarimo
質問者

お礼

>様子が分からないので、探してやっと tiff2dib.c を落としてきました。 わざわざありがとうございます. hDIBは最後にちゃんと解放します. 今はまた下の補足のように別件で悩んでます. なかなかゴールまでたどり着けません・・・

  • taka_tetsu
  • ベストアンサー率65% (1020/1553)
回答No.1

nSizeの値はちゃんと入ってますか? GlobalSize()で取れるのかなぁ? 不精しないでsizeof(BITMAPINFOHEADER) + ビット列のバイト長にしないといけないのでは? #変数名を大文字だけで書くのはやめたほうが。 非常に見づらいです。 特にBMPINFOHEADERがポインタなのに・・・

mkmarimo
質問者

補足

ご回答ありがとうございます. nSizeには値は入ってました. 640×480の画像でnSize = 921640でした. これは多分あってますよね…?? デバッグしてみると,どうやらhDIBがBITMAPINFOHEADERの先頭を指してないみたいなのですが, 原因はそれですよねぇ… hDIBがいったいなにを指しているのかわかりません. LoadTIFFinDIB()が返すものは何なのでしょう…?? 分かる方ご教授ください. >#変数名を大文字だけで書くのはやめたほうが。 >非常に見づらいです。 >特にBMPINFOHEADERがポインタなのに・・・ 気をつけます.

関連するQ&A

  • bitmap画像の保存がうまくいきません。

    VC++のMFC、ダイアログベースで画像処理のソフトを作っています。 処理した画像を保存したいのですが、「描画できませんでした」というメッセージのでるファイルになってしまい、うまく保存できません。 プログラムは以下のようになっていてピクチャーコントロールの変数をm_pict8にしています。また、画像は24ビットで240×320のものを保存します。 static LONG CalcScanLineByte(const LONG w, const WORD bpp) { return (((bpp * w) + 31) / 32) * 4; } //----------------------------------------------------------- void Cstart2Dlg::OnBnClickedButton10() //保存ボタン { CFileDialog myDLG(FALSE,"BMP","*.BMP",OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,"画像(*.BMP)|*.BMP||"); if(myDLG.DoModal() == IDOK){ CStdioFile fout(myDLG.GetPathName(),CFile::modeCreate | CFile::modeWrite|CFile::typeBinary); //ピクチャボックスからビットマップを取り出す HBITMAP hBitmap = m_pict8.GetBitmap(); //無いので処理できない if(hBitmap == NULL)return; //ビットマップの情報を取る BITMAP bitmap = {0}; ::GetObject(hBitmap, sizeof(bitmap), &bitmap); //4バイト調整したスキャンラインのサイズ const int iScanLineByte = ::CalcScanLineByte(240, bitmap.bmBitsPixel); //const int iScanLineByte = ::CalcScanLineByte(bitmap.bmWidth, bitmap.bmBitsPixel); //ファイルヘッダとビットマップヘッダ BITMAPFILEHEADER bmfh = {sizeof(bmfh)}; BITMAPINFOHEADER bmif = {sizeof(bmif)}; //ビットマップである事を示す名称 bmfh.bfType = ('M' << 8) | 'B'; //イメージデータへのオフセットはファイルヘッダ+ビットマップヘッダ bmfh.bfOffBits = sizeof(bmfh) + sizeof(bmif); //見ての通り bmif.biBitCount = 24; //24ビットの時はBI_RGBで固定 bmif.biCompression = BI_RGB; //常に1で固定 bmif.biPlanes = 1; //見ての通り bmif.biWidth = 240; bmif.biHeight= 320; //bmif.biWidth = bitmap.bmWidth; //bmif.biHeight= bitmap.bmHeight; //4バイト調整も含めた正確な合計バイトサイズが必要 bmif.biSizeImage = iScanLineByte * bmif.biHeight; //ファイルヘッダ→ビットマップヘッダの順番に書き出す fout.Write(&bmfh, sizeof(bmfh)); fout.Write(&bmif, sizeof(bmif)); //イメージデータをセーブする HDC hMemDC = ::CreateCompatibleDC(NULL); ::SelectObject(hMemDC, hBitmap); //yを縦幅-1から回転させないと上下逆転してしまう for(int y = bmif.biHeight - 1; y >= 0; --y) { //3バイトずつステップする for(int x = 0; x < iScanLineByte; x += 3) { const COLORREF cref = ::GetPixel(hMemDC, x / 3, y); //色素の位置関係をBGRにしないと赤と青の関係が逆転してしまう const BYTE arrBy[3] = {GetBValue(cref), GetGValue(cref), GetRValue(cref)}; //1ピクセル分(3バイト)書き出す fout.Write(arrBy, sizeof(arrBy)); } } ::DeleteDC(hMemDC); } どこか改善点などありましたら、よろしくお願いいたします。

  • BITMAPFILEHEADER bfSize

    DIBからBMPファイルを作成する処理の一部です。 int nImageSize = pDIB->biSizeImage + (sizeof(RGBQUAD)*dwPaletteSize)+sizeof(BITMAPINFOHEADER); BITMAPFILEHEADER bmpFIH = {0}; bmpFIH.bfType = ( (WORD) ('M' << 8) | 'B'); bmpFIH.bfSize = nImageSize + sizeof(BITMAPFILEHEADER); bmpFIH.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(sizeof(RGBQUAD)*dwPaletteSize); fwrite(&bmpFIH, 1, sizeof(BITMAPFILEHEADER), pFile); fwrite(pDIB, 1, nImageSize, pFile); fclose(pFile); pFile = 0; BMPファイルは作成されますが、メモリリークします。 bmpFIH.bfSize を 0 またはそれに近い小さな数字にすると、メモリリークは検出されません。 bmpFIH.bfSize はファイルサイズのようですが、これが入っていないと問題でしょうか? また入れるとすればどのような数字を入れるべきでしょうか?(メモリリークの原因も含めて) 宜しくお願いします。 開発環境:VC++ 2008 MFC

  • ファイルへの書き込み処理について

    C++の処理で分からないことがあります。 アドバイスを頂けますようよろしくお願い致します。 以下のプログラムがある場合、 どのような時にエラー処理が実行されるのでしょうか? 例えば、書き込み対象のファイル「C:\WORK\20060508.txt」が既に秀丸等で開かれている状態でもエラーは発生するのでしょうか? TRY { CFile file(strLogFilePath, CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate); file.SeekToEnd(); file.Write(strText, strText.GetLength()); file.Close(); } CATCH(CFileException, e) { // エラー処理 } ※補足 strLogFilePath=C:\WORK\20060508.txt strText=テストです。

  • 画像の保存方法。

    VC++のMFC、ダイアログベースで画像処理のソフトを開発している者です。 ピクチャーコントロールに画像を表示させて、その画像を保存したいと思っています。 保存用のダイアログを開き、名前を付けて保存したいのですがうまくいきません。 下が今作っているプログラムです。 void Cstart2Dlg::OnBnClickedButton10() { CFileDialog myDLG(FALSE,"BMP","*.BMP", OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, "画像(*.BMP)|*.BMP||"); if(myDLG.DoModal() == IDOK){ CStdioFile fout(myDLG.GetPathName(),CFile::modeCreate |CFile::modeWrite|CFile::typeBinary); UpdateData(TRUE); fout.WriteString(m_pict8); fout.WriteString("\xla"); filename = myDLG.GetPathName(); SetWindowText("start2"+filename); } ピクチャーコントロールの変数をm_pict8にしています。テキストファイルを保存するプログラムを参考にして作っているので間違っているのだと思います。 画像の保存方法が分かる方がいらっしゃいましたらよろしくお願いいたします。

  • RAID0に高速で書き込む方法(補足2)

    昨日から、たびたび質問させていただいています。 困っているので、教えてください。 下に、ハードディスクにデータを取り込むテストプログラムの一部を載せます。不必要と思われる部分は省かせていただいたので、少々バグが含まれていたり、変数の意味がわからないところがあるかもしれませんが、ご勘弁ください。 CFile file( m_strPath, CFile::modeCreate | CFile::modeReadWrite ); DWORD dwSize; dwSize = 2000000000; file.SetLength( dwSize ); file.SeekToBegin(); DWORD dwTick = GetTickCount(); DWORD aSize = 32 * m_dwSectorsPerCluster * m_dwBytesPerSector; char* buf = new char[ aSize ]; if( buf != NULL ) { DWORD pos; for( pos = 0; pos < dwSize; pos += aSize ) { file.Write( buf, aSize ); } CString str; str.Format( "%d ms for %g MB", GetTickCount() - dwTick, dwSize * 0.001 * 0.001 ); AfxMessageBox( str ); } 最後に書き込んだ大きさと、かかった時間を表示しています。 よろしくお願いします。

  • CFileの使い方を教えてください

    環境はWIN98 VC++6.0 MFCです。 見よう見真似で以下のようにしたら、配列のデータがXXX.txtファイルに保存されました。 それじゃってことで、XXX.txtファイルからデータを取り出し、元通りに配列aa[n]に格納するにはどうすれば良いのですか? そう言うことはやれるのですか? void CAbcDlg::OnButton4() {  // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください  CString str;  LPTSTR pszBuf;  long size;   CFile fpw( "XXX.txt", CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive );   for(n=0;n<10;n++){    str=aa[n];    size = str.GetLength();    fpw.Write( &size, sizeof( long ) );    //バッファの確保    pszBuf = str.GetBuffer( size );   //書き込み    fpw.Write( pszBuf, size );   //バッファの開放   str.ReleaseBuffer();  } }

  • Windowsファイルのサイズの問題

    ネットでWindowsのファイルのサイズを計算するサンプルをみています。 ひとつのサンプルをみても、わからないですので、知っている方に質問したいです。 サンプルは WFDはWIN32_FIND_DATA構造体の変数で、 DWORD dwHigh = WFD.nFileSizeHigh; DWORD dwLow = WFD.nFileSizeLow; __int64 nSize; int nLen; char szTemp[MAX_PATH]; char* p; int nCnt; nSize = dwHigh; nSize = (nSize << 32) + 1023; nSize += dwLow; nSize = (nSize >> 10); //KB単位にする いくつの問題があります。 ●ファイルのサイズは、どうしてこんなに計算するの?  どう計算するの? ●(nSize << 32) とnSize >> 10 はなにをやっているの? よろしくお願いします。

  • 連続フレーム画像を保存について

    現在、USBカメラから撮ったフレーム画像を保存するといったプログラムを作成しています。やりたいのはUSBカメラから撮った全てのフレーム画像(例:300フレーム=300枚画像)の保存ですが、今はキーボートを押されるたび画像の保存しかできない状態です。とっても困っています。どうか教えていただけませんか?よろしくお願いします。 while(1){ int v = 0; hr = pGrab -> GetCurrentBuffer( &n, (long *)buffer );// グラブ if( kbhit() ){  getch();//kbhitで取得したキーの破棄  hr = pMC -> Pause(); memset(&bmphdr, 0, sizeof(bmphdr));  bmphdr.bfType = ('M' << 8) | 'B'; bmphdr.bfSize = sizeof(bmphdr) + sizeof(BITMAPINFOHEADER) + n; bmphdr.bfOffBits = sizeof(bmphdr) + sizeof       (BITMAPINFOHEADER);  while(1){ v++; char filename[300];   _snprintf(filename, sizeof(filename),"%d.bmp", v); fh = CreateFile(filename,GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); WriteFile(fh, &bmphdr, sizeof(bmphdr), &nWritten, NULL); WriteFile(fh,&pVideoHeader->bmiHeader, sizeof(BITMAPINFOHEADER), &nWritten, NULL); WriteFile(fh, buffer, n, &nWritten, NULL); CloseHandle(fh); pMC -> Run(); pGrab -> SetBufferSamples(FALSE); if(kbhit()){ break; }  }  printf( "グラブ停止:終了 -- 0 、再開 -- 1 、処理 -- 2\n" );  scanf("%d",&num); }

  • 【MFC】CFileでSeekした位置から値出力したい

    【MFC】CFileでSeekした位置から値出力したい 環境:WindowsXP(SP2)、VisualStudio2005、MFC CFileの「open」を利用して、ファイルをオープンし、 (この時の指定モードはCFile::modeCreate | CFile::modeNoTruncate | CFile::modeWriteです) 指定した位置にseekし、そこへCFileの「Write」で書き込みを行っています。 Seek位置を「SeekToEnd()」で最後へ移動した際は問題ないのですが、途中へSeekし、書き込みを行った際に、そこから上書きされてしまいます。 「挿入」という形で途中にデータを書き込む方法は無いでしょうか? ※特にMFCにこだわっているわけではないので、何かご存知の方がおられましたらご教授お願いいたしますm(__)m

  • MFCを使うための約束事は?

    MFCをつかって、ファイルが存在しているかチェックする プログラムを作ったのですが、上手くいきません。 test.cppは以下のように書いています。 #include "test.h" int _tmain(int argc, _TCHAR* argv[]) { CFile f; CFileException e; if( !f.Open(argv[1], CFile::modeCreate | CFile::modeWrite, &e)) { return 0; } else { f.Close(); return 1; } } test.hは以下のように書いています。 #pragma once #include <afxwin.h> #include <afxext.h> #include <afxole.h> #include <afxdisp.h> どなたかご教授下さい。