• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:VC++プログラムをつかったBMP画像の拡大縮小について)

VC++プログラムでBMP画像の拡大縮小を実現する方法と原理

このQ&Aのポイント
  • VC++を使用してBMP画像を拡大縮小する方法について教えてください。
  • BITMAPINFOHEADER構造体の中のbiWidthやbiHeightの値を変更するだけで指定した幅と高さに変換された画像データが得られますか?また、画像の輝度情報には何かしらの補正が加えられていますか?
  • 拡大縮小が行われる際、画像データは単純に輝度を抜いたりするだけなのか、それとも滑らかな輝度情報を持つように補正が加えられているのか、その原理について詳しく教えてください。

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

  • ベストアンサー
  • MrBan
  • ベストアンサー率53% (331/615)
回答No.2

> 実際に拡大縮小された画像のデータ(バッファ)がどこに格納されたのかわかりません・・・。 一般的には、メモリやVRAMに載ります。取得もできますが、DIBSectionを使うと、メモリとして参照しやすいです。 > また、StretchBlt関数は、描画を目的としているようなのですが、今回の場合、 > -- snip -- > 描画は無駄な手間のように思われます。 実際の画面に表示するかにかかわらず、WindowsのAPIにおける画像の操作は原則として「DC」という概念を使います。 メモリやプリンタもDCの一種と扱われますし、必ずしも画面描画は意味しません。 ビットマップに何か書くのも、DCと関連付けてそこに描画する仕組みです。 メモリ上で拡大縮小するということは、結局のところ突き詰めれば 元画像を読んで拡大縮小した画像をそこ(メモリ上)に描画している、 ということだと思いますが、いかがでしょう。 # C言語の入出力がコンソールにもファイルにも使えるようにFILE*を使うのとかと大差ない、 # DCってのはWindowsにおける「描画先」の抽象化された概念です。 > 何かスマートな方法はないものでしょうか? DCという概念自体はWindows GDIの根底ですから、WindowsのAPIである限り、 他を使ってもIFは違えど内部的にはDCと大差ないと思われますが。 「質問者さんにとってのスマート」が不明ですが、自前で変換処理を書くか、 完全独自処理の画像変換ライブラリを持ってくるのがスマートですか。

xyz21takku
質問者

お礼

なるほど!ありがとうございます。 「DCはペンやブラシなどを持つための手で、このDCはこの色のペンとブラシをもつ」との決まりが書かれているものがデバイスコンテキストだと思っていました。まずはデバイスコンテキストの概念からきちんと勉強します。 何度もありがとうございました。

xyz21takku
質問者

補足

MrBanさんにご指導いただいたことを参考に、プログラムを書いてみました。img1構造体に格納されたビットマップを、伸縮してimg0構造体に格納するプログラムのつもりです。 このプログラムでは、すでにimg1構造体にビットマップ情報が格納された、として話を進めています。というのも、ビデオカメラで撮影することを目的として考えているため、専門の参考書をよんで、バッファを得るところまではプログラムが完成しているからです。 それから、遅くなりましたが、「スマート」という言葉は、「動画処理のためにわずかでも高速で処理してくれるよう、二度手間のようなことはせず、最適なプログラムにするためには?」という意味で使いました。言葉が足らず、ご迷惑をおかけしました。 まだまだ見苦しいプログラムかもしれませんが、丸一日かかってデバイスコンテキストの概念について勉強しました。間違っているところがあれば、ご指導いただけるとうれしいです。 /**********************************************************/ //グラフィック用ウィンドウのデータをまとめた構造体 typedef struct{ HINSTANCE hi; int x;//表示開始位置 int y; HWND hwnd;//自分のウィンドウハンドル BYTE *lpBmpData;//BMPのデータ部分 BITMAPINFOHEADER bih; BITMAPFILEHEADER bfh; }IMG0;//クラス名 /************************************************************/ ・・・・・・ IMG0 img0;//伸縮後のビットマップを格納する IMG0 img1;//元のビットマップが格納されている HDC bihdc; static HDC hMemDC;//メモリデバイスコンテキスト HBITMAP hBitmap; //1.デバイスコンテキストを作成 bihdc = GetDC ( img1.hwnd ); //2.GDIビットマップ(DDB)を作成する hBitmap = (HBITMAP) CreateDIBitmap ( bihdc,//デバイスコンテキストのハンドル img1.bih, 0,NULL,NULL,0 ); //3.メモリデバイスコンテキストを作成 hMemDC = CreateCompatibleDC ( bihdc ); //4.メモリデバイスコンテキストの内容をhBitmap(DIB)で初期化? SelectObject ( hMemDC, hBitmap ); //5.伸縮モードの変更 SetStretchBltMode ( hMemDC, COLORONCOLOR ); //6.拡大縮小(たとえば縦横とも1/2にする) hBitmap = StretchBlt ( hMemDC, 0,0, (img1.bih.biWidth)/2,(img1.bih.biHeight)/2, bihdc, 0,0, img1.bih.biWidth,img1.bih.biHeight, SRCCOPY ); //7.ビットマップの選択 SelectObject ( hMemDC, hBitmap ); //8.メモリデバイスコンテキストの内容をバッファにコピー img0.lpBmpData = (BYTE*) GetDIBlts ( hMemDC, hBitmap, 0,(img1.bih.Height)/2, img0.lpBmpData, img0.bih, DIB_RGB_COLORS ); //9.ビットマップやメモリデバイスコンテキストの削除 DeleteObject ( hBitmap );//ビットマップの削除 ReleaseDC ( img1.hwnd, bihdc ); DeleteDC ( hMemDC ); ・・・・・・・ /*********************************************************/ 以上のようなステップでバッファに格納するようにしました。 自信はまったくありません。 SelectObjectをステップ4と7で二回もする必要があるのかどうかすらわかりません。また、ステップ9のDeleteObjectの位置も正しいのかわかりません。 また、今回目的が動画処理ということで、高速処理が要求されるので、上記の方法が適切か、それとも、もっと高速で伸縮処理してくれる関数があるのか、ご存知の方がいらっしゃったら、ご指導いただけないでしょうか。 本当にあつかましくてすいませんが、よろしくお願いします。

