通过CLR在VC#项目中调用Opencv C++代码
在VC#项目中调用Opencv c++代码应该是一个比较常见的需求。C#有丰富的UI,而VC++ opencv则几乎没有UI。C#中虽然可以支持Emgucv,但是与原生态的opencv相比,我更愿意选择opencv。Use OpenCV C++ codes in a VC# project — solution of creating a managed CLR wrapper一文中讲到了在VC#项目中调用Opencv C++代码的方法,本文记录我验证此文的过程,翻译原文为主,有部分改动,感谢原作者。
本文示例中将涉及3个项目:一个VC ++ OpenCV项目,用opencv函数读取指定路径的图片并显示在窗口中;一个用于包装VC ++ OpenCV项目(创建DLL)的VC ++ CLR项目;一个是用于测试DLL调用的VC#项目。
STEP1 创建一个从指定路径读取图像并显示的VC++ opencv项目
1.) VC++ opencv项目命名为opencvCPP,创建过程:打开Visual studio —> 文件 —> 新建 —> 项目 —> Visual C ++ —> Win32控制台应用程序, 应用程序类型:控制台应用程序,选择为空项目,单击完成按钮创建一个新项目。
2.) 为当前项目添加头文件opencvCPP.h,代码如下:
1 |
1 2 3 4 5 6 7 8 |
#pragma once #include <opencv2/opencv.hpp> class MYcppGui { public: MYcppGui(); ~MYcppGui(); int myCppLoadAndShowRGB(char* url); }; |
3.) 为当前项目添加源文件opencvCPP.cpp,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#pragma once #include "opencvCPP.h" using namespace cv; MYcppGui::MYcppGui() { win0 = false; } MYcppGui::~MYcppGui() { cvDestroyAllWindows(); } int MYcppGui::myCppLoadAndShowRGB(char* url) { namedWindow("normal", WINDOW_NORMAL); Mat image = cv::imread(url, 0); imshow("normal", image); waitKey(0); return 0; } |
4.) 为当前项目添加另外一个C++源文件,命名为main.cpp,该文件用于测试当前类是否能正常运行,不会在后续步骤中使用,确保已经为该项目正确配置了opencv相关内容,编译并运行该项目,确保没有任何错误,main.cpp的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <stdio.h> #include "opencvCPP.h" int main() { MYcppGui *myGui = new MYcppGui(); myGui->myCppLoadAndShowRGB("D:\\0604.png"); free(myGui); printf("End of the test program\n"); system("pause"); return 0; } |
STEP2 创建主要CLR包装类的VC++项目
1) CLR包装类命名为clropencv,通过文件>新建>项目> Visual C ++>CLR>Class
Library来创建。
2)
在opencv项目中,函数myCppLoadAndShowRGB需要输入一个参数,该参数用于记录当前要显示图片的地址,该参数变量类型和名称为char* url,该变量的值要从C#工程的UI项目中获取。但是变量类型char*是一种非托管的格式,在C#项目中不支持此格式。解决该问题有两种方案,方案一是将C#项目设置为允许不安全代码,方案二是在CLR包装类中完成此转换。我只验证了方案一,因此这里只给出方案一的代码。
头文件clrOpencv.h中代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#pragma once #include "D:\freelancer\6\opencvCPP\opencvCPP\opencvCPP.h"//STEP1中建立工程头文件的路径 #include "D:\freelancer\6\opencvCPP\opencvCPP\opencvCPP.cpp"//STEP1中建立工程源文件的路径 using namespace System; namespace MYopencv { public ref class MYGui { public: MYGui(); int myLoadAndShowRGB(char* url); private: MYcppGui *myGui; }; } |
3) 接下来的步骤是在clrOpencv.cpp中添加如下代码:
1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// This is the main DLL file. #include "stdafx.h" #include "clrOpencv.h" #include "D:\freelancer\6\opencvCPP\opencvCPP\opencvCPP.h" #include "D:\freelancer\6\opencvCPP\opencvCPP\opencvCPP.cpp" using namespace MYopencv; MYGui::MYGui() { myGui = new MYcppGui(); } int MYGui::myLoadAndShowRGB(char *url) { return myGui->myCppLoadAndShowRGB(url); } |
4)在正式编译项目之前,务必为该工程配置OpenCV。编译完成后,即可得到在.NET环境中使用的DLL文件。
STEP3 创建测试CLR包装的VC#工程
1) 创建一个VC#工程,windows窗体应用程序,在该UI界面中添加两个Button,一个用于选择显示图片的路径;一个用于触发调用opencv读取图像的函数。
2) 在当前新建方案资源管理器中选择“引用”–>”添加引用”–>“浏览”,选择STEP2中编译得到的DLL文件。
3) Form1.cs的相关代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using MYopencv;////////////////STEP2中创建的命名空间 namespace testCSharpInterface { public partial class Form1 : Form { MYGui cvGui;//////////////////MYGui是STEP2中创建的类 private double c; public Form1() { InitializeComponent(); cvGui = new MYGui(); } private void Form1_Load(object sender, EventArgs e) { } private void button2_Click(object sender, EventArgs e) { OpenFileDialog fdlg = new OpenFileDialog(); fdlg.Title = "Choose image"; fdlg.InitialDirectory = @"C:\Users\Public\Pictures\Sample Pictures"; fdlg.Filter = "Image files (*.*)|*.*"; fdlg.FilterIndex = 2; fdlg.RestoreDirectory = true; if (fdlg.ShowDialog() == DialogResult.OK) textBox1.Text = fdlg.FileName; } private void button1_Click(object sender, EventArgs e) { string url = textBox1.Text.Trim(); byte[] urlByte = Encoding.ASCII.GetBytes(url); unsafe { fixed (byte* p = urlByte) { sbyte* sp = (sbyte*)p; if (cvGui.myLoadAndShowRGB(sp) < 0) MessageBox.Show("Invalide's directory.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } } } |
按照上述步骤执行即可实现调用简单opencv C++工程,要实现调用较复杂的opencv工程,还需要进一步研究。
感受学习的力量!