美文网首页
C#调用OpenCV函数的实现

C#调用OpenCV函数的实现

作者: 堆石成山 | 来源:发表于2022-02-07 00:35 被阅读0次

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

    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
        {
               /// <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的实现方法;

    相关文章

      网友评论

          本文标题:C#调用OpenCV函数的实现

          本文链接:https://www.haomeiwen.com/subject/whurkrtx.html