【这块具体内容可以直接百度搜索 GOCW】

       使用 csharp  编写 winform 程序,不仅速度快,而且容易界面美化并找到其他类库的支持;而使用  opencv 编写图形图像处理程序,是目前比较流行,而且高效的一种方法。如果需要将两者结合,需要解决的问题就是使用  csharp 调用 vc  下编写的库文件。两个难点,一个是平台调用的内存控制问题,一个是参数传递问题。关注点在解决实际问题

在现实中,我发现问题比较大的是两点,一点是内存控制问题,一个是平台调用问题。

  一、内存控制:(1-6种方法是我验证后失败的方法,关注问题解决者可直接看第7点)

1 、验证  opencv 下的 dll  程序是否能够对内存起到很好的控制。方法是将图片路径写在  dll 文件中,使用 csharp 调用  dll 文件,以此来验证是否是函数内部的溢出。

DllExport  void  imageprocesstest1()
{
vector<Mat> inputmat;
vector<cv::Point2f> points1;
vector<cv::Point2f> points2;
vector<Point> locmat1;vector<Point> locmat2;
char cur_dir[50];
char strimg [50];
for ( int iimg = 1;iimg<=23;iimg++)
{
inputmat.clear();
points1.clear();
points2.clear();
locmat2.clear();
locmat1.clear();
sprintf(cur_dir, "D:\\t\\SteelNo%d\\Camera01" ,iimg);
Mat tmp;
// 读取数据
for ( int i =1;i<=10;i++) // 执行全部文件的遍历
{
if (i<10)
{
sprintf(strimg, "%s\\image00000%d.jpg" ,cur_dir,i);
}
else
{
sprintf(strimg, "%s\\image0000%d.jpg" ,cur_dir,i);
}
cv::Mat src= cv::imread(strimg,0);
if (!src.data)
return ;
inputmat.push_back(src);
}
InputAdjust(inputmat);
MulitMatch(inputmat,locmat1,locmat2,5);
Mat ma = MulitAlign(inputmat,locmat1,locmat2);
if (ma.rows == 2 || ma.rows ==3)
{
return ;
}
else
{

Mat mb;
mb =MulitBlend(inputmat,ma,locmat1,locmat2);
if (mb.rows == 1)
{
return ;
}
else
{
sprintf(strimg, "%s\\3.jpg" ,cur_dir);
imwrite(strimg,mb);
}
}
}
return ;
}

在这个函数中,我对 d 盘  t 文件目录下的 23 个文件夹进行遍历,调用  InputAdjust , MulitMatch  , MulitBlend 这  3 个主要函数进行拼接操作。在运行状态下  (ctrl+F5) ,不溢出。

2 、采用动态加载  dll 文件的方式进行函数调用。(具体见  Zealic.Windows )

就是采用 loaddll  的方式动态地将  dll 调入程序中,并且采用 freedll 的方式动态地将  dll 文件注销掉。这个方法在运行状态下  (ctrl+F5) ,溢出,没有效果。

3 、采用轻量化代码生成的方法  (dynamicmethod) 进行 dll  文件的调用。

 

DynamicLibrary golib =  new  DynamicLibrary( "GOImage.dll"  );
NativeMethodBuilder goBuilder = new NativeMethodBuilder();
goBuilder.ParameterLength = 3;
goBuilder.SetParameterInfo(0, typeof ( string ));
goBuilder.SetParameterInfo(1, typeof ( string ));
goBuilder.SetParameterInfo(2, typeof ( int ));
NativeMethod method = goBuilder.MakeMethod(golib, "imageprocess" );
method.Invoke(strtmp, strImagePath_Res, 1);

同样溢出。以上方法在 F5 模式下都能够运行正常。

4 、这个时候,我开始想是不是我的  dll 那个地方的资源没有释放掉。所以我特地生成了一个简单的函数。

DllExport  void  imageprocesstest(  char  *cur_dir, char  *imagepath_res)
{
char DllBuff[50];
vector<Mat> inputmat;
Mat src;
for ( int i = 1;i<=10;i++) // 执行全部文件的遍历
{
if (i<10)
{
sprintf(DllBuff, "%s\\image00000%d.jpg" ,cur_dir,i);
}
else
{
sprintf(DllBuff, "%s\\image0000%d.jpg" ,cur_dir,i);
}
src= cv::imread(DllBuff,CV_LOAD_IMAGE_COLOR);
if (!src.data)
{
inputmat.clear();
return ;
}
for ( int j=0;j<10;j++) // 采用这种方式
{
inputmat.push_back(src); // 将读取的结果压入 inutmat
}
}
vector<Mat> (inputmat).swap(inputmat);
return ;
}

它的调用采用

  

[ DllImport (  "GOImage.dll" ,
EntryPoint = "imageprocesstest" ,
CharSet = CharSet .Ansi, CallingConvention = CallingConvention .Cdecl)]
public static extern int imageprocesstest( string ImagePath, string ImagePath_Res);

 

这个函数不执行任何函数,唯一的作用就是讲图片读进去,然后清空。其中

vector<Mat> (inputmat).swap(inputmat);

叫做 vector  的 swap 方法,是  effiective c++ 上面推荐的一种确实能够清空  vector 的方法。而 mat 对象时  opencv 的内建对象,自己带注销代码,而且其注销无法被显示调用。

这个时候,依然是 ctrl+F5 会溢出。

 

我认为,现在的情况是,在重复调用一个  dll 文件的情况下(而且这个  dll 文件需要若干秒的执行时间),会发生内存无法情况的情况。因为非托管方式下,垃圾回收器无法管理非托管代码内存。但是我已经显示地将内存注销了,只是不能被垃圾回收器回收。

