C#调用OpenCV函数的实现

C#调用OpenCV函数的实现步骤:
1、C++编写调用OpenCV函数的方法,编译成dll;
2、C#引用C++生成的dll,调用其中的方法。
详细步骤如下:

1、C++编写调用OpenCV函数的方法,编译成dll

C++生成dll的实现方法可参考文章C++ 如何生成一个DLL动态链接库
a.新建一个Visual C++的空项目,名称为OpenCVCPPDLL

新建c++空项目

创建成功后界面

b.在界面右边目录(头文件)右键添加>>新建项>>代码>>头文件.h:OpenCVMethod.h,
添加头文件操作

头文件

编写代码如下:

#pragma once

//这段代码是防止报错——“链接规范与前面的xx不兼容”
#undef _M_CEE
#include
#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
#include

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 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 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 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 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。

dll配置.png

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
    {
           /// 
        /// 外部调用C++的dll,导入图片路径,通过Mat传递数据到C#
        /// 
        /// 图片完整路径
        /// 图片数据输出
        /// 图片数据大小输出
        [DllImport("OpenCVCPPDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        private extern static void loadImage(string imagePath, ref byte data, out ulong size);
        /// 
        /// 外部调用C++的dll,导入图片路径,处理图片,把灰度值大于该阈值的像素灰度值改为255(0黑,255白)
        /// 
        /// 图片完整路径
        /// 阈值门限
        /// 图片数据输出
        /// 图片数据大小输出
        [DllImport("OpenCVCPPDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        private extern static void threshold(string imagePath, double thresh, ref byte data, out ulong size);
        /// 
        /// 外部调用C++的dll,导入图片数据及大小,处理图片,把灰度值大于该阈值的像素灰度值改为255(0黑,255白)
        /// 
        /// 图片数据输入
        /// 图片数据大小输入
        /// 阈值门限
        /// 图片数据输出
        /// 图片数据大小输出
        [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的实现方法;

你可能感兴趣的:(C#调用OpenCV函数的实现)