LoginSignup
10
10

More than 5 years have passed since last update.

OpenCvのMatからC++/MFCへの画像変換

Last updated at Posted at 2017-06-18

cv::MatからMFC picture controlへの画像移行

 OpenCvをC++/MFCで扱う場合、最大の難関は、MatからMFCのpicture controlへ転写させることだと感じる。picture controlは.Netでいうとpicture boxに相当する。

 初心者にはMFCは難関で参考になるサイトも少ないのでわずかなコードを完成させるまで試行錯誤し、トータルで1週間くらいはかかってしまったが、実際は簡単に変換できることが分かった。

 その方法は、CImageで型を作り、SetPixelでカラーデーターを入力させることだ。これによって簡潔で分かりやすいコードをつくることができた。

 MFCはその難しさゆえに何度も挫折しかけたが、逆に面白さも感じた。C++でOpenCvのアプリケーションを作るなら、.NETよりMFCを活用したほうが良いというのが今回の感想だ。

 次回の記事は、MFCでのOpenCvアプリ作成を取り上げてみようと思う。多分だいぶ先になるのだろうが。

code

    cv::Mat BMat = cv::imread("./abc.bmp");

    CImage cimg;
    cimg.Create(BMat.cols,BMat.rows,32,0);

    for (int y = 0; y < BMat.rows; y++) {
            for (int x = 0; x < BMat.cols; x++) {
                uchar r,g,b;

                b = BMat.at<cv::Vec3b>(y, x)[0];
                g = BMat.at<cv::Vec3b>(y, x)[1];
                r = BMat.at<cv::Vec3b>(y, x)[2];

                cimg.SetPixel(x, y, RGB(r, g, b));
        }
    }

    CStatic* pictureB = (CStatic*)GetDlgItem(IDC_STATIC_BITMAP);
    pictureB->SetBitmap(cimg);

超高速な変換方法(動画での利用方法)

  (超高速というか、画像読み込み時はこっちのほうがが正しいやり方のようです。上のコードを書いたあとでよく考えると、ピクセルにアクセスするのは、ピクセル値を変更する場合だけです)
 
 上の方法は各ピクセルにアクセスしていたが、Mat.dataというファンクションを利用すればピクセルデータを一括で確保できるようだ。
 
 ビットマップインフォの構造体からStretchDIBitsでピクセルデータをセットし、そのパラメーターからイメージを作成するのが最も高速に読み込み動作する。

ビットマップインフォから作成する際、以下の点を注意する。

 bitInfo.bmiHeader.biHeight ; というパラメーターの設定について、パラメーターに代入する値を負の値にしないと上下反転した画像になるので、 = -BMat.rowsとすること。(ご指摘ありがとうございます)

 また、動画で使用する場合は、別スレッドを作り、そこにループ処理のコードを書かないといけない。でないと、ループから抜けるまでボタンも押せない「固まった」状態になるからです。

 スレッドの作り方はいくつかあるのでMSDNなどを参照ください。今回のテストアプリを作成するときは、下のようなスレッド内にループ作業を書きました。

ThreadExample

UINT __cdecl ViewOpenCv::thread1(LPVOID pParam) {

  Do looping work here.

}

cvMatToMfcPictureControl

    cv::Mat original;
    cv::VideoCapture capture;
    capture.open(0);// open the default camera


    CWnd *cwn = (CWnd*)GetDlgItem(IDC_STATIC_BITMAP); // Get the picture control ID
    CDC* wcdc = cwn->GetDC();
    HDC whdc = wcdc->GetSafeHdc();

    RECT rec;
    cwn->GetClientRect(&rec);

    cv::Size matSize;
    matSize = cv::Size(rec.right, rec.bottom);

    cv::Mat BMat(matSize.width, matSize.height, CV_32FC3);

    for (;;) {

        capture.read(original);
        BMat = original;

        BITMAPINFO bitInfo;
        bitInfo.bmiHeader.biBitCount = 24;
        bitInfo.bmiHeader.biWidth = BMat.cols; //the bitmap is a bottom-up DIB and its origin is the lower-left corner.
        bitInfo.bmiHeader.biHeight = -BMat.rows; //If biHeight is negative, the bitmap is a top-down DIB and its origin is the upper-left corner.
        bitInfo.bmiHeader.biPlanes = 1;
        bitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bitInfo.bmiHeader.biCompression = BI_RGB;
        bitInfo.bmiHeader.biClrImportant = 0;
        bitInfo.bmiHeader.biClrUsed = 0;
        bitInfo.bmiHeader.biSizeImage = 0;
        bitInfo.bmiHeader.biXPelsPerMeter = 0;
        bitInfo.bmiHeader.biYPelsPerMeter = 0;

        StretchDIBits(
            whdc,
            0, 0,
            matSize.width, matSize.height,
            0, 0,
            BMat.cols, BMat.rows,
            BMat.data,
            &bitInfo,
            DIB_RGB_COLORS,
            SRCCOPY
        );

        if (cv::waitKey(1) >= 0) break;
    }

10
10
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10