【OpenCV学习笔记3】OpenCV GUI 之 搭建MFC+OpenCV開發環境
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);
}
说明
- 【原创】MFC中快速应用OpenCV教程
- 关于图片太大,无法放入view里的一个解决方案,请看 feixiaolin 的帖子 opencv 数据读写操作+图像噪声+ MFC下OpenCV源代码 以及 如何插入滚动条
- OpenCV绘图函数DrawToHDC的一个简要解释
- 在opencv1.0版本中,使用上面介绍的canny的方法,存在单通道图片无法打开的错误,具体的原因请看这里,这主要是由于opencv1.0中的一个bug所导致的。原因
- 在opencv 1.1版本中使用,可能导致MFC的程序无法退出,解决办法请看这里 程序无法正常退出 解决:1.1下不能退出的问题 需要将1.1中_highgui.h中的 #define HAVE_VIDEOINPUT 1这句 注释掉,重新编译OpenCv的Highgui工程,重新生成Highgui.lib就可以了
- 注意,其中的 CImage 其实就是 CvvImage 这两个可以直接替换使用。
- 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/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,请尊重作者的劳动成果。
作者:skyseraph
出处:http://www.cnblogs.com/skyseraph/
更多精彩请直接访问SkySeraph个人站点:http://skyseraph.com//
Email/GTalk: zgzhaobo@gmail.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。