如何在 MFC 视图上显示 OpenCV Mat

Posted

技术标签:

【中文标题】如何在 MFC 视图上显示 OpenCV Mat【英文标题】:How to display OpenCV Mat on MFC View 【发布时间】:2011-12-14 08:59:14 【问题描述】:

我认为在 MFC 视图上显示 OpenCV2 Mat 很简单,但事实并非如此。 This is only relevant material I found on google。请原谅我的无知,但我找不到任何其他材料来展示如何将 SetDIBitsToDevice 与一维数组“数据”成员返回一起使用。更具体地说,我需要知道如何为函数指定 BITMAPINFO。我要回到旧的 C 风格 OpenCV 来使用 MFC 吗?

更新:

我找到了an example of SetDIBitsToDevice,它实际上是用于旧的 C 风格 OpenCV。但是将其转换为 OpenCV2 很简单。我需要提到一些事情才能让它发挥作用:

    Bpp 方法效果不佳,因为 Mat 的深度返回 0。我只是这样更改:

    static int Bpp(cv::Mat img)  return 8 * img.channels();  
    

    Mat 没有原始成员。但是对于 FillBitmapInfo 方法的 origin 参数,简单地放置 0 就可以了。

除此之外,以下代码效果很好。希望这对其他开发者也有帮助。

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; 
     
 

【问题讨论】:

您提供的链接有什么问题? @vasile 尽管有关于 SetDIBitsToDevice 的文档,但我无法弄清楚如何将 SetDIBitsToDevice 与一维数组“数据”成员返回一起使用。 @Paul 我已经将您的示例视为其他几个问题/答案的基础。但是,我想知道,您如何避免在FillBitmapInfo 函数中使用memset 导致内存泄漏?还是以某种方式自动释放?你能详细说明一下这方面吗? @adn 对不起,我不明白你的问题。是什么让您认为使用 memset 会导致内存泄漏?为什么要释放它? 你是我的英雄!节省我很多时间。我不需要 CvvImage,只需复制并稍微修改您的代码即可。关键点是SetDIBitsToDevice调用,但BITMAPINFO迷惑初学者。 【参考方案1】:

这是在 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);

【讨论】:

不确定我是否使用相同版本的 OpenCV,但要将 mat 转换为 IplImage,我只是这样做:CvMat mat = cvMat( height, width, CV_8UC1, data); IplImage *img = cvCreateImage(cvSize(height,width), IPL_DEPTH_8U, 1); cvGetImage(&amp;mat, img);【参考方案2】:

来自 MSDN:

lpvBits [in] 指向存储为字节数组的颜色数据的指针。有关详细信息,请参阅以下备注部分。

这是您必须使用从 Mat::data 返回的数据初始化的指针。

【讨论】:

这就是您所需要的。 msdn.microsoft.com/en-us/library/dd162974(v=vs.85).aspx自己看吧 BITMAPINFO 是一个结构,其字段名称清楚地说明了它们包含的内容:BITMAPINFOHEADER->width、height 等。您真的读过那个页面吗?另请阅读 Mat 文档。 好吧,我前段时间用过,但是看上面的编辑,没有记忆中的那么简单。对不起。【参考方案3】:

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;

【讨论】:

以上是关于如何在 MFC 视图上显示 OpenCV Mat的主要内容,如果未能解决你的问题,请参考以下文章

MFC+opencv+高斯背景前景提取图像显示问题

opencv显示图片的问题

如何在 Opencv 中显示一个 Mat

opencv与mfc显示图片操作,MFC的鼠标响应在opencv图片上失效,opencv滚轮事件没有响应问题描述解决。

安卓上的 OpenCV,Mat.rows() = 0

无法同时显示五个 Mat 图像(C++、OpenCV)