【OpenCV学习笔记3】OpenCV GUI 之 搭建MFC+OpenCV開發環境

单文档程序    VC6.0+OpenCV1.0

SkySeraph Jun.25th 2010  HQU  

zgzhaobo@gmail.com  452728574

 更多精彩请直接访问SkySeraph个人站点www.skyseraph.com 

步骤:

1  创建SDI MFC工程,单文档,最好选择use MFC As a static library  (以防止MFC中使用opencv的内存泄露问题)

2  加入OpenCV库支持[头文件和库文件]:菜单Project->Settings->Link->Input->Object/library modules中加入cxcore.lib cv.lib ml.lib cvaux.lib highgui.lib cvcam.lib

3  在Doc类开头添加HighGui.h 的头文件包含, 同时添加一个变量CImage m_image; [在定义处,建议public,VC下]

4  添加2个虚函数,用来打开和保存文件:OnOpenDocument 和 OnSaveDocument  如下:

BOOL CSDI OpenCVDoc::OnOpenDocument(LPCTSTR lpszPathName){
    if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE;
    // TODO: Add your specialized creation code here
    m_image.Load(lpszPathName);
    return TRUE;
}

BOOL CSDI OpenCVDoc::OnSaveDocument(LPCTSTR lpszPathName){
    // TODO: Add your specialized code here and/or call the base class
    m_image.Save(lpszPathName);
    return TRUE;

   // return CDocument::OnSaveDocument(lpszPathName); //原因:会导致保存后的图片大小变为0 
}

5   在View类添加显示函数

void CSDI_OpenCVView::OnDraw(CDC* pDC)
{
        CSDI_OpenCVDoc* pDoc = GetDocument();
        ASSERT_VALID(pDoc);
        // TODO: add draw code for native data here
        CImage & img = pDoc ->m_image; //CImage类 其实就是CvvImage类,可以直接替换使用

        CRect r;
        GetClientRect (&r);
        img.DrawToHDC(pDC->GetSafeHdc() ,r);
}

6  添加菜单,实现CANNY

①修改前面的 m_image.Load(lpszPathName);为 m_image.Load(lpszPathName,0); //强制转换读取图像为8位深度灰度图

②添加菜单ImageProcessing,子菜单CANNY,在Doc类中添加ID号为IDM_CANNY的COMMAND消息响应函数,代码如下

void CSDI_OpenCVDoc::OnCanny() //菜单处理函数

{

// TODO: Add your command handler code here

IplImage* img;

img=m_image.GetImage();//由CImage类 返回一个IplImage* 指针,因为:IplImage* GetImage() { return m_img; };

cvCanny(img,img,50,150,3);

UpdateAllViews(NULL);

}

说明

  1. 原创】MFC中快速应用OpenCV教程
  2. 关于图片太大,无法放入view里的一个解决方案,请看 feixiaolin 的帖子 opencv 数据读写操作+图像噪声+ MFC下OpenCV源代码 以及 如何插入滚动条
  3. OpenCV绘图函数DrawToHDC的一个简要解释
  4. 在opencv1.0版本中,使用上面介绍的canny的方法,存在单通道图片无法打开的错误,具体的原因请看这里,这主要是由于opencv1.0中的一个bug所导致的。原因
  5. 在opencv 1.1版本中使用,可能导致MFC的程序无法退出,解决办法请看这里 程序无法正常退出    解决:1.1下不能退出的问题 需要将1.1中_highgui.h中的 #define HAVE_VIDEOINPUT 1这句 注释掉,重新编译OpenCv的Highgui工程,重新生成Highgui.lib就可以了 
  6. 注意,其中的 CImage 其实就是 CvvImage 这两个可以直接替换使用。
  7. OLE错误对话框的问题,请看这里 如何解决OLE的问题的论坛讨论

参考资料

http://www.opencv.org.cn/index.php/MFC%E4%B8%AD%E5%BF%AB%E9%80%9F%E5%BA%94%E7%94%A8OpenCV   MFC中快速应用OpenCV

http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=4932&p=18215#p18136   相关问题

对话框程序   VC6.0+OpenCV1.0

 

SkySeraph Oct.28th 2010  HQU  

zgzhaobo@gmail.com  452728574

Latest Modified Date:Oct.29th 2010 HQU

步骤:

1  创建工程:MFC_OpenCV_Dlg     对话框

2  加入OpenCV库支持[头文件和库文件]

菜单Project->Settings->Link->Input->->Object/library modules 加入 cxcore.lib cv.lib ml.lib cvaux.lib highgui.lib cvcam.lib