その他の回答 (2)

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.3

CreateDIBSectionを使う方がメモリ参照が多少効率的になるかもしれません。 また、SelectObjectは引数で以前のObjectを返します。 7 のSelectObjectは、4のSelectObjectした戻り値を保存しておいて再設定する必要があります。 // 処理イメージ old = SelectObject(new); // 変更してから使う ... SelectObject(old); // 使い終わったら戻しておく BitmapをDeleteする時点で、DCにSelectされたままだと使用中のためDeleteに失敗し、 リソースがリークします。(Deleteでエラーが返っていませんか?) 更に、8より後に書くべきかと思います。(8でまだDCとBitmapを使ってるので) # 動画処理が前提だと、元データ形式にもよりますが、 # DirectX等で伸張することを考えた方がいいかもしれません。 # 静止画の処理効率としてはDIBSectionにすればそう悪くないと思います。

xyz21takku
質問者

お礼

何度もご意見いただき、本当にありがとうございます。 上に、DIBSectionを使った前回と同じようなプログラムを作ってみたので、よろしければ見てやってください。 MrBanさんのおかげで、少しずつですがAPIプログラミングのイメージがつかめてきたようなきがします。 ありがとうございます。

xyz21takku
質問者

補足

DIBSectionを勉強するのに時間がかかってしまい、返信が遅れてしまいました。この間のプログラムをDIBSection用に改良しました。 これもまた見苦しいプログラムかもしれませんが、間違い等ありましたらご指導いただけるとうれしいです。 HBITMAP hBitmap,copyhMemDC; HDC hMemDC,hOld; BITMAPINFO bmpInfo,copybmpInfo; int X_SIZE,Y_SIZE; int REF_X_SIZE,REF_Y_SIZE;//拡大縮小したいサイズを指定 LPVOID lpPixel,lpBmpData; //1.まず、BMP画像をDIBSectionとして読み込む //インターネットを参考に関数を作成 CreateDIBSection32FromFile ( infile,//入力画像BMP &hBitmap,//入力画像のDDIへのポインタ &lpPixel,//入力画像のDIBへのポインタ(バッファ) &bmpInfo );//BITMAPINFO構造体に画像情報が格納される //2.X_SIZE、Y_SIZEにBMP画像のサイズを指定した X_SIZE = bmpInfo.bmiHeader.biWidth; Y_SIZE = bmpInfo.bmiHeader.biHeight; //3.メモリデバイスコンテキストの作成 hMemDC = CreateCompatibleDC ( NULL ); copyhMemDC = CreateCompatibleDC ( NULL ); //4.hBitmapの選択 SelectObject ( hMemDC, hBitmap) hOld = SelectObject ( copyhMemDC, hBitmap ); //5.伸縮モードの変更 SetStretchBltMode ( copyhMemDC, COLORONCOLOR ); //6.拡大縮小(縦幅が参照画像と等しくなるように) StretchBlt (copyhMemDC,0,0, REF_X_SIZE,REF_Y_SIZE,//拡大縮小したいサイズを指定 hMemDC,0,0,X_SIZE,Y_SIZE,SRCCOPY ); //7.BITMAPINFO構造体の情報を設定 copybmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); copybmpInfo.bmiHeader.biWidth = REF_X_SIZE; copybmpInfo.bmiHeader.biHeight = REF_Y_SIZE; copybmpInfo.bmiHeader.biPlanes = 1; copybmpInfo.bmiHeader.biBitCount = BITCOUNT; copybmpInfo.bmiHeader.biCompression = BI_RGB; //8.hBitmapの選択解除 SelectObject (copyhMemCD,hOld); //9.メモリデバイスコンテキストの内容をバッファにコピー //バッファとして伸縮後の情報を得ることが今回の目的なので GetDIBlts (copyhMemDC,hBitmap, 0,REF_Y_SIZE, lpBmpData,//バッファとして伸縮後の情報を格納 copybmpInfo.bmiHeader,DIB_RGB_COLORS ); } //10.DCの削除 DeleteDC ( hMemDC ); DeleteDC ( copyhMemDC ); //DIBSectionの削除 DeleteDIBSection32 ( &hBitmap );//DeleteObjectしている GetDIBltsにhBitmapを渡す前に、SelectDbjectを解除しなければならないとあったので、 SelectObject ( copyhMemCD, hOld ); としてみました。本当は、 StretchBlt (hMemDC,0,0,REF_X_SIZE,REF_Y_SIZE, hMemDC,0,0,X_SIZE,Y_SIZE,SRCCOPY ); として、hMemDCを上書きしたかったのですが、それだとSelectObjectを解除できないので、上のような方法をとってみましたが、 もし、copyhMemDCを作らずにhMemDCに上書きする方法がありましたら ご教授いただけるとうれしいです。

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.1

