How to display OpenCV Mat on MFC View

12,239

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;
}
Share:
12,239
Tae-Sung Shin
Author by

Tae-Sung Shin

Updated on June 26, 2022

Comments

  • Tae-Sung Shin
    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:

    1. 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(); } 
      
    2. 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
    Sam over 12 years
    here is all you need. msdn.microsoft.com/en-us/library/dd162974(v=vs.85).aspx Just read it for yourself
  • Sam
    Sam over 12 years
    The 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
    Smash over 12 years
    not 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
    Sam over 12 years
    Ok, I have used it some time ago, but looking at the edit above, it's not as simple as remembered. Sorry.