C#调用OpenCV函数的实现步骤:
1、C++编写调用OpenCV函数的方法,编译成dll;
2、C#引用C++生成的dll,调用其中的方法。
详细步骤如下:
1、C++编写调用OpenCV函数的方法,编译成dll
C++生成dll的实现方法可参考文章C++ 如何生成一个DLL动态链接库
a.新建一个Visual C++的空项目,名称为OpenCVCPPDLL
创建成功后界面
b.在界面右边目录(头文件)右键添加>>新建项>>代码>>头文件.h:OpenCVMethod.h,
添加头文件操作
头文件
编写代码如下:
#pragma once
//这段代码是防止报错——“链接规范与前面的xx不兼容”
#undef _M_CEE
#include<opencv2\opencv.hpp>
#define _M_CEE
//导入图片路径,传递图片数据供C#使用
//imagePath图片文件完整的路径名,data图片数据输出,size图片数据大小输出
extern "C" _declspec(dllexport) void loadImage(const wchar_t* imagePath, uchar *data, size_t& size);
//导入图片路径,处理图片,把灰度值大于该阈值的像素变为255(0黑,255白)
//imagePath图片文件完整的路径名,thresh阈值门限,data图片数据输出,size图片数据大小输出
extern "C" _declspec(dllexport) void threshold(const wchar_t* imagePath, double thresh, uchar *data, size_t& size);
//导入图片数据,把灰度值大于该阈值的像素变为255(0黑,255白)
//dataIn图片数据输入,sizeIn图片数据大小,thresh阈值门限,dataOut图片数据输出,size图片数据大小输出
extern "C" _declspec(dllexport) void threshold1(uchar *dataIn, size_t sizeIn, double thresh, uchar *dataOut, size_t& size);
//_decspec(dllexport)将函数声名为导出函数被其他程序调。
//extern "C" _declspec(dllexport)的目的是为了使用DllImport调用非托管C++的DLL文件。因为使用DllImport只能调用由C语言函数做的DLL。
c.在界面右边目录(源文件)右键添加>>新建项>>代码>>C++文件.cpp:OpenCVMethod.cpp
编写代码如下
#include "OpenCVMethod.h"
#include<opencv2\opencv.hpp>
#include<windows.h>
using namespace std;
using namespace cv;
//导入图片路径,传递图片数据供C#使用
//imagePath图片文件完整的路径名,data图片数据输出,size图片数据大小输出
void loadImage(const wchar_t * imagePath, uchar * data, size_t & size)
{
if (imagePath != NULL)
{
//1.把文件路径转化为OpenCV的函数可识别的格式
//string与wchar_t*转换
//第一次调用确认转换后单字节字符串的长度,用于开辟空间
int pathSize = WideCharToMultiByte(CP_OEMCP, 0, imagePath, wcslen(imagePath), NULL, 0, NULL, NULL);
char* imagePathChar = new char[pathSize + 1];
//第二次调用将双字节字符串转换成单字节字符串
WideCharToMultiByte(CP_OEMCP, 0, imagePath, wcslen(imagePath), imagePathChar, pathSize, NULL, NULL);
imagePathChar[pathSize] = '\0';
string pattern = imagePathChar;
delete imagePathChar;//释放内存
//2.根据图片路径,读取图片数据到OpenCV的Mat
Mat image = imread(pattern);
//3.转换Mat为C#可识别的byte[]数据输出
vector<uchar> buf;
imencode(".bmp", image, buf);//将Mat以bmp格式存入内存中,转换为uchar数组
size = buf.size();
for each (uchar var in buf)//将buf拷贝到C#的输出byte[]内存中
{
*data = var;
data++;
}
}
}
//导入图片路径,处理图片,把灰度值大于该阈值的像素变为255(0黑,255白)
void threshold(const wchar_t * imagePath, double thresh, uchar * data, size_t & size)
{
if (imagePath != NULL)
{
//1.把文件路径转化为OpenCV的函数可识别的格式
string pattern = "";
//第一次调用确认转换后单字节字符串的长度,用于开辟空间
int pathSize = WideCharToMultiByte(CP_OEMCP, 0, imagePath, wcslen(imagePath), NULL, 0, NULL, NULL);
char* imagePathChar = new char[pathSize + 1];
//第二次调用将双字节字符串转换成单字节字符串
WideCharToMultiByte(CP_OEMCP, 0, imagePath, wcslen(imagePath), imagePathChar, pathSize, NULL, NULL);
imagePathChar[pathSize] = '\0';
pattern = imagePathChar;
delete imagePathChar;//释放内存
//2.读取图片数据到Mat中
Mat sourceMat = imread(pattern);//图片处理前的Mat
Mat destMat;//图片处理后的Mat
//3.调用OpenCV函数进行处理
threshold(sourceMat, destMat, thresh, 255, THRESH_BINARY);
//4.将Mat转化为C#可识别的byte[]格式数据
vector<uchar> buf;
imencode(".bmp", destMat, buf);//将Mat以bmp格式存入内存中,转换为uchar数组
size = buf.size();
for each (uchar var in buf)//将buf拷贝到C#的输出byte[]内存中
{
*data = var;
data++;
}
}
}
//导入图片数据,把灰度值大于该阈值的像素变为255(0黑,255白)
//dataIn图片数据输入,sizeIn图片数据大小,thresh阈值门限,dataOut图片数据输出,size图片数据大小输出
void threshold1(uchar *dataIn, size_t sizeIn, double thresh, uchar * dataOut, size_t & size)
{
//1.把C#输入的byte数组数据拷贝到bufIn中,通过cv::imdecode方法进而转化为OpenCV的Mat
vector<uchar> bufIn;
for (size_t i = 0; i < sizeIn; i++)
{
bufIn.push_back(dataIn[i]);
}
Mat sourceMat = cv::imdecode(bufIn,1);//与cv::imread方法中的额flags=1一致,图片处理前的Mat
Mat destMat;//图片处理后的Mat
//2.调用OpenCV中的函数,cv::threshold
cv::threshold(sourceMat, destMat, thresh, 255, THRESH_BINARY);
//3.将OpenCV的Mat转化为C#可识别的byte[]内存中
vector<uchar> buf;
cv::imencode(".bmp", destMat, buf);//将Mat以bmp格式存入内存中,转换为uchar数组
size = buf.size();
for each (uchar var in buf)//将buf拷贝到C#的输出byte[]内存中
{
*dataOut = var;
dataOut++;
}
}
d.在界面右边目录(源文件)右键添加>>新建项>>代码>>模块定义文件.def:OpenCVMethod.def
def.png
编写代码如下:
LIBRARY "OpenCVMethod"
EXPORTS
;loadImage函数
loadImage
;threshold函数
threshold
;threshold1函数
threshold1
e.编译生成dll之前需要先安装OpenCV库,并部署好环境变量,其实现方法可参考文章OpenCV安装部署详细教程
f.在界面右边项目文件OpenCVCPPDLL右键选择属性>>配置属性>>常规>>目标文件扩展名的.exe修改为为.dll,解决方案平台为x64(因为OpenCV库有用到64),编译生成,之后在...\OpenCVCPPDLL\x64\Debug目录下找到生成的dll。
x64.png
dll文件位置.png
二、C#引用C++生成的dll,调用其中的方法
a.创建一个C#的窗体程序OpenCVCSharp.sln,打开VS,新建项目>>Visual C#>>Windows窗体应用程序
b.界面放置两个PictureBox控件
c.双击窗体,编写代码如下:
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace OpenCVCSharp
{
public partial class Form1 : Form
{
/// <summary>
/// 外部调用C++的dll,导入图片路径,通过Mat传递数据到C#
/// </summary>
/// <param name="imagePath">图片完整路径</param>
/// <param name="data">图片数据输出</param>
/// <param name="size">图片数据大小输出</param>
[DllImport("OpenCVCPPDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private extern static void loadImage(string imagePath, ref byte data, out ulong size);
/// <summary>
/// 外部调用C++的dll,导入图片路径,处理图片,把灰度值大于该阈值的像素灰度值改为255(0黑,255白)
/// </summary>
/// <param name="imagePath">图片完整路径</param>
/// <param name="thresh">阈值门限</param>
/// <param name="data">图片数据输出</param>
/// <param name="size">图片数据大小输出</param>
[DllImport("OpenCVCPPDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private extern static void threshold(string imagePath, double thresh, ref byte data, out ulong size);
/// <summary>
/// 外部调用C++的dll,导入图片数据及大小,处理图片,把灰度值大于该阈值的像素灰度值改为255(0黑,255白)
/// </summary>
/// <param name="dataIn">图片数据输入</param>
/// <param name="sizeIn">图片数据大小输入</param>
/// <param name="thresh">阈值门限</param>
/// <param name="data">图片数据输出</param>
/// <param name="size">图片数据大小输出</param>
[DllImport("OpenCVCPPDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private extern static void threshold1(byte[] dataIn, ulong sizeIn, double thresh, ref byte data, out ulong size);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//图片文件路径
string picPath = @"D:\Work\OpenCVCPPCSharp\image\testPic.png";
//尽可能大的byte[],存图片数据
byte[] ptrData = new byte[2048 * 2048 * 3];
//存储byte的长度
ulong size = new ulong();
//导入图片路径,通过Mat传递数据到C#,将C++的内存数据存入C#的内存中
loadImage(picPath, ref ptrData[0], out size);
//将byte[]转化为MemoryStream再传递给控件image显示
pictureBox1.Image = Image.FromStream(new MemoryStream(ptrData, 0, (int)size));
//自适应控件窗口
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
//尽可能大的byte[],存图片数据
byte[] ptrData1 = new byte[2048 * 2048 * 3];
//存储byte的长度
ulong size1 = new ulong();
////根据图片路径输入处理图片
//threshold(picPath, 80, ref ptrData1[0], out size1);//212814
//根据图片数据输入处理图片
threshold1(ptrData,(ulong)size, 80, ref ptrData1[0], out size1);
//将byte[]转化为MemoryStream再传递给控件image显示
pictureBox2.Image = Image.FromStream(new MemoryStream(ptrData1, 0, (int)size1));
//自适应控件窗口
pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
}
d.同样配置解决方案平台为x64,编译生成;
e.在目录...\OpenCVCPPDLL\x64\Debug中拷贝C++生成的OpenCVCPPDLL.dll到 目录...\OpenCVCSharp\OpenCVCSharp\bin\x64\Debug下,运行C#程序效果如下图:
C#调用OpenCV处理图片效果图
至此,C#调用OpenCV函数库的方法完成。欢迎大家提出问题一起探讨。
总结本文技术要点:
1.C++生成dll文件方法;
2.C++OpenCV的Mat与C#的Image互换;
3.C++中方法的输入参数、输出参数的实现方式;
4.C++OpenCV的环境配置;
5.OpenCV方法imencode与imdecode的使用;
6.C#调用C++非托管dll的实现方法;
网友评论