3  添加头文件和全局宏

在CMFC_OpenCV_Dlg.h开头 #include "resource.h"  下 加入如下代码

#include "cv.h"

#include "HighGUI.h"

#define IMAGE_WIDTH 512  //此处假设读入和显示的图像都是512*512(像素)大小的彩色图像,读者可自行修改

#define IMAGE_HEIGHT 512

#define IMAGE_CHANNELS 3

4  添加IplImage*变量并初始化及内存释放

在CMFC_OpenCV_DlgDlg  右击添加一个 IplImage* 类型的变量 TheImage,

再双击该类下的OnInitDialog,在“// TODO: Add extra initialization here”下面添加TheImage 的初始化代码:

    CvSize ImgSize;
    ImgSize.height = IMAGE_HEIGHT;
    ImgSize.width = IMAGE_WIDTH;
    TheImage = cvCreateImage( ImgSize, IPL_DEPTH_8U, IMAGE_CHANNELS );

接着在CMFC_OpenCV_DlgApp 下面的成员列表中双击 InitInstance,

在两个“// TODO: Place code here to handle when the dialog is…”下面添加:

cvReleaseImage( &dlg.TheImage );//释放内存

即按下“OK”或“Cancel”时,释放TheImage占用的内存。//按下OK或Cancel 会调用InitInstance,看源代码

5  窗口重绘

在CMFC_OpenCV_DlgDlg ,双击 OnPaint,在 if(IsIconic())…的 else 里添加以下代码,用来重绘窗口:

        CDialog::OnPaint();                    // 重绘对话框
        CDialog::UpdateWindow();          // 更新windows窗口,如果无这步调用,图片显示还会出现问题
        ShowImage( TheImage, IDC_ImgShowCtrl );    // 重绘图片函数

6  添加控件及成员函数

1个Edit

 

关联变量m_Path

 

1个picture

ID:IDC_ImgShowCtrl

 

 

4个Button

ID:IDC_ChooseFile   用来选择图片并保存路径到m_Path

ID:IDC_ShowImage     用来显示m_Path路径上的图片

ID:IDC_EdgeDetect  边缘检测测试

ID:IDC_Test               扩展/自定义

消息响应函数:OnChooseFile()

消息响应函数:OnShowImage()

消息响应函数:OnEdgeDetect()

消息响应函数:OnTest()

 詳細代碼見下

在CMFC_OpenCV_DlgDlg,添加两个成员函数void ShowImage( IplImage* img, UINT ID ) 和 void ResizeImage(IplImage* img),代码如下:

void CMFC_OpenCV_DlgDlg::ShowImage(IplImage *img, UINT ID) //ID 是Picture Control控件的ID号

{

// 获得显示控件的 DC

CDC* pDC = GetDlgItem( ID ) ->GetDC();

// 获取 HDC(设备句柄) 来进行绘图操作                

HDC hDC = pDC ->GetSafeHdc();                                

CRect rect;

GetDlgItem(ID) ->GetClientRect( &rect );

/*

CBrush brush(RGB(255,255,255));

pDC->FillRect(&rect,&brush);

*/

// 求出图片控件的宽和高

int rw = rect.right - rect.left;                        

int rh = rect.bottom - rect.top;

// 读取图片的宽和高

int iw = img->width;        

int ih = img->height;

// 使图片的显示位置正好在控件的正中

int tx = (int)(rw - iw)/2;        

int ty = (int)(rh - ih)/2;

SetRect( rect, tx, ty, tx+iw, ty+ih );//

// 复制图片

CvvImage cimg;

cimg.CopyOf( img );

// 将图片绘制到显示控件的指定区域内        

cimg.DrawToHDC( hDC, &rect );        

ReleaseDC( pDC );

}

void CMFC_OpenCV_DlgDlg::ResizeImage(IplImage *img)

