#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アプリ作成を取り上げてみようと思う。多分だいぶ先になるのだろうが。
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などを参照ください。今回のテストアプリを作成するときは、下のようなスレッド内にループ作業を書きました。
UINT __cdecl ViewOpenCv::thread1(LPVOID pParam) {
Do looping work here.
}
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;
}