DCというものを調べて、StretchBlt等を使ってください。 補正はSetStretchBltModeを。 なお、ヘッダ構造体のサイズを変えた場合、画像を伸張するわけではなく、 単にトリムしたりするだけです。

xyz21takku
質問者

お礼

どうもありがとうございます! 早速StretchBltとデバイスコンテキストについて調べてみました。そしたら、申し訳ないのですが、新しくわからないことがでてきてしまいました。 画像処理関係のプログラムは初めてなので、変なことを聞いてしまっていたらすいません。 StretchBlt関数等を使うと、画像を任意のサイズに拡大縮小できるということはわかったのですが、実際に拡大縮小された画像のデータ(バッファ)がどこに格納されたのかわかりません・・・。 また、StretchBlt関数は、描画を目的としているようなのですが、今回の場合、たとえば、「2倍、3倍にした画像データをバッファに一時格納して、必要とあれば、その時ファイルに書きだす」等の作業をしたいので、描画は無駄な手間のように思われます。 何かスマートな方法はないものでしょうか? もしよろしければ、これらの質問にもお答えいただけないでしょうか。 お願いします。お手数をかけて申し訳ありません。

xyz21takku
質問者

補足

上で書いたプログラムについて、自分で間違っていると思われるところを発見したので、訂正したかったのですが、訂正の仕方がわからなかったため、このような形でのコメントとなってしまいました。申し訳ないです。 /**********************************************************/ ・・・・・・ IMG0 img0;//伸縮後のビットマップを格納する IMG0 img1;//元のビットマップが格納されている HDC bihdc; static HDC hMemDC;//メモリデバイスコンテキスト HBITMAP hBitmap; //1.デバイスコンテキストを作成 bihdc = GetDC ( img1.hwnd ); //2.GDIビットマップ(DDB)を作成する hBitmap = (HBITMAP) CreateDIBitmap ( bihdc,//デバイスコンテキストのハンドル img1.bih, 0,NULL,NULL,0 ); //3.メモリデバイスコンテキストを作成 hMemDC = CreateCompatibleDC ( bihdc ); //4.メモリデバイスコンテキストの内容をhBitmap(DIB)で初期化? SelectObject ( hMemDC, hBitmap ); //5.伸縮モードの変更 SetStretchBltMode ( hMemDC, COLORONCOLOR ); //6.拡大縮小(たとえば縦横とも1/2にする) StretchBlt ( hMemDC, 0,0, (img1.bih.biWidth)/2,(img1.bih.biHeight)/2, bihdc, 0,0, img1.bih.biWidth,img1.bih.biHeight, SRCCOPY ); //7.メモリデバイスコンテキストの内容をバッファにコピー img0.lpBmpData = (BYTE*) (すいません省略) GetDIBlts ( hMemDC, hBitmap, 0,(img1.bih.Height)/2, img0.lpBmpData, img0.bih, DIB_RGB_COLORS ); //8.ビットマップやメモリデバイスコンテキストの削除 DeleteObject ( hBitmap );//ビットマップの削除 ReleaseDC ( img1.hwnd, bihdc ); DeleteDC ( hMemDC ); ・・・・・・・ /*********************************************************/

関連するQ&A