{

// 图片的大小

int width=(int)IMAGE_WIDTH;                        // 宽

int height=(int)IMAGE_HEIGHT;                // 高

float max_w_h=(float) (IMAGE_WIDTH>IMAGE_HEIGHT)? IMAGE_WIDTH : IMAGE_HEIGHT;

// 读取图片的宽和高

    int w = img->width;

    int h = img->height;// 找出宽和高中的较大值者

    int max = (w > h)? w: h;

// 计算将图片缩放到TheImage区域所需的比例因子

    //float scale = (float) ( (float) max / 512.0f );

float scale = (float) ( (float) max / max_w_h );

   

    // 缩放后图片的宽和高

    int nw = (int)( w/scale );

    int nh = (int)( h/scale );

// 为了将缩放后的图片存入 TheImage 的正中部位,需计算图片在 TheImage 左上角的期望坐标值

    int tlx = (nw > nh)? 0: (int)(width-nw)/2;

    int tly = (nw > nh)? (int)(height-nh)/2: 0;

/*        //确保为3通道图像

IplImage *temp = 0; //定义中转图像指针

temp = cvCreateImage( cvGetSize(img), img->depth, 3);

if(1==img->nChannels)        // 如果是单通道的 图像,转换成3通道的

{

cvConvertImage(img,temp,CV_CVTIMG_SWAP_RB);        // P70

//CV_CVTIMG_FLIP   (flip vertically)    CV_CVTIMG_SWAP_RB (swap the R and B channels)

}

else if(3==img->nChannels)

{

temp=cvCloneImage(img);        

}

else AfxMessageBox(_T("channel wrong on function show picture !"));

*/

// 设置 TheImage 的 ROI 区域,用来存入图片 img

    cvSetImageROI( TheImage, cvRect( tlx, tly, nw, nh) ); //P87

// 对图片 img 进行缩放,并存入到 TheImage 中

    cvResize( img, TheImage,CV_INTER_AREA ); //P255

// 重置 TheImage 的 ROI 准备读入下一幅图片

    cvResetImageROI( TheImage );

//cvReleaseImage(&temp);        // 释放 中转 图像指针

}

void CMFC_OpenCV_DlgDlg::OnChooseFile()

{

// TODO: Add your control notification handler code here

CFileDialog dlg(

        TRUE, NULL, NULL,

        OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,

        _T("All Files (*.*) |*.*||"), AfxGetMainWnd()

        );                                        // 选项图片的约定

    dlg.m_ofn.lpstrTitle = _T("Open Image");    // 打开文件对话框的标题名

    if( dlg.DoModal() != IDOK )                    // 判断是否获得图片

        return;

   

    m_Path.SetWindowText( dlg.GetPathName() );            // 获取图片路径

}

void CMFC_OpenCV_DlgDlg::OnShowImage()

{

// TODO: Add your control notification handler code here

CString mPath;

m_Path.GetWindowText(mPath);

if(mPath=="")

{

AfxMessageBox(_T("file path is empty ,please choose file!"));

return;

}

IplImage* ipl = cvLoadImage( mPath, CV_LOAD_IMAGE_COLOR );    // 读取图片、缓存到一个局部变量 ipl 中

    if( !ipl )                                    // 判断是否成功载入图片

        return;

    if( TheImage )                                // 对上一幅显示的图片数据清零

        cvZero( TheImage );

   

ResizeImage( ipl );    // 对读入的图片进行缩放,使其宽或高最大值者刚好等于 512,再复制到 TheImage 中

    ShowImage( TheImage, IDC_ImgShowCtrl );            // 调用显示图片函数   

    cvReleaseImage( &ipl );                        // 释放 ipl 占用的内存

}

void CMFC_OpenCV_DlgDlg::OnEdgeDetect()

{

// TODO: Add your control notification handler code here

CString mPath;

m_Path.GetWindowText(mPath);

IplImage *gray = 0, *edge = 0;

    gray = cvCreateImage( cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 1 );

    edge = cvCreateImage( cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 1 );

    cvCvtColor( TheImage, gray, CV_BGR2GRAY );

    cvCanny( gray, edge, 30, 100, 3 );

    cvCvtColor( edge, TheImage, CV_GRAY2BGR );  

if(!TheImage)

{

AfxMessageBox(_T("PictureCtrl is empty ,please choose Pictrue!"));

return;

}

    ShowImage( TheImage, IDC_ImgShowCtrl );            // 调用显示图片函数

    cvReleaseImage( &gray );

    cvReleaseImage( &edge );

}

效果:

圖片弄不上,真暈。。。誰告訴俺如何插入圖片? %>_<%   ^_^

参考资料

书:OpenCV教程-基础篇

http://blog.csdn.net/chenyusiyuan/archive/2009/10/29/4744097.aspx   晨宇思远OpenCV学习笔记9

http://www.site.uottawa.ca/~laganier/tutorial/opencv+directshow/cvision.htm  A step-by-step guide to the use of Microsoft Visual C++ and the Intel OpenCV library

Author:         SKySeraph

Email/GTalk: zgzhaobo@gmail.com    QQ:452728574

From:         http://www.cnblogs.com/skyseraph/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,请尊重作者的劳动成果。

原文地址:https://www.cnblogs.com/skyseraph/p/1863992.html