5 、所以我强制进行垃圾回收

Gc.colect();

依然没有效果。所以这个方向的问题我卡在了这里。

 

6、这个时候,我想到了以前通过  com 调用 matlab  函数的时候也出现过这样的问题,但是当时是个小项目,就采用了一种方法对付了一下。现在也想看一下这种方法是否可行。

我们要达到的效果,就是输入一个目录,下面是以某种方式保存的图片,然后  进行拼接,得到对应的拼接图片。是否可以采用这种形式

 

Csharp 调用图像处理的  dll 文件,同时向 param.log 文件中以固定格式写入需要处理的图片的参数。  Dll 文件收到后,按照 param.log 中指定的参数进行处理,同时将结果写入  result.log ,并且在完成后,返回一个值,  csharp 在收到这个值后,到指定的地方读取  param.log ,并显示出来。

如果采用这种方法,就只需要调用几次  dll 文件,那么内存溢出的问题就不会影响到系统的稳定。但是这个问题仍然没有被解决。

 

7、最终,在研究appdomin后,我发现,可以讲pinvoke 的调用写在program.cs下面。

using  System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text;
namespace WinFormTester
{
static class Program
{
[ STAThread ]
static void Main()
{
Application .EnableVisualStyles();
Application .SetCompatibleTextRenderingDefault( false );
Application .Run( new Form1 ());
}

[ DllImport ( "F:\\PInvoke\\stringbuilder\\Debug\\stringbuilder.dll" ,
EntryPoint = "stringbuilder" ,
CharSet = CharSet .Auto, CallingConvention = CallingConvention .Cdecl)]
public static extern Boolean ParamIsString( string str, ref StringBuilder ip);


}
}

  这种方法下再进行调用,解决内存问题。其原有我认为是。。

二、 参数传递问题。传递int等系统默认变量很简单方便,回写的方法,最后能够传递image变量

1、int 写入,如果要写出,加*

2、算是解决吧 bool值

DllExport  int  stringbuilder(  char  *str, int  **  str_out)

          public  static  extern  Boolean  ParamIsString( string  str,   ref  int  ip);

bool值的解决方法,装换为int类型后强制转换,0为假,不是0为真;

 

      

private  void  button1_Click( object  sender,  EventArgs  e)
{

int k = Convert .ToInt32(textBox1.Text);
Boolean b = Program .ParamIsString(textBox1.Text,k);
textBox2.Text = b.ToString();
}

  就返回值这一块,其实就是一个类型转换,不仅是可行的,而且可以灵活运用起来

3、string 写入,char*在里面,没有问题

To summarize, System.String is preferable when working with immutable strings, especially for input (In) arguments. On the other hand, System.Text.StringBuilder is preferable with changeable strings especially output (Out) arguments.

#include  "stdafx.h"
#define DllExport extern "C" __declspec ( dllexport )
DllExport bool stringbuilder( char *str, char * str_out)
{
char CharBuf[256];
// memcpy(CharBuf,str,sizeof(char)*256);
// memcpy(str_out,CharBuf,sizeof(char)*256);
strcpy(CharBuf,str);
strcat(CharBuf, "ddd" );
strcpy(str_out,CharBuf);
return 1;
}

  这里采用了stringcpy,直接的内存操作,需要在外部结合起来进行边界检查

 

             byte [] srcBytes = System.Text. Encoding  .ASCII.GetBytes(textBox1.Text);

             byte [] resBytes =  new  byte [256];

             bool  b =  Program  .ParamIsString( ref  srcBytes[0],  ref  resBytes[0]);

             string  res = (System.Text. Encoding  .ASCII.GetString(resBytes, 0, resBytes.Length)).TrimEnd();

            textBox2.Text = res;

以及

[ DllImport (  "F:\\04.研D究?项?目?\\PInvoke\\stringbuilder\\Debug\\stringbuilder.dll"  ,
EntryPoint = "stringbuilder" ,
CharSet = CharSet .Auto, CallingConvention = CallingConvention .Cdecl)]
public static extern bool ParamIsString( ref byte src, ref byte res);

有很多需要注意的点  .没有办法传递中文,但是这个方法具有通用性,体现了这种的问题如何来解决。变长等

4、结构体,关键是内外要用一样的数据结构,然后能够修改,这样就很强。

OK,这样可以解决问题:

DllExport  bool  structbuilder(ImageInfo & imginfo,  int  width, int  height, int  type)
{
imginfo.width = width;
imginfo.height = height;
imginfo.type = type;
return 1;
}


[ DllImport ( "F:\\PInvoke\\stringbuilder\\Debug\\stringbuilder.dll" ,
EntryPoint = "structbuilder" ,
CharSet = CharSet .Auto, CallingConvention = CallingConvention .Cdecl)]
public static extern bool ParamIsStruct( ref ImageInfo src, int width, int height, int type);
[ StructLayout ( LayoutKind .Sequential)]
public struct ImageInfo
{
public int width;
public int height;
public int type;
};

并且,非常重要的一点是,这里可以对这些类进行重新的封装,这样能够把csharp的优点和c++原生代码的优点结合起来。结果就可以变得比较稳定。这是一个新的总结性的问题。

 

下面是传递图像。如果int是第一类,代表的是系统原生的类型,struct是一类,代表你能够研究清楚内存放置的结构,而string又是一类,代表的是内存的直接操作。好的opencv的图像,要用上面的所有知识。