みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

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

締切り済みの質問

OpenCVの透過処理

VC 2010 C++/CLI + OpenCVで教えていただきたい事が有ります。

【教えていただきたい事】
・pbPictureの画像を透過処理して表示
※ 同じサイズの画像をアルファブレンドしたり、上面の画像の背景のみを透過するサンプルは見かけるのですが、
  背面と異なるサイズの上面の画像全体を透過する、サンプルを見つけられませんでした。
  (純粋に透過する機能がopenCVには無いとの事で、小細工が必要なのだと考えています)

【やりたいこと】
・pbBackground(Picturebox)に背景となる画像を読込表示
・pbPicture(Picturebox)に親フォームで作成したBMPの図形(画像)を半透明(透過率50%位)で重ねて表示
・pbBackgroundのサイズは読込データ依存
・pbPictureのサイズは親フォームで作成した図形依存
  ※つまり、pbBackgroundとpbPictureは違うサイズ
・将来的には、マウス移動でpbPictureの位置、大きさ、台形補間をする予定

イメージとしては、下記URLのお化け屋敷の画像とほぼ同じ
http://aidiary.hatenablog.com/entry/20061203/1251465083
※実際は、背景が風景で、上書きする画像は建屋

【現状できているのは】
・cvLoadImageで画像を読込してpbBackgroundに描画
・親フォームで作成した図形を無加工でpbPictureに描画

