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

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

VC++プログラムをつかったBMP画像の拡大縮小について

現在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

  • フォトショップでの画像の縦横の比を変えないで拡大縮小をしたい

    フォトショップ5.5を使用しています。今合成に挑戦しているのですが、コピーペーストまではできました。でも切り取った画像を拡大縮小しようとしたら、縦横の比が変わってしまい、人物が太ったり痩せたりします。拡大縮小を縦横の比を変えないでする方法を教えて頂きたいのです。よろしくお願い致します。

  • 画像をきれいに拡大する

    ベクター系のソフトを使えば、画像をきれいに拡大できると書いてあったのですが(限界はあるでしょうが)、それは「bmp」や「jpg」などから直接拡大を行えばいいということではないようですね。 大まかにいえば、ビットマップをトレースし、ベクター変換してから拡大すればきれいに拡大できるということでしょうか? 回答よろしくお願いします。

  • JPG画像の拡大

    JPG画像を拡大すると画質が悪化しますが拡大前にJPGをbmpに変換しこれを拡大して再度JPGに変換した場合画質はどうなるでしょうか。

  • 画像の縮小

    画像を20%にくらい縮小して、 自分のホームページへアップロードしたいのですが、 「画像編集ツール」で開いて 「サイズ変換」で小さくすると どうしても文字などが潰れてしまい、読めなくなってしまいます。 「CatPunch」というソフトを使って、 拡張子を「bmp」や「gif」「jpg」にしてみたのですが、 「エラー:画像を開くことができませんでした」 になってしまいます。 でも開ける画像もあります。 原因なんだかわかりません。 何か画像を縮小するソフトや良い方法をお願いいたします。

  • jpg画像を縮小すると、拡大した時元に戻りますか

    何処で質問したものかよくわかりませんでしたので、ここで質問させてもらいます。 jpg画像を縮小するということは、圧縮するということなのでしょうか? jpgは非可逆な圧縮方法なので、例えば画質50で圧縮したものは、画質100にしても、元通りにはならない、という風に聞きましたが、正しいでしょうか? で、縦横サイズの縮小をしたものを、元の大きさに拡大したとしたら、やはり元通りの画質には戻りませんか? つまり、縮小するということは、圧縮もしているということでしょうか? 混乱しています、是非教えてください。

  • イラストレーターで「配置した画像の拡大・縮小・回転はできるだけ避けてく

    イラストレーターで「配置した画像の拡大・縮小・回転はできるだけ避けてください」はなぜ? イラストレーター初心者です。 チラシの印刷データを印刷会社へ入稿しようとしています。 写真画像をフォトショップでEPS保存してイラストレーターのテンプレートに配置しています。 入稿の注意事項に、イラストレーターで「配置した画像の拡大・縮小・回転はできるだけ避けてください」という文言がありました。 拡大・縮小は画像が荒れそうな気がするので避けるのはわかるのですが、回転はなぜだめなのでしょうか?

  • 沢山の画像を縮小表示して一つにまとめたい

    沢山の画像を縮小表示して一つにまとめたい 沢山の画像を縮小表示して一つにまとめたいんですが、どうすればいいでしょうか? 説明が分かりにくかったらすみません。 沢山ある画像100枚くらいを縮小した表示のままで一枚の画像(jpg,BMP)などに変換したいんですが、どうすればいいでしょうか? (分かりやすく言うと、写真でいうインデックスを簡単に作りたい) 今はウィンドウスの縮小表示したものをキャプチャして、画像化しているんですが、かなり手間がかかるのと、 一枚に収まりきらない場合があるので、ソフトで一括で書き出してくれるようなものはないでしょうか? 出来ればフリーソフトがいいですが、そんなソフトはありますか? かなり困っているので、よろしくお願いします。m(_ _)m

  • 図面の拡大、縮小

    UNIXのGTK+でちょっとしたCADを作っています。 その際、図面に書いたデータを始点、終点をデータとして格納して保存するようにしたいと思います。 このプログラムで、その図面を拡大縮小したいとき、その格納したデータを画面上の座標(つまりデータとしての絶対的な座標を画面に表示するための相対的な座標に変換する)に変換する方法が思い浮かばなくて困っています。 何かいいアイデアは無いでしょうか? よろしくお願いします。

  • Flashで画像を拡大縮小する時の画像の荒れを防止する方法を教えてください。

    Flashで、「画像にマウスが乗ると拡大する」というムービークリップを作っています。 画像は、拡大した時のサイズで作成し、Flashに読み込んでいるのですが、 ファイルサイズを縮小する時、プレビューすると画像が荒れてしまいます。 それを回避する方法教えていただければとても助かります。 拡大縮小の指示は(画像のMCに) onClipEvent(load){ wideX = [245,300]; a = 0; function move(x){ a = x; } } onClipEvent(enterFrame){ this._height = 0.666*this._width; //縦横比を固定 this._width += (wideX[a] - this._width)/3; } です。 横幅300pxで取り込んだ画像の横幅を、245pxか300pxにしています。

    • ベストアンサー
    • Flash
  • 読み込んだBMPデータの行方

    参考書を元にBMPを読み込み、 BYTE*型にデータを移して、画像処理や転送に利用できるようにしたいのですが、 どこに画像データの実体が有るのかがよくわかりません・・。 ---------- static LPBYTE lpDIB = NULL; static LPBITMAPINFO lpbiInfo; static LPDWORD lpPixel; LPBYTE lpBMP; LPBITMAPINFOHEADER lpbiBMPInfo; LPBYTE lpBMPPixel; BYTE* ppp; static int iWidth, iHeight, iLength; int iFileSize; DWORD dwOffset; int i, j; HANDLE fhBMP; DWORD dwRead; HDC hdc; PAINTSTRUCT ps; /*BMP取得*/ //ファイルオープン fhBMP = CreateFile("test.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fhBMP == INVALID_HANDLE_VALUE) { MessageBox(NULL, "test.bmpが見つかりません。", "エラー", MB_OK); return 0; } //ファイルサイズ取得 iFileSize = GetFileSize(fhBMP, NULL); //ファイル読み込みバッファ確保 lpBMP = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, iFileSize); //ファイル読み込み ReadFile(fhBMP, lpBMP, iFileSize, &dwRead, NULL); //ファイルを閉じる CloseHandle(fhBMP); //BMP内のBITMAPINFO取得 lpbiBMPInfo = (LPBITMAPINFOHEADER) (lpBMP + sizeof(BITMAPFILEHEADER)); //先頭からピクセル列までのオフセット取得 dwOffset = *(LPDWORD)(lpBMP + 10); //BMP内ピクセル列の先頭アドレス計算 lpBMPPixel = lpBMP + dwOffset; //ビットマップの大きさ取得 iWidth = lpbiBMPInfo->biWidth; iHeight = lpbiBMPInfo->biHeight; //BMPピクセル列の1ラインの長さを計算 if (iWidth % 4 == 0) { iLength = iWidth * 3; } else { iLength = iWidth * 3 + (4 - (iWidth * 3) % 4); } //DIB用バッファを確保 lpDIB = (LPBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFO) + iWidth * iHeight * 4); //DIB用ポインタ分配 lpbiInfo = (LPBITMAPINFO)lpDIB; lpPixel = (LPDWORD)(lpDIB + sizeof(BITMAPINFO)); //BITMAPINFO設定 lpbiInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); lpbiInfo->bmiHeader.biWidth = iWidth; lpbiInfo->bmiHeader.biHeight = iHeight; lpbiInfo->bmiHeader.biPlanes = 1; lpbiInfo->bmiHeader.biBitCount = 32; lpbiInfo->bmiHeader.biCompression = BI_RGB; //BMP内のピクセル列を32ビット化してコピー for (i = 0;i < iHeight;i++) for (j = 0;j < iWidth; j++) CopyMemory(lpPixel + j + i * iWidth, lpBMPPixel + j * 3 + i * iLength, 3); //ファイル読み込みバッファ解放 HeapFree(GetProcessHeap(), 0, lpBMP); /*描画*/ hdc = BeginPaint(hWnd, &ps); //DIBをウインドウのDCに描画 StretchDIBits(hdc, 0, 0, iWidth, iHeight, 0, 0, iWidth, iHeight, lpPixel, lpbiInfo, DIB_RGB_COLORS,SRCCOPY); EndPaint(hWnd, &ps); ---------- 描画部分のStretchDIBits()を調べて lpPixelに格納されているように思えたのですが、これはただのDWORDですし。 lpBMPPixelだとしても、描画では全く使われていないのが不可解で。 なぜこう(描画にDWORD)なっているのでしょうか? どこに画像データが有るのでしょうか?