csharp编写界面,opencv编写类库,解决 Pinvoke过程中的参数传递和平台调用问题...

 
 
  使用 csharp  编写 winform 程序,不仅速度快,而且容易界面美化并找到其他类库的支持;而使用  opencv 编写图形图像处理程序,是目前比较流行,而且高效的一种方法。如果需要将两者结合,需要解决的问题就是使用  csharp 调用 vc  下编写的库文件。两个难点,一个是平台调用的内存控制问题,一个是参数传递问题。关注点在解决实际问题
在现实中,我发现问题比较大的是两点,一点是内存控制问题,一个是平台调用问题。
  一、内存控制:(1-6种方法是我验证后失败的方法,关注问题解决者可直接看第7点)
1 、验证  opencv 下的 dll  程序是否能够对内存起到很好的控制。方法是将图片路径写在  dll 文件中,使用 csharp 调用  dll 文件,以此来验证是否是函数内部的溢出。
 
DllExport  void  imageprocesstest1()
{       
         vector inputmat;
         vector points1;
         vector points2;
         vector locmat1;vector 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 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 (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 (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.
我相信,marsh和stringbuilder的方法一定可以解决这个问题,但是目前我还没有研究清楚。还是那句老话“一鸟在手胜过千鸟在林”我这里有一种可用的解决方法。
#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的图像,要用上面的所有知识。

你可能感兴趣的:(人工智能,c/c++,数据结构与算法)