How to display OpenCV Mat on MFC View
Solution 1
From MSDN:
lpvBits [in] A pointer to the color data stored as an array of bytes. For more information, see the following Remarks section.
This is the pointer you must init with the data returned from Mat::data.
Solution 2
Here is another possible way of displaying OpenCV data in MFC which I use and works great:
IplImage* image// <-- this contains the image you want to display
CvvImage tempdefault;
RECT myrect; // <-- specifiy where on the screen you want it to be displayed
myrect.top = 0;
myrect.bottom = _pictureh;
myrect.left = _picturex;
myrect.right = _picturew+_picturex;
tempdefault.Create(_pictureh,_picturew,32);
tempdefault.CopyOf(image);
tempdefault.DrawToHDC(pDC->GetSafeHdc(),&myrect);
Solution 3
CvvImage is not available in new versions of OpenCV. Using the following code you can convert Mat to CImage and then display CImage everywhere you want:
int Mat2CImage(Mat *mat, CImage &img){
if(!mat || mat->empty())
return -1;
int nBPP = mat->channels()*8;
img.Create(mat->cols, mat->rows, nBPP);
if(nBPP == 8)
{
static RGBQUAD pRGB[256];
for (int i = 0; i < 256; i++)
pRGB[i].rgbBlue = pRGB[i].rgbGreen = pRGB[i].rgbRed = i;
img.SetColorTable(0, 256, pRGB);
}
uchar* psrc = mat->data;
uchar* pdst = (uchar*) img.GetBits();
int imgPitch = img.GetPitch();
for(int y = 0; y < mat->rows; y++)
{
memcpy(pdst, psrc, mat->cols*mat->channels());//mat->step is incorrect for those images created by roi (sub-images!)
psrc += mat->step;
pdst += imgPitch;
}
return 0;
}
Tae-Sung Shin
Updated on June 26, 2022Comments
-
Tae-Sung Shin almost 2 years
I thought displaying OpenCV2 Mat on MFC View is simple but is not. This is only relevant material I found on google. Excuse me for my ignorance but I can't find any other materials showing how to use SetDIBitsToDevice with one dimensional array "data" member returns. More specifically, I need to know how to specify BITMAPINFO for the function. Do I go back to Old C-style OpenCV to work with MFC?
UPDATE:
I found an example of SetDIBitsToDevice which is actually for old C-style OpenCV. But it was straightforward to convert it for OpenCV2. There are things I need to mention to make it work:
-
Bpp method does not work well as Mat's depth returns 0. I just changed like this:
static int Bpp(cv::Mat img) { return 8 * img.channels(); }
Mat does not have origin member. But simply putting 0 is fine for origin argument of FillBitmapInfo method.
Other than that, following code works great. Hope this helps other devs too.
void COpenCVTestView::OnDraw(CDC* pDC) { COpenCVTestDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; if(pDoc->m_cvImage.empty()) return; // TODO: add draw code for native data here int height=pDoc->m_cvImage.rows; int width=pDoc->m_cvImage.cols; uchar buffer[sizeof( BITMAPINFOHEADER ) + 1024]; BITMAPINFO* bmi = (BITMAPINFO* )buffer; FillBitmapInfo(bmi,width,height,Bpp(pDoc->m_cvImage),0); SetDIBitsToDevice(pDC->GetSafeHdc(), 0, 0, width, height, 0, 0, 0, height, pDoc->m_cvImage.data, bmi, DIB_RGB_COLORS); } void COpenCVTestView::FillBitmapInfo(BITMAPINFO* bmi, int width, int height, int bpp, int origin) { assert(bmi && width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32)); BITMAPINFOHEADER* bmih = &(bmi->bmiHeader); memset(bmih, 0, sizeof(*bmih)); bmih->biSize = sizeof(BITMAPINFOHEADER); bmih->biWidth = width; bmih->biHeight = origin ? abs(height) : -abs(height); bmih->biPlanes = 1; bmih->biBitCount = (unsigned short)bpp; bmih->biCompression = BI_RGB; if (bpp == 8) { RGBQUAD* palette = bmi->bmiColors; for (int i = 0; i < 256; i++) { palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i; palette[i].rgbReserved = 0; } } }
-
-
Sam over 12 yearshere is all you need. msdn.microsoft.com/en-us/library/dd162974(v=vs.85).aspx Just read it for yourself
-
Sam over 12 yearsThe BITMAPINFO is a structure with the field names clearly saying what they contain: BITMAPINFOHEADER->width, height, etc. Did you really ever read that page? Please read also Mat documentation.
-
Smash over 12 yearsnot sure if I'm using the same version of OpenCV, but to convert from a mat to an IplImage, I just do:
CvMat mat = cvMat( height, width, CV_8UC1, data); IplImage *img = cvCreateImage(cvSize(height,width), IPL_DEPTH_8U, 1); cvGetImage(&mat, img);
-
Sam over 12 yearsOk, I have used it some time ago, but looking at the edit above, it's not as simple as remembered. Sorry.