【3】C#中多中不同的方法调用c++DLL(OpenCV)库完成图像处理

tech2023-12-03  39

【1】算法思路

算法运行的思路由图所示

思路1  (1)c#中读入一张本地图片的路径;(2)将该路径传递给dll里面的函数,OpenCV根据路径打开图像,完成图像处理;

            (3)结果保存到C#中分配的内存中。(适用打开本地图片,进行处理)

思路2   (1)c#获取图像数据的在内存中的头部指针(图像可能为相机拍摄的图像存放在内存中,也可能为本地加载的图像保存到内存中)。(2)将指针传递给dll函数,调用OpenCV完成mat的重建,然后用OpenCV完成图像处理。(3)结果保存到C#中分配的内存中。(适用于在内存中的图像)

关于dll库的创建可以参考前面两篇文章,再次不详细阐述了。下面进入主题

【2】代码分享

DLL库代码:

.h文件

#pragma once #define DLL_API extern "C" _declspec(dllexport) #include<opencv.hpp> #include<opencv2/highgui.hpp> #include "opencv2/imgproc/imgproc_c.h"//IplImage头文件 #include <opencv2/core.hpp> #include<iostream> #include <opencv2/imgproc/imgproc.hpp> #include "opencv2/highgui/highgui.hpp" using namespace std; using namespace cv; //非托管代码实现 //*****************************************需要导出的DLL函数**********************************************************// //以下两个函数均为c#调用c++里面的dll库完成图像处理的函数,不同点就是读入的是数据还是图像的路径。 /* 传入图像路径---------》》》opencv读取图像------------》》》图像处理----------》》》返回图像数据到内存中 GetFileToProcess函数简介:读入一个图片的路径,一块内存的首地址,返回图像的大小; 函数功能:在c#中调用此函数,输入图像的路径,在c++中完成图像处理,在c#中得到处理完成的结果; @filename 文件的路径名,通常为"C:\\Users\\Administrator\\Pictures\\opencvpicture\\arrow2.jpg"形式; @data 开辟内存的首地址,需要提前分配,内存的大小要大于图像的大小; @size 图像的大小,长和宽; */ DLL_API void GetFileToProcess(char * filename, uchar *data, size_t&size); /* 传入图像的数据到内存中---------》》》OpenCV重建mat图像-----------》》》图像处理----------》》》返回图像数据到内存中 GetDateToProcess函数简介:读入图像数据的指针,读入图像的长、宽、步长信息,读入一个空白的内存用于存放处理的数据 函数功能:从c#中读入一段图像内存和图像的大小信息,然后重建mat图像,完成图像处理,将图像返回值空白内存中; @bimage:c#中图像的数据的头部指针 @nH:图像的高度 @nw:图像的宽度 @stride:图像的扫描步长-------------(图像在内存里是按行存储的。扫描行宽度就是存储一行像素,用了多少字节的内存) @data:c#申请的内存,存放处理完成的图像数据 @size: c#处理完成的图像的图像大小 */ DLL_API void GetDateToProcess(char * bimage, int nH, int nW, int stride, uchar *data, size_t &size); //*****************************************内部进行图像处理的函数**********************************************************// //以下函数为自定义的图像处理函数,为全局函数 //自定义函数 void GrayImage(Mat & image, Mat & grayimage);

.cpp文件

#include"unmanage.h" #include<opencv.hpp> #include<opencv2/highgui.hpp> #include "opencv2/imgproc/imgproc_c.h"//IplImage头文件 #include <opencv2/core.hpp> #include<iostream> #include <opencv2/imgproc/imgproc.hpp> #include "opencv2/highgui/highgui.hpp" #include<algorithm> #include<vector> #include<typeinfo> using namespace std; using namespace cv; /********************************************自定义图像处理函数********************************************************/ //自定义图像处理函数 void GrayImage(Mat & image, Mat & grayimage) { cvtColor(image, grayimage, COLOR_BGR2GRAY); } /******************************************************************************************************************/ DLL_API void _stdcall GetFileToProcess(char * filename ,uchar *data, size_t&size) { //[1]读入图像 std::vector<uchar> buf; //Mat src = cv::imread("C:\\Users\\Administrator\\Pictures\\opencvpicture\\beads.jpg"); //读入一个Mat Mat src =imread(filename); //[2]图像处理 Mat grayimage; GrayImage(src, grayimage); //[3]处理结果数据保存到内存中 imencode(".bmp", grayimage, buf); //将Mat以bmp格式存入内存中,转换为uchar数组 size = buf.size(); for (uchar &var : buf) { *data = var; data++; } } DLL_API void _stdcall GetDateToProcess(char * pImg, int nH, int nW, int stride, uchar *data, size_t &size) { //[1]由内存数据重建mat图像 Mat image = Mat(nH, nW, CV_8UC3, pImg, stride).clone(); //[2]图像处理 Mat grayimage, thresholdimage; GrayImage(image, grayimage); threshold(grayimage,thresholdimage,50,255,1); //[3]处理结果数据保存到内存中 vector<uchar> buf; imencode(".bmp", thresholdimage, buf); //将Mat以bmp格式存入内存中,转换为uchar数组 size = buf.size(); for (uchar &var : buf) //将buf拷贝到C#的输出byte[] 内存中 { *data = var; data++; } }

.def文件

LIBRARY unmanaged EXPORTS GetFileToProcess EXPORTS GetDateToProcess

