Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display OpenCV Mat on MFC View

Tags:

c++

opencv

mfc

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; 
    } 
} 
}
like image 680
Tae-Sung Shin Avatar asked Dec 14 '11 08:12

Tae-Sung Shin


3 Answers

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);
like image 168
Smash Avatar answered Oct 23 '22 11:10

Smash


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.

like image 40
Sam Avatar answered Oct 23 '22 11:10

Sam


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;
}
like image 1
hosseinkhosravi Avatar answered Oct 23 '22 12:10

hosseinkhosravi