如何在MFC视图上显示OpenCV Mat

6
我认为在MFC视图上显示OpenCV2 Mat看似简单,但实际上却不然。我在谷歌上找到的唯一相关材料是这个:This is only relevant material I found on google。抱歉我的无知,但我找不到其他任何资料展示如何使用带有单维数组"data"成员返回的SetDIBitsToDevice函数。更具体地说,我需要知道如何为该函数指定BITMAPINFO。我要回到旧的C风格的OpenCV来处理MFC么?
更新:
我找到了一个SetDIBitsToDevice的例子,它实际上是针对旧的C风格OpenCV的。但将其转换为OpenCV2很简单。有几件事情需要注意才能使其工作:
  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.

除此之外,以下代码运行良好。希望这能对其他开发人员有所帮助。
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; 
    } 
} 
}

2
你提供的链接有什么问题吗? - Sam
@vasile 尽管有关SetDIBitsToDevice的文档,我仍然无法弄清楚如何使用一维数组"data"成员返回的SetDIBitsToDevice函数。 - Tae-Sung Shin
@Paul,我看到你的示例被用作其他几个问题/答案的基础。然而,我想知道,在FillBitmapInfo函数中使用memset时,你是如何避免内存泄漏的?或者说它会自动释放吗?你能详细说明一下吗? - adn
@adn 很抱歉,我不理解你的问题。是什么让你认为使用memset会导致内存泄漏?为什么需要释放它? - Tae-Sung Shin
你是我的英雄!你为我节省了很多时间。我不需要CvvImage,只需复制并稍微修改您的代码即可。关键点是SetDIBitsToDevice调用,但BITMAPINFO对于初学者来说可能会有些困惑。 - Raymond
3个回答

3
这是另一种在MFC中显示OpenCV数据的可能方法,我使用它并且效果很好:
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);

1
我不确定我是否使用的是相同版本的OpenCV,但是要将mat转换为IplImage,我只需要执行以下操作:CvMat mat = cvMat(height, width, CV_8UC1, data); IplImage *img = cvCreateImage(cvSize(height, width), IPL_DEPTH_8U, 1); cvGetImage(&mat, img); - Smash

1
从MSDN: lpvBits [in] 指向颜色数据的指针,存储为字节数组。有关更多信息,请参阅以下“备注”部分。
这是您必须使用从Mat :: data返回的数据初始化的指针。

这是你所需要的所有内容。http://msdn.microsoft.com/en-us/library/dd162974(v=vs.85).aspx 请自行阅读。 - Sam
BITMAPINFO 是一个结构体,字段名称清晰地说明了它们包含的内容:BITMAPINFOHEADER->width(宽度),height(高度),等等。你真的有读过那个页面吗?请也阅读 Mat 文档。 - Sam
好的,我之前用过它,但是看了上面的编辑,发现它并不像我记得的那么简单。抱歉。 - Sam

1

CvvImage在新版的OpenCV中已不再可用。使用以下代码,您可以将Mat转换为CImage,然后在任何您想要的地方显示CImage:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接