C#中的代码:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace ShowPicture { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private string pathname = string.Empty; //定义路径名变量 //打开图像 private void button1_Click(object sender, EventArgs e) { OpenFileDialog file = new OpenFileDialog(); file.InitialDirectory = "."; file.Filter = "所有文件(*.*)|*.*"; file.ShowDialog(); if (file.FileName != string.Empty) { try { pathname = file.FileName; //获得文件的绝对路径 this.pictureBox1.Load(pathname); } catch (Exception ex) { MessageBox.Show(ex.Message); } } } //保存图像 private void button2_Click(object sender, EventArgs e) { SaveFileDialog save = new SaveFileDialog(); save.ShowDialog(); if (save.FileName != string.Empty) { pictureBox1.Image.Save(save.FileName); } } // //路径----》bitmap----->>mat----->>bmp----->>返回 /*GetDateToProcess函数简介:读入图像数据的指针,读入图像的长、宽、步长信息,读入一个空白的内存用于存放处理的数据 函数功能:从c#中读入一段图像内存和图像的大小信息,然后重建mat图像,完成图像处理,将图像返回值空白内存中; @bimage:c#中图像的数据的头部指针 @nH:图像的高度 @nw:图像的宽度 @stride:图像的扫描步长-------------(图像在内存里是按行存储的。扫描行宽度就是存储一行像素,用了多少字节的内存) @data:c#申请的内存,存放处理完成的图像数据 @size: c#处理完成的图像的图像大小 */ [DllImport("unmanaged.dll")] private extern static void GetDateToProcess(byte[] buffer, int imageHeight, int imageWeidth, int imageStride, ref byte ptrData, out ulong size); private void button3_Click(object sender, EventArgs e) { Bitmap bmp = (Bitmap)Bitmap.FromFile(@"C:\\Users\\Administrator\\Pictures\\opencvpicture\\beads.jpg"); System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height); System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat); //Get the address of the first line. IntPtr ptr = bmpData.Scan0; // Declare an array to hold the bytes of the bitmap. int bytesLength = Math.Abs(bmpData.Stride) * bmp.Height; int imageWeidth = bmp.Width; int imageHeight = bmp.Height; //图像的Stride int imageStride = bmpData.Stride; byte[] buffer = new byte[bytesLength]; // Copy the RGB values into the array. Marshal.Copy(ptr, buffer, 0, bytesLength); bmp.UnlockBits(bmpData); byte[] ptrData = new byte[2048 * 2048 * 3]; //尽可能大的byte[] ulong size = new ulong(); GetDateToProcess(buffer, imageHeight, imageWeidth, imageStride, ref ptrData[0], out size); pictureBox1.Image = Image.FromStream(new MemoryStream(ptrData, 0, (int)size)); } //打开一个路径调用dll完成图像处理,传输图像路径到dll库完成图像处理,之后再返回数据。 //路径----》mat----->>bmp----->>返回 /* @filename: 图片的路径名;------------在c#中类型为string ,在c++dll中对应的是char* @data: 返回图片存放的数组,dll在使用时调用C#申明的这段内存,然后在c++将数据存放到此数组中;----------在c#中类型为byte ,在c++dll中对应的是 uchar* @size: 图像的宽高信息。-------- 在c#中类型为ulong c++中为size_t类型 */ [DllImport("unmanaged.dll")] private extern static void GetFileToProcess(string filename, ref byte data, out ulong size); private string filename = string.Empty; private void button4_Click(object sender, EventArgs e) { OpenFileDialog file = new OpenFileDialog(); file.InitialDirectory = "."; file.Filter = "所有文件(*.*)|*.*"; file.ShowDialog(); if (file.FileName != string.Empty) { try { filename = file.FileName; //获得文件的绝对路径 //string filename = "C:\\Users\\Administrator\\Pictures\\opencvpicture\\arrow2.jpg";//自定义打开文件路径 byte[] ptrData = new byte[2048 * 2048 * 3]; //尽可能大的byte[],一般大于显示的最大图片内存即可 ulong size = new ulong(); GetFileToProcess(filename, ref ptrData[0], out size); //调用dll库中的函数,将C++的内存数据转入C#的内存中 pictureBox1.Image = Image.FromStream(new MemoryStream(ptrData, 0, (int)size));//将byte[]转化为MemoryStream再传递给image } catch (Exception ex) { MessageBox.Show(ex.Message); } } } } }

c#界面展示

(1)导入数据结果显示  (2)导入路径结果显示

【3】易犯错误

个人心得:前前后后做了两天终于完成了c++非托管dll库在c#中的调用,将路上的坑整理如下

(1)调用dll库上,c++里面调试没有问题,c#打开显示内存受损或者受保护。可能原因,

          a)   c#里面的数据和c++里面的数据格式不对应。格式参考:c# 调用 C++ dll 传入传出 字符串

         b)    private extern static void GetFileToProcess(string filename, ref byte data, out ulong size);检查导入外部函数时是否添加了static,没有statice返回的值很容易被覆盖掉。这里的返回值为void可以不添加。

          c) 注意c#环境配置和c++里面的环境配置是否一致,对应debug和x64;

         d)项目的目标平台改为和配置环境一样

(2)dll制造注意点

提示main函数不存在可能是如下原因,解决方法如下所示。

(1)将目标文件扩展名和配置类型改为dll形式,将环境配置改为x64和debug

 

最新回复(0)