PS:我记录下这个过程,既是一个学习过程,也想分享给大家。因为是尝试,所以可能中途可能有错误。我尽量记录下来。而且我制作中尽量做到每一步都可以运行,方便测试。简单地说,我想要用VC6.0和Open CV 1.0进行数字图像处理的学习,并且写出自己完整程序来。如果直接使用open cv,看起来不太直观、不专业。《在MFC中使用OpenCV》应该是最好的学习资料和例程,可是我既不熟悉VC,也不熟悉open cv,入门难度比较大,这里把我的学习和分析的过程、经验以及结果分享给大家,希望对大家有帮助。
一、环境
1、VC6.0
2、OPENCV 1.0
3、改编自《在MFC中使用OpenCV》,来源于:
http://wiki.opencv.org.cn/index.php/%E5%9C%A8MFC%E4%B8%AD%E4%BD%BF%E7%94%A8OpenCV
4、VC2010 OPENCV2.4.9环境下面建立过程基本一样,只有少许变化。(VC2013下面如果使用2010这个工程,首先它会自动升级,然后编译失败,需要下载一个
Multibyte MFC Library for Visual Studio 2013,连接为)
关于vc2013直接建立工程,因为工程是默认的unicode,所以如果不设置成多字节模式,会有一个比较严重的问题:opencv的很多对文件操作的函数不是unicode的,而LPCTSTR到const char *这样的转换非常麻烦(当然可以用WideCharToMultiByte这样的函数来完成),所以,如果使用vc2013的话,还是应该不使用unicode,其他部分和vc2010是一样的。
注意:这个原始程序估计是因为修改了类名,但是消息映射的某些东东没有改,最后导致类向导中处理某些类时失败!本人已经修复了此错误。
附件中将提供:《在MFC中使用OpenCV》原始程序及文档、VC6.0 OPENCV1.0版本、VC2010 OPENCV2.4.9版本3样东东,预计将放在CSDN下载频道中。可是本博客不知为何会处于审核状态,等解禁后就上传。
二、参考资料
1、《在MFC中使用OpenCV.doc》,上述文章和程序的作品。这些东东最后都提供给大家,可能需要大家仔细研究。
2、《MFC多文档中opencv处理图像打开、保存》,来源于:
http://blog.csdn.net/abcjennifer/article/details/7313711
这个MM的BLOG不错啊,大家可以认真研究一下。
三、新建MFC .EXE工程MyCV(这个工程特意取了一个新的名字,和原程序不一样!),MDI,注意向导的最后一步我们的视图类CMyCVView的基类应该选择CScrollView,因为我们做图象处理不希望显示时进行缩放,需要原样显示,所以滚动条是必须的。
四、加入OpenCV及DirectShow相关的东东
1、给工程加入opencv的几个库
cxcore.lib cv.lib ml.lib cvaux.libhighgui.lib cvcam.lib
2、将CameraDS.h、CameraDS.cpp、对应头文件以及目录DirectShow复制到你的项目中
3、Additional include directories
Project->Settings->Settings for:(All configurations)->C/C++->Category(Preprocessor)
->Additional include directories设置为../DirectShow/Include
4、Additional library directories
Project->Settings->Settings for:(Allconfigurations)->Link->Category(Input)
->Additional library directories设置为 ../DirectShow/Lib
PS:2、3、4三点在CameraDSA.cpp中有说明,注意上面绿色部分和目录结构有关!
4、tools->option中的lib file把例程的lib文件夹前置到最顶头
例程中包含了strmiid.lib含有这些外部符号,windows系统SDK也包含了strmiid.lib,需要优先使用directshow中的,否则编译过不了。参见:
http://blog.csdn.net/makenothing/article/details/8750703
5、将Processing.cpp和Processing.h复制到你的项目中
本程序设计的思路是这样的:图形处理本身使用opencv,而显示结果则使用windows自己的方式。由于二者存储格式不同,所以需要进行一些转换。另一种实现MFC的方法是采用CvvImage类,我们这里不采用它。
模块中增加了4个函数,即CtreateMapInfo、imageType、imageClone与imageReplace。CtreateMapInfo函数用于建立工作位图workImg的位图信息m_lpBmi,其特点是可以为单通道位图设置两种调色板,即黑白灰阶调色板与VGA默认调色板。因为在OpenCV中二值图像显示为灰阶图像,imageType函数可从单通道位图中识别出二值位图。ImageClone与imageReplace函数使用OpenCV函数实现位图的复制,自动释放老的指针所指向的存储单元以防止内存泄漏,同时返回的m_dibFlag标志可以用于激发刷新工作位图workImg的位图信息m_lpBmi。imageReplace与ImageClone相似,但不建立新位图,只用输入位图替换输出位图。
这四个函数及其他常规图像处理放在两个文件中:Processing.h和Processing.cpp,拷贝并加工程中。编译有17个错误,经检查,这两个文件没有包含opencv的头文件(顺便说一下,前面建立工程后就应该引用cxcore.lib cv.lib ml.lib cvaux.lib highgui.lib cvcam.lib这几个库文件,赶紧加上)。经查,是在stdafx.h中include的:
- #include "MyCV.h" // 窗口管理
-
- #include "cv.h" // OpenCV 文件头
- #include "highgui.h"
-
- #include "CameraDS.h" // DirectShow(基于OpenCV)
- #include "CVDSCap.h" // 视频采集接口
-
- #include "Processing.h" // 附加辅助函数
-
- #endif
-
-
-
-
-
五、修改IDD_ABOUTBOX对话框,加上自己的版权信息。修改String Table中的IDR_MAINFRAME和AFX_IDS_APP_TITLE的Caption为:“OpenCV MFC MDI图像处理程序框架”,把应用程序的名称改为我们需要的。
六、修改CMainFrame::OnCreate,注释掉工具条(mainfrm.cpp文件中),因为我们需要对菜单等进行大动作,如果保留工具条的话太复杂了,有很多工作要做,只留菜单要简单得多。
- int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
- {
- if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
- return -1;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- return 0;
- }
七、修改菜单
1、主菜单IDR_MAINFRAME
先删除“查看”菜单
“文件”菜单中,删除“新建”、“打印设置”菜单(同时为了美观删除多余的separator),只留下“打开”、“最近文件”、“退出”三个菜单
2、子菜单IDR_MYCVTYPE(为了简单起见,我们认为所有的图象都是一种类型),所以只有这一个!
“编辑”、“查看”都删掉,只保留“文件”、“窗口”、“帮助”三个菜单
“窗口”:只保留“层叠”、“平铺”两个,其余删掉。(PS:我感觉可以全部保留,留待后面验证,到时不行再删掉。结论是:其他两个没有什么用,删掉)
“文件”:
(1)“新建”删掉
(2)“打开”ID_FILE_OPEN,改成“打开图象”
(3)新建一个“恢复图象”,ID_REFRESH
(4)在下面插入一个separator
(5)“关闭”ID_FILE_CLOSE,改成“关闭当前窗口”
(6)新建一个“保存当前位图”,ID_CONSERVATION_IMAGE
(7)删除“保存”、“另存为”、separator、“打印”、“打印预览”、“打印设置”
(8)“退出”前面插入一个“恢复原始图象”,ID_COLOR_IMAGE_REFRESH
(9)“退出”前面插入一个“当前画面存盘”,ID_FILE_SAVE_AS
(10)“退出”前面插入一个separator
(11)暂时到这,后面加入具体功能时再来加入其他菜单
八、“软件锁”
本演示程序中有许多功能需要打开OpenCV设置的窗口,如果不关闭这些窗口就执行其他功能会造成程序故障。为了避免产生这种情况,特在使用这种窗口的程序中加设“软件锁”,即在相应功能的执行程序中设置参数m_ImageType的值为-3。而在需要锁住功能的使能函数OnUpdateXXX()中用条件(m_ImageType!=-3)来限制。因此,当程序执行过程中菜单无法使用时,必定是有所开的窗口忘了关闭,请先关闭这些窗口,然后再运行所需功能或退出演示程序。
具体是在MyCVView.h中CMyCVView类的定义中进行修改:
- #ifdef _DEBUG
- virtual void AssertValid() const;
- virtual void Dump(CDumpContext& dc) const;
- #endif
-
- protected:
- IplImage* saveImg;
- IplImage* workImg;
-
- LPBITMAPINFO m_lpBmi;
-
- int m_CaptFlag;
- int m_dibFlag;
- int m_SaveFlag;
- int m_ImageType;
-
-
相应的构造函数也要初始化它们
- CFile fCapture;
- CFileException eCapture;
- char pbuf[20];
- int captSetFlag=0;
-
- CMyCVView::CMyCVView()
- {
-
- saveImg = NULL;
- workImg = NULL;
-
- m_lpBmi = 0;
-
- m_CaptFlag = 0;
- m_dibFlag = 0;
- m_ImageType= 0;
-
- CSize sizeTotal;
- sizeTotal.cx = sizeTotal.cy = 100;
- SetScrollSizes(MM_TEXT, sizeTotal);
-
- if(fCapture.Open( "CaptSetup.txt", CFile::modeRead, &eCapture ) )
- {
- fCapture.Read( pbuf, 20 );
- sscanf(pbuf,"%d %d",&frameSetW,&frameSetH);
- fCapture.Close();
- }
-
- }
CMyCVDoc类的定义中:
- class CMyCVDoc : public CDocument
- {
- protected:
- CMyCVDoc();
- DECLARE_DYNCREATE(CMyCVDoc)
-
-
- public:
- IplImage* pImg;
- int m_Display;
相应的构造函数
- CMyCVDoc::CMyCVDoc()
- {
-
- pImg=NULL;
- m_Display=-1;
-
- }
好了,现在可以打开图象文件了,但是没有绘制图象,这说明还有一些工作没有做。
九、现在需要打开图像
用类向导给文档生成打开的函数(CMyCVDoc::OnOpenDocument)
- BOOL CMyCVDoc::OnOpenDocument(LPCTSTR lpszPathName)
- {
- if (!CDocument::OnOpenDocument(lpszPathName))
- return FALSE;
-
-
- BOOL bool;
-
- Load(&pImg,lpszPathName);
- if (pImg!=NULL) bool=true;
- else bool=false;
- return(bool);
- }
然后这里需要成员函数Load,干脆同时加入Save函数
- BOOL CMyCVDoc::Load(IplImage** pp,LPCTSTR csFileName)
- {
- IplImage* pImg=NULL;
-
- pImg = cvLoadImage(csFileName,-1);
- if (!pImg) return(false);
- cvFlip(pImg);
- if (*pp) {
- cvReleaseImage(pp);
- }
- (*pp)=pImg;
- m_Display=0;
- return(true);
- }
-
-
- BOOL CMyCVDoc::Save(LPCTSTR csFileName,IplImage* pImg)
- {
- int bl;
-
- cvFlip(pImg);
- bl=cvSaveImage(csFileName,pImg);
- return(bl);
- }
类中声明如下:
- class CMyCVDoc : public CDocument
- {
- protected:
- CMyCVDoc();
- DECLARE_DYNCREATE(CMyCVDoc)
- BOOL Load(IplImage** pImg,LPCTSTR pszFilename);
- BOOL Save(LPCTSTR pszFilename,IplImage* pImg);
-
-
- public:
- IplImage* pImg;
- int m_Display;
还有这个需要通过类向导给打开菜单加上
- void CMyCVView::OnUpdateFileOpen(CCmdUI* pCmdUI)
- {
-
- pCmdUI->Enable(m_ImageType!=-3);
-
- }
十、CMyCVView类的OnDraw函数
-
-
-
- void CMyCVView::OnDraw(CDC* pDC)
- {
- CMyCVDoc* pDoc = GetDocument();
- ASSERT_VALID(pDoc);
-
-
- if (pDoc->pImg!=NULL) {
- if (pDoc->m_Display==0) {
- imageClone(pDoc->pImg,&saveImg);
- m_dibFlag=imageClone(saveImg,&workImg);
-
- m_ImageType=imageType(workImg);
- m_SaveFlag=m_ImageType;
- pDoc->m_Display=1;
- }
- }
-
- if (m_dibFlag) {
- if (m_lpBmi)
- free(m_lpBmi);
- m_lpBmi=CtreateMapInfo(workImg,m_dibFlag);
- m_dibFlag=0;
-
- CSize sizeTotal;
- sizeTotal = CSize(workImg->width,workImg->height);
- SetScrollSizes(MM_TEXT,sizeTotal);
- }
-
-
-
-
-
- char *pBits;
- if (m_CaptFlag==1) pBits=m_Frame->imageData;
- else if (workImg) pBits=workImg->imageData;
-
- if (workImg) {
- StretchDIBits(pDC->m_hDC,
- 0,0,workImg->width,workImg->height,
- 0,0,workImg->width,workImg->height,
- pBits,m_lpBmi,DIB_RGB_COLORS,SRCCOPY);
- }
-
- }
好了,现在可以打开并显示图象了
十一、当前画面存盘
利用类向导,对菜单“当前画面存盘”(ID_FILE_SAVE_AS)生成处理程序
- void CMyCVView::OnFileSaveAs()
- {
-
- CString csBMP="BMP Files(*.BMP)|*.BMP|";
- CString csJPG="JPEG Files(*.JPG)|*.JPG|";
- CString csTIF="TIF Files(*.TIF)|*.TIF|";
- CString csPNG="PNG Files(*.PNG)|*.PNG|";
- CString csDIB="DIB Files(*.DIB)|*.DIB|";
- CString csPBM="PBM Files(*.PBM)|*.PBM|";
- CString csPGM="PGM Files(*.PGM)|*.PGM|";
- CString csPPM="PPM Files(*.PPM)|*.PPM|";
- CString csSR ="SR Files(*.SR) |*.SR|";
- CString csRAS="RAS Files(*.RAS)|*.RAS||";
-
- CString csFilter=csBMP+csJPG+csTIF+csPNG+csDIB
- +csPBM+csPGM+csPPM+csSR+csRAS;
-
- CString name[]={"","bmp","jpg","tif","png","dib",
- "pbm","pgm","ppm","sr", "ras",""};
-
- CString strFileName;
- CString strExtension;
-
- CFileDialog FileDlg(false,NULL,NULL,OFN_HIDEREADONLY,csFilter);
-
- if (FileDlg.DoModal()==IDOK ) {
- strFileName = FileDlg.m_ofn.lpstrFile;
- if (FileDlg.m_ofn.nFileExtension == 0) {
- strExtension = name[FileDlg.m_ofn.nFilterIndex];
- strFileName = strFileName + '.' + strExtension;
-
- }
-
- CMyCVDoc* pDoc = GetDocument();
- ASSERT_VALID(pDoc);
-
- pDoc->Save(strFileName,workImg);
- }
-
- }
以及
- void CMyCVView::OnUpdateFileSaveAs(CCmdUI* pCmdUI)
- {
-
- pCmdUI->Enable((m_CaptFlag!=1)&&(m_ImageType!=-3));
- }
十二、恢复图象
利用类向导,对菜单“恢复图象”(ID_REFRESH)生成处理程序
- void CMyCVView::OnRefresh()
- {
-
- m_dibFlag=imageClone(saveImg,&workImg);
- m_ImageType=m_SaveFlag;
- Invalidate();
- }
以及
- void CMyCVView::OnUpdateRefresh(CCmdUI* pCmdUI)
- {
-
- pCmdUI->Enable((m_CaptFlag!=1)&&(m_ImageType!=-3));
- }
十三、保存当前位图
利用类向导,对菜单“保存当前位图”(ID_CONSERVATION_IMAGE)生成处理程序
- void CMyCVView::OnConservationImage()
- {
-
- imageClone(workImg,&saveImg);
- m_SaveFlag=m_ImageType;
- }
以及
- void CMyCVView::OnUpdateConservationImage(CCmdUI* pCmdUI)
- {
-
- pCmdUI->Enable((m_CaptFlag!=1)&&(m_ImageType!=-3));
- }
十四、恢复原始图像
利用类向导,对菜单“恢复原始图像”(ID_COLOR_IMAGE_REFRESH)生成处理程序
- void CMyCVView::OnColorImageRefresh()
- {
-
- CMyCVDoc* pDoc = GetDocument();
- ASSERT_VALID(pDoc);
- pDoc->m_Display=0;
- Invalidate();
-
- }
以及
- void CMyCVView::OnUpdateColorImageRefresh(CCmdUI* pCmdUI)
- {
-
- pCmdUI->Enable((m_CaptFlag!=1)&&(m_ImageType!=-3));
- }
到此为止,框架基本完成,下面就是要加入一个真正的图象处理来验证我们的成果了。
十五、菜单建立
先建立菜单“点处理”
再新建菜单项“彩色变灰阶”,ID_COLOR_TO_GRAY
十六、建立彩色变灰阶的代码
利用类向导,对菜单“彩色变灰阶”(ID_COLOR_TO_GRAY)生成处理程序
- void CMyCVView::OnColorToGray()
- {
-
- IplImage* pImage;
- IplImage* pImg8u = NULL;
-
- pImage = workImg;
-
-
- pImg8u = cvCreateImage(cvGetSize(pImage),IPL_DEPTH_8U,1);
-
- cvCvtColor(pImage,pImg8u,CV_BGR2GRAY);
-
- m_dibFlag=imageReplace(pImg8u,&workImg);
-
- imageClone(workImg,&saveImg);
-
- m_SaveFlag=m_ImageType=1;
- Invalidate();
-
- }
以及
- void CMyCVView::OnUpdateColorToGray(CCmdUI* pCmdUI)
- {
-
- pCmdUI->Enable((m_CaptFlag==0)&&(m_ImageType>1));
-
- }
现在,我们可以打开一个图象,点“彩色变灰阶”可以看到确实灰阶成功了,到此程序基本完成。
十七、让 MDI 程序 在刚启动时 不打开新文档
在*App::InitInstance()中的(我们的程序是BOOLCMyCVApp::InitInstance())
- CCommandLineInfo cmdInfo;
- ParseCommandLine(cmdInfo);
后面加一句
- cmdInfo.m_nShellCommand=CCommandLineInfo::FileNothing;
这个算是对原始程序的一个小改进吧。
十八、程序打开时就立刻最大化
因为我们做图象,自然是全屏显示的好,不希望经常去拉滚动条
BOOL CMyCVApp::InitInstance()进行如下修改:
-
-
-
- m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
- pMainFrame->UpdateWindow();
子窗口默认最大化显示:
- BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
- {
-
-
- cs.style=WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_MAXIMIZE|FWS_ADDTOTITLE;
-
- if( !CMDIChildWnd::PreCreateWindow(cs) )
- return FALSE;
-
- return TRUE;
- }
这个也算是对原始程序的一个小改进吧。
代码下载 点击打开链接
版权声明:本文为博主原创文章,未经博主允许不得转载。