opencv是一个开源的视觉处理平台,为我们的视觉处理提供了非常多的函数。本人在vs下配置opencv,在实际应用中需要将基于opencv开发的程序打包为dll文件,并通过C#调用。在网上借鉴了诸多大牛的博客,现总结如下:
主要参考博客有,在此表示感谢:https://blog.csdn.net/kuer1379/article/details/80843239
本文直接跳过VS 配置opencv的教程,网上有很多很好的教程,在配置时需要注意的是修改属性管理器位置不同,配置后的结果有些不同。配置位置如下图所示:
两者配置的区别在于进入里面进行配置为永久有效(即新建工程后保留在opencv的配置信息),在外面配置的为只针对该工程有效,新建工程后配置信息消失,再次使用时需要重新进行配置。
配置时需要注意的是:若只配置Debug,则realease会默认配置该信息(在附加依赖项处出错)。 若用realease直接进行运行会报错,需要修改为realease下的依赖文件。
步入封装正题:(需要C#调用,可直接跳过该步骤)
1、基于C++和opencv的程序封装为dll文件,仍通过C++调用
本人新建dll文件按如下步骤进行:
1)文件—》新建——》项目——》,按自己需要修改名称。
2)点击确定,下一步 进入该界面
选择dll 和导出符号,新手建议勾选导出符号,可查看系统生成的示例代码
新建后主要生成6个文件如下图所示,
主要需要修改的是自己命名的头文件和CPP文件,即:dll.h和dll.cpp文件 ,这两个文件示意了变量,类,函数的声明和定义方法。下面以自己实际做的例子为例进行分析。
头文件编写如下:
其中if 0 和#endif中间为将示例程序进行注释,编写的头文件声明如图所示。
cpp文件编写如下:
#if 0 #endif意义和h文件相同。训练结果部分为下面函数需要调用的变量。
至此完成头文件和CPP文件的声明和定义,在生成dll文件时,只要正常生成解决方案即可,需要注意的是:debug和realease模式的不同,本人的结果是debug生成的 在调用时只能debug调用,realease生成的 在调用时只能realease调用(该出不太明白,有精通的欢迎进行交流)。生成的dll文件和lib文件在如图位置查看
debug模式生成的在debug下,realease下生成的在release下查看。本人在debug下生成,故灭有realease文件。
1、C++对生成的dll文件进行调用
调用前准备,按照正常步骤生成控制台程序,将dll文件和lib文件,以及生成dll文件的函数声明文件拷贝到新建工程下:
在使用该dll文件时仅需在源文件中进行包含对应lib库即可,具体参考下图
第一行为包含生成dll的头文件,第三行为包含lib库。其中Check()函数为dll库中文件。运行结果如下
至此,在C++中调用dll库结束。
下面进入本文醉醉重要部分,由C#调用dll库 。(和C++调用略有不同)
不同1、生成dll文件时小细节不同(该部分参考上面博客编写),直接上代码
改代码为。h文件代码,和上面不同之处在于27~28行之间 。 此处加入了 extern "C" 。 cpp文件和上面格式完全一样。本人在Realease下生成dll文件。
不同2、调用方式不同。
创建C#项目参考上述引用博客。在复制dll文件时和C++调用不同的是不需要复制对应的lib文件和头文件。只需要dll一个即可,复制目录为C#工程下的bin目录,本人在x64下的realease,即
由于调用了opencv库,故需要将opencv的dll库拷进来。
调用动态库
进入form.cs,添加命名空间,添加命名空间
using System.Runtime.InteropServices;
引入dll库
[DllImport("txt.dll", EntryPoint = "showImg")]
public extern static int showImg(string str);
具体代码如下:
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 System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("txt.dll", EntryPoint = "showImg")]
public extern static int showImg(string str);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
showImg("C:\\Users\\hp\\Desktop\\cat.jpg");
}
}
}
引用dll库时,EntryPoint=“”showImg“”可不写,只写dll名称亦可。
public 关键字亦可没有。但extern static 必须保留。
题外话:为什么C#引用dll时在生成dll库时必须声明extern "C"。具体可百度,本人理解为,加与不加的区别主要在于:
通过工具查看时,extern c 方式导出的函数, 在 DLL 中函数名就是我们定义的名字;
在C++中, 不使用exern c 方式导出, 在 DLL 中函数名字已经不是我们定义时的名字了。 添加了一些特殊符号。
可通过VS自带工具查看生成的dll文件,
进入界面
输入 dumpbin -exports+dll文件的目录
回车显示 用extern "C"生成的dll文件
红色部分为dll库中包含的函数名
回车显示 没有用extern "C"生成的dll文件
红色部分为dll文件的函数名,可以看出在生成之后 对函数名做了一些转化,进而造成C#无法调用dll文件。