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

【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

 

  • 7
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值