OpenCV 与 MFC 共存

OpenCV 功能强大,但在初次使用时有若干注意事项,现记下备忘。

 

M1.  在没有 MFC 的Win32程序中,highGUI可以很好的工作。只是注意“编译选项/代码生成/运行时库”必须选择为多线程或单线程的DLL。否则 DEBUG版可能会报heap Error。因为通过 CvImage.load 调用生成的对象是由highGUI的动态库调用动态运行时库分配的内存,而CvImage在析构的时候用的是静态运行时库。所以Debug会报试图释放不同堆分配的内存。若编译选项使用DLL的运行时库,则不再有问题。

 

M2. 当编译使用MFC的OpenCV应用程序,必须使用静态链接的MFC。若使用了动态连接的MFC,则Debug版在程序退出时可能会报告大量的内存泄漏。测试表明,只要调用了 CvImage.load,内存泄漏必然出现。而静态连接MFC导致运行时库也必须选用静态的,这又和M1的结论相冲突。

 

M1和M2的焦点都集中到了highGUI 库上。然而,创建一个使用MFC的OpenCV更有现实意义,所以应该讨论一下解决方案和原因。

 

我并不想花时间研究OpenCV的源码,以解决动态链接MFC时可能导致内存泄漏的原因,而是选择了一种偷懒的方式,那就是逃避。因此,方案的第一步就是使用静态连接的MFC。由于使用了静态连接的MFC,就不得不使用静态运行时库,这就不得不解决M1提到的问题。我方案的第二步是避免调用 CvImage.load 函数。下面解释一下为什么不使用CvImage.load就可以解决问题。

 

从源码可知,CvImage.load是将各种格式的图像文件装载到CvImage的内存对象的函数。load的实现是通过函数指针运行的。而函数指针的值由运行时动态指定,例如 load 一个 *.bmp 图像,其实际的代码由highGUI动态库提供,因而内存也由highGUI动态库的代码在运行时分配。而CvImage在析构的时候却没有区分它内部包裹的 IplImage 的内存是由哪个模块分配的。所以,只要使用了 CvImage.load,那么对应的内部 IplImage 在析构的时候就可能出问题。可见只要避免调用CvImage.load, 使用静态运行时库和静态MFC就没有问题。

 

既然不使用CvImage.load了,如何读取不同格式的图像文件到CvImage呢? 我方案的第三步就是使用 FreeImage 库。FreeImage 可以识别大部分常用图像格式,并将数据以 BITMAP 的数据结构保存,结合 OpenCV中文官网上提供的"BMP与IplImage相互转换"的代码构建CvImage。这样就解决了 M1。

 

此外,由于CvImage.save 方法对于图像格式的支持也很有限,因此将 FreeImage 引入进来还可以丰富save的图像格式。

 

总结一下:

1.  从 CvImage 继承一个新类  CvImg

2.  重载 load,并用 FreeImage 实现数据的读取。

     内部使用OpenCV中文官网上提供的"BMP与IplImage相互转换"的代码中的 CopyData 函数即可。

3.  重载 save, 用 FreeImage 实现

4.  使用静态运行时库和静态MFC

 

注意,这样继承只是为了节省代码,在以后的使用中必须保持用 CvImg 完全替代 CvImage。

因为 CvImage 中的 load和save都不是虚函数。

 

 

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
首先,需要在MFC项目中添加OpenCV文件和头文件,具体步骤如下: 1. 在Visual Studio中打开MFC项目,选择项目名称右键点击,选择属性; 2. 在属性页中,选择“VC++目录”选项卡,找到“包含目录”选项,添加OpenCV的头文件路径,例如:C:\opencv\build\include; 3. 在同一属性页中,找到“目录”选项,添加OpenCV文件路径,例如:C:\opencv\build\x64\vc14\lib; 4. 在同一属性页中,找到“链接器”选项卡,选择“输入”选项,添加OpenCV所需的文件,例如:opencv_world341d.lib。 添加完文件和头文件后,就可以开始编写代码了。以下是一个简单的示例: ``` #include "stdafx.h" #include "opencv2\highgui.hpp" #include "opencv2\core.hpp" #include "opencv2\imgproc.hpp" using namespace cv; void CMyDlg::OnBnClickedButtonLoadImage() { // 打开文件对话框 CFileDialog dlg(TRUE); if (dlg.DoModal() == IDOK) { CString strFilePath = dlg.GetPathName(); // 读取图像文件 Mat img = imread(strFilePath.GetBuffer(), CV_LOAD_IMAGE_COLOR); // 显示图像 imshow("Image", img); } } ``` 在这个示例中,我们在MFC对话框中添加了一个按钮“Load Image”,点击按钮后会弹出文件对话框,选择一个图像文件后会加载并显示在窗口中。其中,imread函数用于读取图像文件,imshow函数用于显示图像。需要注意的是,在MFC程序中使用imshow函数需要在头文件中添加"#define CV_NO_BACKWARD_COMPATIBILITY",否则会出现编译错误。 另外,需要在对话框的OnInitDialog函数中添加以下代码,以初始化OpenCV环境: ``` BOOL CMyDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 初始化OpenCV环境 cv::namedWindow("Image", CV_WINDOW_NORMAL); cv::resizeWindow("Image", 640, 480); return TRUE; } ``` 在这个示例中,我们创建了一个名为“Image”的窗口,并设置了窗口大小为640x480。需要注意的是,在MFC程序中使用OpenCV的窗口函数需要先调用namedWindow函数创建窗口,并在之后的代码中使用窗口名称来引用窗口。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值