【現状のソース】
System::Void PhotoRead_Click(System::Object^ sender, System::EventArgs^ e) {
double BmpX,BmpY,XYRatio;
double PhotoX,PhotoY;
int PX,PY;
System::Drawing::Point p;
System::String^ filename;
// pbBackgroundのディフォルトサイズは500×500

OpenFileDialog^ OpFile = gcnew OpenFileDialog(); //
OpFile->DefaultExt = "jpg";
OpFile->Filter = "画像ファイル(*.jpg;*.png;*.bmp;*.gif)|*.jpg;*.png;*.bmp;*.gif";

if (OpFile->ShowDialog() == Windows::Forms::DialogResult::OK) {

SuspendLayout();

filename = OpFile->FileName;

// String^型をchar*に安全に変換
char* pStr = (char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi( filename ).ToPointer();

// ファイル読み込み
IplImage* img = cvLoadImage( pStr );
if( img == 0 ){
return;
}

// Bitmapに直接img->imgDataを読ませると、エラーになるのでコピーデータを渡す
IntPtr ip( new unsigned char[ img->widthStep * img->height ] );
memcpy( ip.ToPointer(), img->imageData, img->widthStep * img->height );
Bitmap^ bmp = gcnew Bitmap(img->width, img->height, img->widthStep, System::Drawing::Imaging::PixelFormat::Format24bppRgb, ip);

// 読み込みデータは解放
cvReleaseImage( &img );

//ピクチャボックスをビットマップ画像サイズに合わせる
BmpX = (double)bmp->Width;
BmpY = (double)bmp->Height;
PhotoX = 500;
PhotoY = 500;
p.X = 10;
p.Y = 40;

PX = 500;
PY = 500;

if (BmpX <= BmpY) {
XYRatio = BmpY / BmpX;
PX = (int)(PhotoX / XYRatio);
p.X = 10 + (500 - PX) /2;
} else {
XYRatio = BmpX / BmpY;
PY = (int)(PhotoY / XYRatio);
p.Y = 40 + (500 - PY) /2;
}

PictureBox^ pbBackground=gcnew PictureBox;
pbBackground->Location = p;

// サイズ指定、従来はWidthとHeightを別々に定義していたが、Sizeを使用すると1行で済む
pbBackground->Size=System::Drawing::Size(PX,PY);

//ピクチャボックスのImageへ読込画像をセット
pbBackground->SizeMode = PictureBoxSizeMode::StretchImage;
pbBackground->Image = bmp;
Controls->Add(pbBackground);

// ピクチャーボックスのpbBackgroundを親としているので、相対座標は0にする

BmpX = (double)PhotBMP->Width;
BmpY = (double)PhotBMP->Height;

PhotoX = pbBackground->Width;
PhotoY = pbBackground->Height;

GX = (int)(PX / 2 - 100) + p.X;
GY = (int)(PY / 2 - 100) + p.Y;

// PictureBoxのグラフィックエリアにBitmapを描画する。
PictureBox^ pbPicture=gcnew PictureBox;
pbPicture->Parent = pbBackground;
pbPicture->Location = System::Drawing::Point(GX, GY);
pbPicture->Size = System::Drawing::Size(200, 200);
pbPicture->SizeMode = PictureBoxSizeMode::StretchImage;

// 上書きする画像をセット
pbPicture->Image = PhotBMP;
Controls->Add(pbPicture);

// デバッグで見やすくするためにバックをどぎつい色に
BackColor=Color::FromArgb(0xFF,0xFF,0x00,0x00);

pbPicture->BringToFront();
pbCursor->BringToFront();

ResumeLayout();

}
}

投稿日時 - 2014-07-29 13:08:58

QNo.8696755

困ってます

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

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

回答(2)

ANo.2

>cvAddWeightedだと、同じサイズ、移動時は再計算、変形後の配列をどうするかなどなど、
>問題があって、cvAddWeightedでは実現できないと考えております。
cvAddWeighted を出したのはただの例です。
合成方法など無数にありますから、ご自分で用途に合う関数をお選びください。

>ROIは矩形しか対応していないと色々なところで記載されていたので、
>考えていなかったのですが、ROIで非対称の台形なども指定できるのでしょうか?
そんなことは自分で組み合わせてするのですよ。
OpenCV 自体は別に 3D に限定するものではない、汎用の画像処理ライブラリですから、台形変換がしたいのであれば、台形変換をすればいいのです。
幸いにも、cvWarpPerspective という関数で透視投影変換ができ、cvGetPerspectiveTransform で変換行列を簡単に求められます。
台形変換は透視投影変換の一種ですから、できますよね。

>移動時は再計算…など、問題があって…実現できないと考えております。
を見て思ったのですが、使い方を根本的に勘違いしていませんか?
OpenGL では GPU に処理ステージというものがあって、モデルビュー変換や投影変換はパラメータを指定すれば勝手にやってくれますが、
OpenCV は汎用の画像処理ライブラリですから、そのようなフレームワークは無く、変換同士の組み合わせや、パラメータが変わった時の再計算などは全部自分でやる必要があります。

投稿日時 - 2014-08-01 20:07:59

お礼

ありがとうございます

でも、ご意見ではなく、回答をいただけると助かるのですが。

投稿日時 - 2014-08-04 09:20:53

ANo.1

アルファ合成をするには、例えば AddWeighted のような関数を使用します。
http://opencv.jp/opencv-2svn/c/core_operations_on_arrays.html#addweighted

ただし、既にご存知の通り、同じ合成する2者の画像は同じサイズでなければなりません。
> マスクを除くすべての入出力配列は,同じ型,同じサイズ(または ROI サイズ)でなければいけません.

異なるサイズの画像の中の一部に対して操作するには、ROI(Region Of Interest; 注目領域)というものを設定する必要があります。
http://opencv.jp/opencv-2svn/c/core_operations_on_arrays.html#setimageroi

今回の場合ですと、背景画像の中の任意の座標に、上面画像と同じサイズのROIを設定してから合成するとよいでしょう。

投稿日時 - 2014-07-30 22:40:28

補足

回答ありがとうございます。

cvAddWeightedだと、同じサイズ、移動時は再計算、変形後の配列をどうするかなどなど、
問題があって、cvAddWeightedでは実現できないと考えております。
OpenGLの様にZバッファを用いて、アルファ値を設定するだけで、背景が透過されるのが好ましいのですが、
GLはGLで色々制約があるので、CVで実現する方法があればと思い質問させていただきました。

ROIは矩形しか対応していないと色々なところで記載されていたので、
考えていなかったのですが、ROIで非対称の台形なども指定できるのでしょうか?
※CvPoint2D32fも矩形しか対応してなく、実際は長方形とひし形(頂点が対象)の物しかできないので、
 やる前から諦めてました

投稿日時 - 2014-07-31 09:21:17

あなたにオススメの質問