Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

takamon9
C/C++,ATL/MFC,C#(.NET),Python,OpenCv,VBAが得意。他にOpenSSL,ICU,SQL Serverなどを利用したWindowsアプリケーション作成などを嗜む。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away