在MFC中应用OpenCV(无CvvImage类)

MFC程序中显示OpenCV图像主要用到的是BITMAPINFO、BITMAPINFOHEADER结构及StretchDIBits函数。

先贴一下MSDN文档中对这些结构与函数的语法描述:

typedef struct tagBITMAPINFO {
  BITMAPINFOHEADER bmiHeader;
  RGBQUAD          bmiColors[1];
} BITMAPINFO, *PBITMAPINFO;
    
typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;
  LONG  biWidth;
  LONG  biHeight;
  WORD  biPlanes;
  WORD  biBitCount;
  DWORD biCompression;
  DWORD biSizeImage;
  LONG  biXPelsPerMeter;
  LONG  biYPelsPerMeter;
  DWORD biClrUsed;
  DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;</pre><pre class="brush:cpp;toolbar:false;">int StretchDIBits(
  __in  HDC hdc,
  __in  int XDest,
  __in  int YDest,
  __in  int nDestWidth,
  __in  int nDestHeight,
  __in  int XSrc,
  __in  int YSrc,
  __in  int nSrcWidth,
  __in  int nSrcHeight,
  __in  const VOID *lpBits,
  __in  const BITMAPINFO *lpBitsInfo,
  __in  UINT iUsage,
  __in  DWORD dwRop
);

BITMAPINFO结构中的第一个成员指向BITMAPINFOHEADER结构,第二个成员却颇具误导性,GBQUAD bmiColors[1]从语法上看似乎是一个GBQUAD数组中的第二个成员,但其实可以把它就看成一个指向颜色表的指针,而且在用它来显示OpenCV图像时可以不关注它(CSDN上的一个讨论帖)。

BITMAPINFOHEADER结构中主要需要设置biSize、biWidth、biHeight、biPlanes、biBitCount及biCompression成员,而且对于biHeight成员的正、负符号需要特别关注,因为这将影响显示图像时StretchDIBits的正确使用。

对于StretchDIBits函数,主要需要关注的就是源矩形和目标矩形的坐标系、尺寸及度量单位的问题。

再来说一下在MFC中显示OpenCV图像的具体方法。

1)创建MFC单文档项(我创建了UseOpenCV),设置包含目录与库目录,添加相关附加依赖项。

2)在文档头文件(UseOpenCVDoc.h)中添加包含文件

#include &lt;opencv2/core/core.hpp&gt;
#include &lt;opencv2/highgui/highgui.hpp&gt;

添加OpenCV Mat类类型成员变量 m_cvImg,形式如下:

protected: // 仅从序列化创建
    CUseOpenCVDoc();
    DECLARE_DYNCREATE(CUseOpenCVDoc)
    cv::Mat     m_cvImg;

3)在文档实现文件(UseOpenCVDoc.cpp)中重写OnOpenDocument函数,如下:

BOOL CUseOpenCVDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
    if (!CDocument::OnOpenDocument(lpszPathName))
        return FALSE;
    
    if (lpszPathName != NULL)
    {
        // 将lpszPathName 转换成 std::string类型 文件名
#ifdef UNICODE
        // 中文路径时会存在问题
        size_t i = 0;
        char* nstring = new char[(wcslen(lpszPathName)+1)*2];
        wcstombs_s(&amp;i, nstring, (wcslen(lpszPathName)+1)*2, lpszPathName, (wcslen(lpszPathName)+1)*2);
        std::string filename(nstring);
        delete[] nstring;
#else
        std::string filename(lpszPathName);
#endif
    
        m_cvImg  = cv::imread(filename);
        if (!m_cvImg.data)
        {
            MessageBox(NULL, lpszPathName, _T("图像文件打开失败"), MB_OK);
            return FALSE;
        }
    }
    
    return TRUE;
}

4)在视图头文件(UseOpenCVView.h)中添加四个成员函数,如下:

protected:
    cv::Mat                   m_cvImg;
    BITMAPINFO*               m_bmi;
    BITMAPINFOHEADER*         m_bmih;
    unsigned int              m_buffer[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256];

5)修改视图类 (CUseOpenCVView)构造函数如下:

CUseOpenCVView::CUseOpenCVView()
{
    // TODO: 在此处添加构造代码
    // 初始化 BITMAPINFO结构 及 BITMAPINFOHEADER结构
    m_bmi = (BITMAPINFO*) m_buffer;
    m_bmih = &amp;(m_bmi-&gt;bmiHeader);
    memset(m_bmih, 0, sizeof(*m_bmih));
    m_bmih-&gt;biSize = sizeof(BITMAPINFOHEADER);
}

6)修改视图绘制函数如下:

void CUseOpenCVView::OnDraw(CDC* pDC)
{
    CUseOpenCVDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;
    
    // TODO: 在此处为本机数据添加绘制代码
    m_cvImg = pDoc-&gt;m_cvImg;
    
    m_bmih-&gt;biWidth = m_cvImg.cols;
    m_bmih-&gt;biHeight = -m_cvImg.rows;           // 在自下而上的位图中 高度为负
    m_bmih-&gt;biPlanes = 1;
    m_bmih-&gt;biCompression = BI_RGB;
    m_bmih-&gt;biBitCount = 8 * m_cvImg.channels();
    CRect rect;
    GetClientRect(&amp;rect);
    StretchDIBits(
        pDC-&gt;GetSafeHdc(),
        0, 0, rect.Width(), rect.Height(),
        0, 0, m_cvImg.cols, m_cvImg.rows,
        m_cvImg.data,
        (BITMAPINFO*) m_bmi,
        DIB_RGB_COLORS,
        SRCCOPY);
}

编译运行程序,打开OpenCV支持的图像文件后,图像就绘制在视图中了。

在MFC中应用OpenCV(无CvvImage类)》上有3条评论

  1. 我想问下按照你的步骤做了后,有错误error C2039: “imread”: 不是“cv”的成员。我已经添加了库和头文件了,

    回复

发表评论

电子邮件地址不会被公开。