先日OpenGLの画像をフォームに貼り付けました。
→C++/CLI フォームアプリとOpenGLの連携(フォームへのレンダリングとGUI操作)
それはよいのですが、更に以下のようなことをしたくなった。
1. OpenGL画像をOpenCV画像(cv::Mat)に変換
2. 画像処理
3. 結果画像(cv::Mat)をフォームのピクチャボックスに表示
2は知ったこっちゃ無いですね。cv::Matさえ扱えればよいのです。
1と3だけメモります。
【1. OpenGL画像をOpenCV画像(cv::Mat)に変換】
超簡単です。
// この前の段階でOpenGLレンダリングコンテキストをカレントにしておく
cv::Mat cvImg = cv::Mat::zeros(winHeight, winWidth, CV_8UC3); glReadPixels(0, 0, winWidth, winHeight, GL_BGR_EXT, GL_UNSIGNED_BYTE, cvImg.data); cv::flip(cvImg , cvImg , 0);
|
【3. 結果画像(cv::Mat)をフォームのピクチャボックスに表示】
結構はまりました。コードは簡単ですが。
めぐりめぐって結局次のサイトを組み合わせて実現しました。
画像処理ソリューション-【OpenCV】C++/CLIによるサンプルプログラム
画像処理ソリューション-【C++/CLI】Graphicsオブジェクトの作成
Code Project-How to Display Image In Picturebox in VC++ from Iplimage and Mat
2番目のサイトによると、
1. Imageオブジェクトから作成(速いからオススメ!)
2. CreateGraphicsメソッドを使う方法(遅いし再描画必要からオススメしない!)
とあるので、両方試してみた。
どちらにしろ流れはこんな感じ。
・ピクチャボックスに対するGraphics オブジェクト作成する。
・Bitmat オブジェクトのバッファにOpenCV のバッファを渡す。
・Graphics オブジェクトでBitmat のバッファをピクチャボックスに描画する。
以下サンプルです。
1. Imageオブジェクトから作成
・10回計測: 平均 = 5838[us], 標準偏差 = 102[us]
2. CreateGraphicsメソッドを使う方法
・10回計測: 平均 = 3563[us], 標準偏差 = 239[us] ← 勝利とあるので、両方試してみた。
どちらにしろ流れはこんな感じ。
・ピクチャボックスに対するGraphics オブジェクト作成する。
・Bitmat オブジェクトのバッファにOpenCV のバッファを渡す。
・Graphics オブジェクトでBitmat のバッファをピクチャボックスに描画する。
以下サンプルです。
1. Imageオブジェクトから作成
・10回計測: 平均 = 5838[us], 標準偏差 = 102[us]
if ((pictureBox1->Image == nullptr) || (pictureBox1->Width != cvImg.cols) || (pictureBox1->Height != cvImg.rows)){ //ピクチャボックスをビットマップ画像サイズに合わせる pictureBox1->Width = cvImg.cols; pictureBox1->Height = cvImg.rows; //PictureBoxと同じ大きさのBitmapクラスを作成する。 Bitmap^ bmpPicBox = gcnew Bitmap(pictureBox1->Width, pictureBox1->Height); //空のBitmapをPictureBoxのImageに指定する。 pictureBox1->Image = bmpPicBox; } Graphics^g = Graphics::FromImage(pictureBox1->Image); // グラフィックオブジェクト作成 bmp = gcnew Bitmap(cvImg.cols, cvImg.rows, cvImg.step, System::Drawing::Imaging::PixelFormat::Format24bppRgb, ip); // bmp->Save("test.jpg"); // これはデバッグ用。バッファがコピーされているか確認。 g->DrawImage(bmp, 0, 0, cvImg.cols, cvImg.rows); // this->pictureBox3->Image = bmp; pictureBox1->Refresh(); // これは必要 delete g; delete bmp; |
2. CreateGraphicsメソッドを使う方法
// どこかから cv::Mat cvImg がやってきたとする。 Bitmap^ bmp; IntPtr ip(cvImg.ptr()); // OpenCVの画像バッファをintポインタに変換 Graphics^g = pictureBox1->CreateGraphics(); // グラフィックオブジェクト作成 bmp = gcnew Bitmap(cvImg.cols, cvImg.rows, cvImg.step, System::Drawing::Imaging::PixelFormat::Format24bppRgb, ip); // bmp->Save("test.jpg"); // これはデバッグ用。バッファがコピーされているか確認。 g->DrawImage(bmp, 0, 0, cvImg.cols, cvImg.rows); // pictureBox3->Refresh(); // これは除外。動画だと画面がちらつく。 delete g; delete bmp; |
え?2の方が速いですよ。再描画いらないし。
しかも1は再描画がいらないと書いてあるけど、Refresh()関数を呼ばないとうまく描画されません。多分説明が反対なのかもしれませんね。