プロが教える店舗&オフィスのセキュリティ対策術

現在VC++をつかった画像を扱うプログラミングを学習中なのですが、画像データの取り扱いについてわからないことがあるため教えていただきたいです。

BMP画像をバッファに取り込んだあとで、画像の縦横を拡大縮小したデータを新たなバッファに格納したいのですが、画像を拡大縮小する方法がわかりません。いくつかのプログラムのソースを見させていただいたのですが、BITMAPINFOHEADER構造体の中のbiWidthやbiHeightの値を変えているだけのようなのです。

これらの値を変えるだけで、指定した幅と高さに変換された画像データが得られるのでしょうか?
また、それで拡大縮小されるならば、変換された画像データは輝度情報が滑らかになるように何かしらの補正が加えられていたりするのでしょうか?それとも、途中途中の輝度を単純に抜いていたりするだけなのでしょうか。
その辺の原理についても教えていただけるとうれしいです。

お手数をかけますが、よろしくお願いします。

A 回答 (3件)

> 実際に拡大縮小された画像のデータ(バッファ)がどこに格納されたのかわかりません・・・。



一般的には、メモリやVRAMに載ります。取得もできますが、DIBSectionを使うと、メモリとして参照しやすいです。

> また、StretchBlt関数は、描画を目的としているようなのですが、今回の場合、
> -- snip --
> 描画は無駄な手間のように思われます。

実際の画面に表示するかにかかわらず、WindowsのAPIにおける画像の操作は原則として「DC」という概念を使います。
メモリやプリンタもDCの一種と扱われますし、必ずしも画面描画は意味しません。
ビットマップに何か書くのも、DCと関連付けてそこに描画する仕組みです。

メモリ上で拡大縮小するということは、結局のところ突き詰めれば
元画像を読んで拡大縮小した画像をそこ(メモリ上)に描画している、
ということだと思いますが、いかがでしょう。

# C言語の入出力がコンソールにもファイルにも使えるようにFILE*を使うのとかと大差ない、
# DCってのはWindowsにおける「描画先」の抽象化された概念です。

> 何かスマートな方法はないものでしょうか?

DCという概念自体はWindows GDIの根底ですから、WindowsのAPIである限り、
他を使ってもIFは違えど内部的にはDCと大差ないと思われますが。
「質問者さんにとってのスマート」が不明ですが、自前で変換処理を書くか、
完全独自処理の画像変換ライブラリを持ってくるのがスマートですか。

この回答への補足

MrBanさんにご指導いただいたことを参考に、プログラムを書いてみました。img1構造体に格納されたビットマップを、伸縮してimg0構造体に格納するプログラムのつもりです。
このプログラムでは、すでにimg1構造体にビットマップ情報が格納された、として話を進めています。というのも、ビデオカメラで撮影することを目的として考えているため、専門の参考書をよんで、バッファを得るところまではプログラムが完成しているからです。

それから、遅くなりましたが、「スマート」という言葉は、「動画処理のためにわずかでも高速で処理してくれるよう、二度手間のようなことはせず、最適なプログラムにするためには?」という意味で使いました。言葉が足らず、ご迷惑をおかけしました。

まだまだ見苦しいプログラムかもしれませんが、丸一日かかってデバイスコンテキストの概念について勉強しました。間違っているところがあれば、ご指導いただけるとうれしいです。

/**********************************************************/
//グラフィック用ウィンドウのデータをまとめた構造体
typedef struct{
HINSTANCE hi;
int x;//表示開始位置
int y;
HWND hwnd;//自分のウィンドウハンドル
BYTE *lpBmpData;//BMPのデータ部分
BITMAPINFOHEADERbih;
BITMAPFILEHEADERbfh;
}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の位置も正しいのかわかりません。

また、今回目的が動画処理ということで、高速処理が要求されるので、上記の方法が適切か、それとも、もっと高速で伸縮処理してくれる関数があるのか、ご存知の方がいらっしゃったら、ご指導いただけないでしょうか。

本当にあつかましくてすいませんが、よろしくお願いします。

補足日時:2008/01/15 01:20
    • good
    • 0
この回答へのお礼

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

何度もありがとうございました。

お礼日時:2008/01/14 09:17

CreateDIBSectionを使う方がメモリ参照が多少効率的になるかもしれません。



また、SelectObjectは引数で以前のObjectを返します。
7 のSelectObjectは、4のSelectObjectした戻り値を保存しておいて再設定する必要があります。
// 処理イメージ
old = SelectObject(new); // 変更してから使う
...
SelectObject(old); // 使い終わったら戻しておく

BitmapをDeleteする時点で、DCにSelectされたままだと使用中のためDeleteに失敗し、
リソースがリークします。(Deleteでエラーが返っていませんか?)

更に、8より後に書くべきかと思います。(8でまだDCとBitmapを使ってるので)

# 動画処理が前提だと、元データ形式にもよりますが、
# DirectX等で伸張することを考えた方がいいかもしれません。
# 静止画の処理効率としてはDIBSectionにすればそう悪くないと思います。

この回答への補足

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に上書きする方法がありましたら
ご教授いただけるとうれしいです。

補足日時:2008/01/17 16:46
    • good
    • 0
この回答へのお礼

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

お礼日時:2008/01/17 16:57

DCというものを調べて、StretchBlt等を使ってください。


補正はSetStretchBltModeを。

なお、ヘッダ構造体のサイズを変えた場合、画像を伸張するわけではなく、
単にトリムしたりするだけです。

この回答への補足

上で書いたプログラムについて、自分で間違っていると思われるところを発見したので、訂正したかったのですが、訂正の仕方がわからなかったため、このような形でのコメントとなってしまいました。申し訳ないです。

/**********************************************************/
・・・・・・

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 );

・・・・・・・
/*********************************************************/

補足日時:2008/01/15 01:59
    • good
    • 0
この回答へのお礼

どうもありがとうございます!
早速StretchBltとデバイスコンテキストについて調べてみました。そしたら、申し訳ないのですが、新しくわからないことがでてきてしまいました。
画像処理関係のプログラムは初めてなので、変なことを聞いてしまっていたらすいません。

StretchBlt関数等を使うと、画像を任意のサイズに拡大縮小できるということはわかったのですが、実際に拡大縮小された画像のデータ(バッファ)がどこに格納されたのかわかりません・・・。

また、StretchBlt関数は、描画を目的としているようなのですが、今回の場合、たとえば、「2倍、3倍にした画像データをバッファに一時格納して、必要とあれば、その時ファイルに書きだす」等の作業をしたいので、描画は無駄な手間のように思われます。
何かスマートな方法はないものでしょうか?

もしよろしければ、これらの質問にもお答えいただけないでしょうか。
お願いします。お手数をかけて申し訳ありません。

お礼日時:2008/01/13 11:53

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています


このQ&Aを見た人がよく見るQ&A