SlideShare a Scribd company logo
C++/CLI
ネイティブライブラリラッピン
     グ入門


     暁 紫電


    わんくま同盟 東京勉強会 #69
自己紹介
• HN 暁 紫電
• Twitter @akatukisiden
• 年齢 25歳
• フリープログラマー
• 使用言語
    C++、C++/CLI、C#
• 現在のお仕事
    Kinect(OpenNI)、OpenCV、MIDI

         わんくま同盟 東京勉強会 #69
マネージ コードとネイティブ コード
    相互運用テクノロジ

• P/Invoke
• COM
• C++/CLI



        わんくま同盟 東京勉強会 #69
Platform Invoke (P/Invoke)
• DLLからC スタイルのネイティブ関数を呼び出す。
• ヘッダファイルの情報を
           .NET側に用意する必要がある。
• .NET Frameworkの内部でよく使用されている
• 引数のマネージ←→ネイティブ変換コストに加
  え
    一回の呼び出しでx86命令10~30個分の
    オーバーヘッドが係る
• どんな関数でも正常に呼び出せるわけではない


           わんくま同盟 東京勉強会 #69
COM相互運用
• I Unknown
• マネージ コードからCOMインターフェイスを使
  用、
   またはマネージAPIを COMインターフェイス
として
   公開する機能
• Office関連や、WinRTなどで使われている
• 時間コストはC++/CLIと同程度


              わんくま同盟 東京勉強会 #69
C++/CLI
• Cヰ
• マネージ コードおよびネイティブ コード
  が混在するアセンブリを作成する
• P/Invokeとは違い、見た目は普通の
  マネージオブジェクトにすることが可能
• おそらく、どんなクラス・関数でもラップ
  可能



       わんくま同盟 東京勉強会 #69
今回は
画像処理ライブラリOpenCVのラップを通し
             て
       C++/CLIを用いた
.NETでのネイティブコードの利用について
        紹介します。
※ ヘッダファイルに宣言と定義、両方まとめて書いてい
     ますが、実際は分けて書いてください



         わんくま同盟 東京勉強会 #69
マネージ型

  値・参照\デフォ
      ルト        Public          private
   アクセス指定子

    参照型        ref struct;     ref class;


    値型        value struct;   value class;



• class structの使い方はC++相当
• 値型・参照型はその前につけるvalue/refで決ま
  る。
• 参照型のハンドルは型名の後ろに^をつけて表
  す
             わんくま同盟 東京勉強会 #69
矩形等のサイズを表す構造体
  CvSizeをラップする




    わんくま同盟 東京勉強会 #69
Wrap1 最低限のラップ (CvSize)
public ref class CvSize
{
    public:
        CvSize(void)
        { ptr = new ::CvSize; }

         ~CvSize()
         { this -> !CvSize(); }

         !CvSize()
         { delete ptr; }
     internal:
         ::CvSize* ptr;
};



                  わんくま同盟 東京勉強会 #69
Wrap1 最低限のラップ (CvSize)
• ref class
• ネイティブオブジェクトのポインタを
            internalメンバとして持たせる。
• コンストラクタでオブジェクトを生成
• デストラクタ/ファイナライザで破棄
• デストラクタ(~Class())はDispose(true)相当
• ファイナライザ(!Class())はDispose(false) 相当
• ファイナライザは通常の関数として呼び出し可
  能


             わんくま同盟 東京勉強会 #69
Wrap2 基本型メンバ変数の公開 (CvSize)
public ref class CvSize
{
    // ~~略~~
    public:
         property int width
         {
             int get(){ return ptr_->width;}
             void set( int value ){ ptr_->width = value; }
         }
         property int height
       {
             int get(){return ptr_->height;}
            void set(int value){ptr_->height = value; }
         }
    internal:
         ::CvSize* ptr_
};


                         わんくま同盟 東京勉強会 #69
Wrap2 基本型メンバ変数の公開 (CvSize)

• メンバ変数はプロパティとして公開する
• Internalポインタを通してメンバ変数にアクセス
• 基本型はマネージ、ネイティブ間で互換性有り




          わんくま同盟 東京勉強会 #69
OpenCV画像クラス
IplImageをラップする




   わんくま同盟 東京勉強会 #69
Wrap3 ポインタで管理するクラス
                       (IplImage)
public ref class IplImage
{
    public:
      IplImage(CvSize^ size,int depth,int channels)
      { ptr = ::cvCreateImage(*size->ptr,depth,channels);}

      ~IplImage(){ this->!IplImage(); }

      !IplImage()
      {
          pin_ptr<::IplImage*> pin_Image = &ptr;
          ::cvReleaseImage(pin_Image);
      }
      internal:
          ::IplImage* ptr;
};


                      わんくま同盟 東京勉強会 #69
Wrap3 ポインタで管理するクラス
          (IplImage)
• 独自の生成関数、解放関数を持ち、
  インスタンスはポインタで管理するクラス
• 生成・解放関数自体はコンストラクタ、
      ファイナライザ内で呼び出す。
• ダブルポインタを引数に取る関数(解放関数)は
  そのままではガベコレでアドレスが変わる
  可能性があるというエラーが発生するので
  pin_ptr<T*>でアドレスを固定する



        わんくま同盟 東京勉強会 #69
画像ファイル読み込み関数
    cvLoadImageを
コンストラクタとしてラップ




     わんくま同盟 東京勉強会 #69
Wrap4 文字列変換(cvLoadImage)
public ref class IplImage
{
  // ~~略~~
  public:
    IplImage(System::String^ str, int iscolor)
    {
          const char* chars = (const char*)
                 System::Runtime::InteropServices::Marshal
                               ::StringToHGlobalAnsi(str).ToPo
inter();

               this->ptr = ::cvLoadImage(chars ,iscolor);

               System::Runtime::InteropServices::Marshal
                                ::FreeHGlobal(System::IntPtr((vo
id*)chars));
    }
// ~~略~~
};
                        わんくま同盟 東京勉強会 #69
Wrap4 文字列変換(cvLoadImage)
• System.Stringからchar*に変換
• マネージ・アンマネージの変換用の関数をまと
  めたMarshalクラスのメンバを使用
• IntPtr Marshal::StringToHGlobalAnsi(string);
     String の内容をアンマネージ メモリにコピーし、
     コピー時に ANSI 形式に変換します。
• void Marshal::FreeHGlobal(IntPtr);
     アンマネージメモリから割り当てられたメモリを
     解放します。



                 わんくま同盟 東京勉強会 #69
しきい値処理関数
cvThresholdをラップする




    わんくま同盟 東京勉強会 #69
Wrap5 関数のラップ(cvThreshold)
public ref class IplImage
{
 // ~~略~~
    double cvThreshold( IplImage^ src, IplImage^ dst,
        double threshold, double max_value, int
threshold_type )
    {
        return ::cvThreshold(src->ptr, dst->ptr,
                         threshold, max_value,
threshold_type);
    }
};




                   わんくま同盟 東京勉強会 #69
Wrap5 関数のラップ(cvThreshold)
• オブジェクトを引数に取るものは
   内部ポインタを(必要なら*をつけて)渡す。
• 基本型引数はそのまま渡せる。




          わんくま同盟 東京勉強会 #69
とりあえず一回実行してみる




 ※ 表示に必要な関数は予めラップ済
 み




       わんくま同盟 東京勉強会 #69
表示用関数郡
using namespace System::Runtime::InteropServices;
public ref class GUI
{
  public:
    static int cvNamedWindow( System::String^ str, int flags )
    {
      const char* chars = (const char*)
                     Marshal::StringToHGlobalAnsi(str).ToPointer();
      int r = ::cvNamedWindow(chars,flags);
      Marshal::FreeHGlobal(System::IntPtr((void*)chars));

        return r;
    }




                        わんくま同盟 東京勉強会 #69
表示用関数
     static void cvDestroyWindow( System::String^ str)
     {
       const char* chars = (const char*)
              Marshal::StringToHGlobalAnsi(str).ToPointer();
       ::cvDestroyWindow( chars );
       Marshal::FreeHGlobal( System::IntPtr((void*)chars));
     }

     static void cvShowImage( System::String^ name,IplImage^ image)
     {
         const char* chars = (const char*)
                     Marshal::StringToHGlobalAnsi(name).ToPointer();
         ::cvShowImage( chars ,image->ptr);
         Marshal::FreeHGlobal( System::IntPtr((void*)chars));
       }
       static int cvWaitKey(int delay)
       { return ::cvWaitKey(delay); }
};

                       わんくま同盟 東京勉強会 #69
実行コード C#

class Program
{
    static void Main( string[] args)
    {
        GUI.cvNamedWindow( "window" , 1);

       IplImage loadedImage = new IplImage( "wankuma.png” , 0);

       GUI.cvShowImage( "window” , loadedImage);
       GUI.cvWaitKey(0);

       CvSize size = new CvSize();
       size.width = loadedImage.width;
       size.height = loadedImage.height;
       IplImage img2 = new IplImage(size, loadedImage.depth,
                                          loadedImage.nChannels );



                        わんくま同盟 東京勉強会 #69
実行コード C#

        IplImage.cvThreshold(loadedImage, img2, 205, 255, 0);

        GUI.cvShowImage( "window" , img2);
        GUI.cvWaitKey(0);

        GUI.cvDestroyWindow( "window"   );
    }
}




                        わんくま同盟 東京勉強会 #69
わんくま同盟 東京勉強会 #69
わんくま同盟 東京勉強会 #69
わんくま同盟 東京勉強会 #69
二値画像から輪郭と
その外接矩形を取得する




   わんくま同盟 東京勉強会 #69
Wrap6
 輪郭クラスはメモリストレージ
CvMemStorage内に確保されるので
  まずはCvMemStorageをラップ



       わんくま同盟 東京勉強会 #69
Wrap6 メモリストレージ CvMemStorage
public ref class CvMemStorage
{
    public:
        CvMemStorage( int block_size)
        { ptr_ = ::cvCreateMemStorage(block_size);   }

        ~CvMemStorage() { this->!CvMemStorage();}

       !CvMemStorage()
        {
            pin_ptr< ::CvMemStorage* > p = &(this->ptr_ );
            ::cvReleaseMemStorage( p ) ;
        }
        static void cvClearMemStorage(CvMemStorage^ storage)
        { ::cvClearMemStorage(storage->ptr_ ); }

     internal:
         ::CvMemStorage* ptr_;
};

                          わんくま同盟 東京勉強会 #69
Wrap6 メモリストレージ CvMemStorage

• コンストラクタでストレージ生成
• ファイナライザで全解放
• 割り当て済みストレージの解放関数
 – void cvClearMemStorage(CvMemStorage^ storage);




                わんくま同盟 東京勉強会 #69
wrap7
輪郭クラスCvContourは生成・破棄の方法が
         特殊なので
先にそのメンバ変数rect(CvRect型)をラップ
             し
    プロパティとして公開する




         わんくま同盟 東京勉強会 #69
Wrap7 複合型メンバ変数をプロパティとして公開
           (CvContour::rect)




Class AとそのメンバBをラップしたものが

         わんくま同盟 東京勉強会 #69
Wrap7 複合型メンバ変数をプロパティとして公開
           (CvContour::rect)




ClassAのラッパーがBのラッパーをメンバに持つよ
うに見えるようにする必要がある

          わんくま同盟 東京勉強会 #69
Wrap7 複合型メンバ変数をプロパティとして公開
               (CvContour::rect)
public ref class CvContour
{
   public:
     // ~~略~~
     property CvRect^ rect
     {
       CvRect^ get()
       {
         return gcnew CvRect(&ptr_->rect);
       }
       void set(CvRect^ value)
       {
           *ptr_->rect = *(value->ptr_);
       }
     }
     internal:
         ::CvContour* ptr_;
};

                         わんくま同盟 東京勉強会 #69
Wrap7 複合型メンバ変数をプロパティとして公開
                (CvContour::rect)
public ref class CvRect                   ~CvRect()
{                                         { this->!CvRect();}
  public:
    // ~~略~~                              !CvRect()
    CvRect()                              {
    {                                        if(!noalloc_)
      noalloc_ = false;                      {
      this-> ptr_ = new ::CvRect;              delete ptr_;
    }                                        }
                                             this->ptr_ = nullptr;
   CvRect( ::CvRect* pRect)               }
   {                                    private:
     noalloc_ = true;                      bool noalloc_ ;
     this->ptr_ = pRect;
   }                                    Internal:
                                           ::CvRect* ptr_;
                                    }


                         わんくま同盟 東京勉強会 #69
Wrap7 複合型メンバ変数をプロパティとして公開
            (CvContour::rect)
• プロパティのgetterではポインタを受け取
  るコンストラクタを用いて、
  ラッパーオブジェクトを作成(gcnew)する。
• ポインタを受け取るコンストラクタで初期
  化した場合は非破壊フラグを立て、ファイ
  ナライザでdeleteしないようにする。
• setterではvalue側の内部ポインタの指す値
  をコピーする。


          わんくま同盟 東京勉強会 #69
wrap8
輪郭スキャンクラス
 CvContourScanner
輪郭スキャン関数群
cvStartFindContours
 cvFindNextContour
cvEndFindContours
      のラップ


    わんくま同盟 東京勉強会 #69
Wrap8 輪郭スキャン関連




     使い方が複雑なので
まずC++側でどのように使うかを確認




      わんくま同盟 東京勉強会 #69
Wrap8 輪郭スキャン関連(C++)
IplImage img_th = 閾値処理後の画像
CvContourScanner scanner = cvStartFindContours(img_th, storage);

CvContour* contour;
do
{
    contour = reinterpret_cast< CvContour*>
                            ( cvFindNextContour(scanner) );
    if(contour != nullptr)
    {
      CvRect rect = contour->rect;
    }
}
while (contour != nullptr);
contour = reinterpret_cast<CvContour*>(cvEndFindContours(&scanner));
cvClearMemStorage(contour->storage);



                         わんくま同盟 東京勉強会 #69
Wrap8 輪郭スキャン関連(C++)
• CvContourScanner は_CvContourScanner*のtypedef
• CvContourScannerの定義はヘッダファイルにない
• cvStartFindContours(img,storage)は閾値処理済み画像と、
  輪郭オブジェクトの確保につかうメモリストレージを引数にとり
  CvContourScanner を生成
• cvFindNextContourは、輪郭を一つ取得
• cvEndFindContoursはCvContourScanner を破棄し、
  全部の輪郭データを取得 メンバ変数をたどって別の輪郭の取得も
可能
• cvClearMemStorage(storage)で輪郭データの解放+メモリストレー
  ジにメモリを返還
• cvFindNextContour,cvEndFindContourの戻り値は
  オブジェクトの先頭のメモリ構造を同じにすることで行う疑似継承
  における 疑似基底クラス

                わんくま同盟 東京勉強会 #69
Wrap8 輪郭抽出関数のラップ
public ref class CvContourScanner
{
  public:
    static const int DEFAULT_HEADER_SIZE = sizeof( ::CvContour);

   CvContourScanner(IplImage^ image,CvMemStorage^ storage,
              int header_size, int mode, int method,CvPoint^ offset)
   {
     ptr_ =::cvStartFindContours(image->ptr_,storage->ptr_,
                             header_size,mode,method,*offset->ptr_);
   }

   static CvContourScanner^ cvStartFindContours(
            IplImage^ image,CvMemStorage^ storage, int header_size,
                    int mode, int method,CvPoint^ offset)
   {
       return = gcnew CvContourScanner(image,storage,header_size,
                                   mode,method,offset);
   }
                         わんくま同盟 東京勉強会 #69
Wrap8 輪郭抽出関数のラップ
CvContour^ cvFindNextContour()
{
  ::CvContour* contour= reinterpret_cast< ::CvContour*>
                          ( ::cvFindNextContour(this->ptr_) );
  if(contour != nullptr)
  {
       return gcnew CvContour(contour);
  }
  else
  {
    return nullptr;
  }
}

static CvContour^ cvFindNextContour(CvContourScanner^ scanner)
{
   return scanner->cvFindNextContour();
}

                     わんくま同盟 東京勉強会 #69
Wrap8 輪郭抽出関数のラップ
CvContour^ cvEndFindContours()
{
  pin_ptr< ::_CvContourScanner* > pp = &(this->ptr_);
  ::CvContour* ptr = reinterpret_cast< ::CvContour*>
                                      (::cvEndFindContours(pp));
  pp = nullptr; this->ptr_ = nullptr;
  CvContour^ contour = gcnew CvContour();
  contour->ptr_ = ptr;
  return contour;
}

static CvContour^ cvEndFindContours(CvContourScanner^ scanner)
{
  return scanner->cvEndFindContours();
}




                     わんくま同盟 東京勉強会 #69
Wrap8 輪郭抽出関数のラップ
~CvContourScanner()
{
    this->!CvContourScanner();
}
  !CvContourScanner()
  {
    if(!noalloc_)
    {
      if(this->ptr_ != nullptr)
      {
        pin_ptr< ::_CvContourScanner* > pp = &(this->ptr_);
        ::CvContour* contour = reinterpret_cast< ::CvContour*>
                                      ( ::cvEndFindContours(pp) );
        ::cvClearMemStorage(contour->storage);
        this->ptr_ = nullptr;
      }
    }
}

                      わんくま同盟 東京勉強会 #69
Wrap8 輪郭抽出関数のラップ(C++/CLI)
    private:
       bool noalloc_;

    internal:
    _CvContourScanner* ptr_;
}

struct _CvContourScanner{};




                           わんくま同盟 東京勉強会 #69
Wrap8 輪郭抽出関数のラップ(C++/CLI)
public ref class CvContour
{
  public:
      CvContour(){ noalloc_ = false;   this->ptr_ = nullptr;   }

      ~CvContour() {this->!CvContour();}
      !CvContour()
      {
        if(!noalloc_) { ::cvClearMemStorage(ptr_->storage);    }
      }
  private:
      bool noalloc_;
  internal:
      CvContour( ::CvContour* ptr)
      {noalloc_ = true; this->ptr_ = ptr; }
      ::CvContour* ptr_;
};


                        わんくま同盟 東京勉強会 #69
輪郭抽出関数のラップ

• CvContourScannerがヘッダファイルに定義がないので、
  自分で空の構造体を作成する。
  (ポインタのサイズが分かれば正常に動作するはず)
• CvContourScannerオブジェクトはcvStartFindContours()で作成し、
   cvEndFindContours()で破棄する
   cvEnd~を呼び忘れても良いように、データが破棄されていなけれ
ば、
  ファイナライザで呼び出す。
• 輪郭データの破壊はcvEndFindContours()で返されるオブジェクト
   (全輪郭の代表に)に対して::cvClearMemStorage()を呼び出すこと
   で行う。(ファイナライザで呼び出す。)
• cvFindNextContourの戻り値の輪郭データは開放してはいけないので
    cvFindNext~では非破壊コンストラクタ、
  cvEndFind~では通常のコンストラクタで輪郭オブジェクトを
  作成する(cvContour)
                  わんくま同盟 東京勉強会 #69
もう一回実行してみる




  わんくま同盟 東京勉強会 #69
輪郭・矩形 描画関連 (C++/CLI)
public ref class IplImage
{
   public:
   // ~~略~~
     static void cvDrawContours(IplImage^ img, CvContour^ contour,
                      CvScalar^ external_color,CvScalar^ hole_color,
         int max_level, int thickness ,int line_type, CvPoint^ offset)
     {
       ::cvDrawContours(img->ptr_,reinterpret_cast<CvSeq*>
         (contour->ptr_), *external_color->ptr_, *hole_color->ptr_,
                   max_level,thickness,line_type, *offset->ptr_);
     }
     static void cvRectangleR(IplImage^ img, CvRect^ r,
            CvScalar^ color,int thickness, int line_type, int shift)
     {
        ::cvRectangleR(img->ptr_,*r->ptr_,*color->ptr_,
                                        thickness,line_type,shift);
     }
};
                         わんくま同盟 東京勉強会 #69
続・実行コード C#
class Program
{
  static void Main(string[] args)
  {
     // ~~略~~
     CvMemStorage storage = new CvMemStorage(0);
     CvContourScanner scanner = new CvContourScanner
          (img2,storage,CvContourScanner.DEFAULT_HEADER_SIZE,
                                       1,2,new CvPoint(0,0));

   List<CvRect> list = new List<CvRect>();
   CvContour scanningContour = null;




                     わんくま同盟 東京勉強会 #69
続・実行コード C#
do
{
     scanningContour = scanner.cvFindNextContour();
     if (scanningContour!= null)
     {
        list.Add(scanningContour.rect);
     }
}
while (scanningContour != null);

CvContour endContour = scanner.cvEndFindContours();
IplImage.cvDrawContours(img2,endContour,new CvScalar(64,64,64,64),
       new CvScalar(255,255,255,255), 1,2,8, new CvPoint(0, 0));

GUI.cvShowImage( "window" , img2);
GUI.cvWaitKey(0);




                        わんくま同盟 東京勉強会 #69
続・実行コード C#
      foreach(CvRect r in list)
      {
        IplImage.cvRectangleR(img2, r,
                      new CvScalar(128, 128,128,128), 2, 8, 0);
      }

      endContour.Dispose();
      storage.Dispose();

      GUI.cvShowImage( "window" , img2);
      GUI.cvWaitKey(0);

      img2.Dispose();
      GUI.cvDestroyWindow( "window" );
  }
};


                        わんくま同盟 東京勉強会 #69
わんくま同盟 東京勉強会 #69
わんくま同盟 東京勉強会 #69
わんくま同盟 東京勉強会 #69
わんくま同盟 東京勉強会 #69
わんくま同盟 東京勉強会 #69
まとめ



わんくま同盟 東京勉強会 #69
まとめ

• ネイティブオブジェクトはポインタで管理し
  コンストラクタでnew,ファイナライザでdeleteす
  る
• ポインタ等の露出する部分はinternalにして
  外部から見えないようにする。
• メンバ変数はプロパティとして公開
• ダブルポインタはpin_ptrでアドレスを固定
• char*↔System.String等はMarshalクラスで変換



            わんくま同盟 東京勉強会 #69
まとめ
• 複合型メンバ変数は、ポインタを受け取るコン
  ストラクタを使い、マネージド型(ラッパークラ
  ス)のプロパティとして公開する。
  解放は親が解放される事で行われるので、
  ファイナライザで開放しないようにする。
• マネージドオブジェクトが消滅するとき
  ラップしたネイティブオブジェクトも
  解放(delete)してよいのか注意し、
• よくない場合は、
  コンストラクタでフラグを立てるなどの方法で、
  ファイナライザでの解放を防ぐ

        わんくま同盟 東京勉強会 #69
ご清聴ありがとうございました




    わんくま同盟 東京勉強会 #69

More Related Content

PPTX
C++CLIで、ネイティブCの保守開発に.NETを使って楽をしよう
PPTX
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
PDF
PostgreSQLのパラレル化に向けた取り組み@第30回(仮名)PostgreSQL勉強会
PDF
トランザクションスクリプトのすすめ
PDF
Ssl証明書を設定したらapacheが起動しない?
PDF
あなたの知らないPostgreSQL監視の世界
PDF
DBスキーマもバージョン管理したい!
PDF
Intel RealSense for ROSConJP20221121.pdf
C++CLIで、ネイティブCの保守開発に.NETを使って楽をしよう
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
PostgreSQLのパラレル化に向けた取り組み@第30回(仮名)PostgreSQL勉強会
トランザクションスクリプトのすすめ
Ssl証明書を設定したらapacheが起動しない?
あなたの知らないPostgreSQL監視の世界
DBスキーマもバージョン管理したい!
Intel RealSense for ROSConJP20221121.pdf

What's hot (20)

PDF
超簡単!Subversion入門 概念編
PDF
リクルートにおけるデータのインフラ化への取組
PDF
Docker Swarm入門
PDF
MongoDBのアレをアレする
PDF
SmartNews TechNight Vol5 : SmartNews AdServer 解体新書 / ポストモーテム
PDF
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
PPTX
モデル高速化百選
PDF
SREチームとしてSREしてみた話
PDF
Djangoフレームワークの紹介
PDF
Hadoop入門
PDF
Enterprise agile dev ops-and-xr-techonology-adoption-for-fintech-20180324
PPTX
Depth Estimation論文紹介
PDF
アセット生成AIで作成したキャラクターをリリースした事例 ~これが『逆転オセロニア』のエイプリルフール!~
PDF
それはYAGNIか? それとも思考停止か?
PDF
オススメの標準・準標準パッケージ20選
PDF
PlaySQLAlchemy: SQLAlchemy入門
PDF
徳丸本に学ぶ 安全なPHPアプリ開発の鉄則2011
PPTX
Python製BDDツールで自動化してみた
PDF
SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File)
PDF
Kubernetes Meetup Tokyo #35_GitOps Toolkit による Kubernetes マニフェスト CD
超簡単!Subversion入門 概念編
リクルートにおけるデータのインフラ化への取組
Docker Swarm入門
MongoDBのアレをアレする
SmartNews TechNight Vol5 : SmartNews AdServer 解体新書 / ポストモーテム
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
モデル高速化百選
SREチームとしてSREしてみた話
Djangoフレームワークの紹介
Hadoop入門
Enterprise agile dev ops-and-xr-techonology-adoption-for-fintech-20180324
Depth Estimation論文紹介
アセット生成AIで作成したキャラクターをリリースした事例 ~これが『逆転オセロニア』のエイプリルフール!~
それはYAGNIか? それとも思考停止か?
オススメの標準・準標準パッケージ20選
PlaySQLAlchemy: SQLAlchemy入門
徳丸本に学ぶ 安全なPHPアプリ開発の鉄則2011
Python製BDDツールで自動化してみた
SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File)
Kubernetes Meetup Tokyo #35_GitOps Toolkit による Kubernetes マニフェスト CD
Ad

Viewers also liked (8)

PPT
Introduction to C++ over CLI
PPTX
Kinect深度情報処理入門
PDF
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
PDF
Photon Server Deep Dive - PhotonWireの実装から見つめるPhotonServerの基礎と応用
PDF
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
PPTX
RuntimeUnitTestToolkit for Unity
PDF
NextGen Server/Client Architecture - gRPC + Unity + C#
PDF
ZeroFormatter/MagicOnion - Fastest C# Serializer/gRPC based C# RPC
Introduction to C++ over CLI
Kinect深度情報処理入門
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
Photon Server Deep Dive - PhotonWireの実装から見つめるPhotonServerの基礎と応用
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
RuntimeUnitTestToolkit for Unity
NextGen Server/Client Architecture - gRPC + Unity + C#
ZeroFormatter/MagicOnion - Fastest C# Serializer/gRPC based C# RPC
Ad

Similar to T69 c++cli ネイティブライブラリラッピング入門 (20)

PDF
わんくま同盟大阪勉強会#61
PPTX
OpenCVを用いた画像処理入門
PDF
C++0x 言語の未来を語る
PDF
Handson opencv! 画像処理ライブラリを使って面白いプログラムを作ろう!その2
PDF
C++ ポインタ ブートキャンプ
PPTX
わんくまT78 mfcを始めようとしてみた
PDF
OpenCVの拡張ユーティリティ関数群
PPTX
T93 com入門
PDF
vImageのススメ
PPTX
ぱっと見でわかるC++11
PDF
ゲーム開発者のための C++11/C++14
PDF
vImageのススメ(改訂版)
PDF
Siv3Dで楽しむゲームとメディアアート開発
PDF
C++11概要 ライブラリ編
PDF
emc++ chapter32
PDF
怪しいWindowsプログラミング
PDF
C++コミュニティーの中心でC++をDISる
PPTX
2012 kanemotolablecture4
PPT
オブジェクト指向入門7
PPT
C++でぼくが忘れがちなこと
わんくま同盟大阪勉強会#61
OpenCVを用いた画像処理入門
C++0x 言語の未来を語る
Handson opencv! 画像処理ライブラリを使って面白いプログラムを作ろう!その2
C++ ポインタ ブートキャンプ
わんくまT78 mfcを始めようとしてみた
OpenCVの拡張ユーティリティ関数群
T93 com入門
vImageのススメ
ぱっと見でわかるC++11
ゲーム開発者のための C++11/C++14
vImageのススメ(改訂版)
Siv3Dで楽しむゲームとメディアアート開発
C++11概要 ライブラリ編
emc++ chapter32
怪しいWindowsプログラミング
C++コミュニティーの中心でC++をDISる
2012 kanemotolablecture4
オブジェクト指向入門7
C++でぼくが忘れがちなこと

T69 c++cli ネイティブライブラリラッピング入門

  • 1. C++/CLI ネイティブライブラリラッピン グ入門 暁 紫電 わんくま同盟 東京勉強会 #69
  • 2. 自己紹介 • HN 暁 紫電 • Twitter @akatukisiden • 年齢 25歳 • フリープログラマー • 使用言語 C++、C++/CLI、C# • 現在のお仕事 Kinect(OpenNI)、OpenCV、MIDI わんくま同盟 東京勉強会 #69
  • 3. マネージ コードとネイティブ コード 相互運用テクノロジ • P/Invoke • COM • C++/CLI わんくま同盟 東京勉強会 #69
  • 4. Platform Invoke (P/Invoke) • DLLからC スタイルのネイティブ関数を呼び出す。 • ヘッダファイルの情報を .NET側に用意する必要がある。 • .NET Frameworkの内部でよく使用されている • 引数のマネージ←→ネイティブ変換コストに加 え 一回の呼び出しでx86命令10~30個分の オーバーヘッドが係る • どんな関数でも正常に呼び出せるわけではない わんくま同盟 東京勉強会 #69
  • 5. COM相互運用 • I Unknown • マネージ コードからCOMインターフェイスを使 用、 またはマネージAPIを COMインターフェイス として 公開する機能 • Office関連や、WinRTなどで使われている • 時間コストはC++/CLIと同程度 わんくま同盟 東京勉強会 #69
  • 6. C++/CLI • Cヰ • マネージ コードおよびネイティブ コード が混在するアセンブリを作成する • P/Invokeとは違い、見た目は普通の マネージオブジェクトにすることが可能 • おそらく、どんなクラス・関数でもラップ 可能 わんくま同盟 東京勉強会 #69
  • 7. 今回は 画像処理ライブラリOpenCVのラップを通し て C++/CLIを用いた .NETでのネイティブコードの利用について 紹介します。 ※ ヘッダファイルに宣言と定義、両方まとめて書いてい ますが、実際は分けて書いてください わんくま同盟 東京勉強会 #69
  • 8. マネージ型 値・参照\デフォ ルト Public private アクセス指定子 参照型 ref struct; ref class; 値型 value struct; value class; • class structの使い方はC++相当 • 値型・参照型はその前につけるvalue/refで決ま る。 • 参照型のハンドルは型名の後ろに^をつけて表 す わんくま同盟 東京勉強会 #69
  • 10. Wrap1 最低限のラップ (CvSize) public ref class CvSize { public: CvSize(void) { ptr = new ::CvSize; } ~CvSize() { this -> !CvSize(); } !CvSize() { delete ptr; } internal: ::CvSize* ptr; }; わんくま同盟 東京勉強会 #69
  • 11. Wrap1 最低限のラップ (CvSize) • ref class • ネイティブオブジェクトのポインタを internalメンバとして持たせる。 • コンストラクタでオブジェクトを生成 • デストラクタ/ファイナライザで破棄 • デストラクタ(~Class())はDispose(true)相当 • ファイナライザ(!Class())はDispose(false) 相当 • ファイナライザは通常の関数として呼び出し可 能 わんくま同盟 東京勉強会 #69
  • 12. Wrap2 基本型メンバ変数の公開 (CvSize) public ref class CvSize { // ~~略~~ public: property int width { int get(){ return ptr_->width;} void set( int value ){ ptr_->width = value; } } property int height { int get(){return ptr_->height;} void set(int value){ptr_->height = value; } } internal: ::CvSize* ptr_ }; わんくま同盟 東京勉強会 #69
  • 13. Wrap2 基本型メンバ変数の公開 (CvSize) • メンバ変数はプロパティとして公開する • Internalポインタを通してメンバ変数にアクセス • 基本型はマネージ、ネイティブ間で互換性有り わんくま同盟 東京勉強会 #69
  • 14. OpenCV画像クラス IplImageをラップする わんくま同盟 東京勉強会 #69
  • 15. Wrap3 ポインタで管理するクラス (IplImage) public ref class IplImage { public: IplImage(CvSize^ size,int depth,int channels) { ptr = ::cvCreateImage(*size->ptr,depth,channels);} ~IplImage(){ this->!IplImage(); } !IplImage() { pin_ptr<::IplImage*> pin_Image = &ptr; ::cvReleaseImage(pin_Image); } internal: ::IplImage* ptr; }; わんくま同盟 東京勉強会 #69
  • 16. Wrap3 ポインタで管理するクラス (IplImage) • 独自の生成関数、解放関数を持ち、 インスタンスはポインタで管理するクラス • 生成・解放関数自体はコンストラクタ、 ファイナライザ内で呼び出す。 • ダブルポインタを引数に取る関数(解放関数)は そのままではガベコレでアドレスが変わる 可能性があるというエラーが発生するので pin_ptr<T*>でアドレスを固定する わんくま同盟 東京勉強会 #69
  • 17. 画像ファイル読み込み関数 cvLoadImageを コンストラクタとしてラップ わんくま同盟 東京勉強会 #69
  • 18. Wrap4 文字列変換(cvLoadImage) public ref class IplImage { // ~~略~~ public: IplImage(System::String^ str, int iscolor) { const char* chars = (const char*) System::Runtime::InteropServices::Marshal ::StringToHGlobalAnsi(str).ToPo inter(); this->ptr = ::cvLoadImage(chars ,iscolor); System::Runtime::InteropServices::Marshal ::FreeHGlobal(System::IntPtr((vo id*)chars)); } // ~~略~~ }; わんくま同盟 東京勉強会 #69
  • 19. Wrap4 文字列変換(cvLoadImage) • System.Stringからchar*に変換 • マネージ・アンマネージの変換用の関数をまと めたMarshalクラスのメンバを使用 • IntPtr Marshal::StringToHGlobalAnsi(string); String の内容をアンマネージ メモリにコピーし、 コピー時に ANSI 形式に変換します。 • void Marshal::FreeHGlobal(IntPtr); アンマネージメモリから割り当てられたメモリを 解放します。 わんくま同盟 東京勉強会 #69
  • 20. しきい値処理関数 cvThresholdをラップする わんくま同盟 東京勉強会 #69
  • 21. Wrap5 関数のラップ(cvThreshold) public ref class IplImage { // ~~略~~ double cvThreshold( IplImage^ src, IplImage^ dst, double threshold, double max_value, int threshold_type ) { return ::cvThreshold(src->ptr, dst->ptr, threshold, max_value, threshold_type); } }; わんくま同盟 東京勉強会 #69
  • 22. Wrap5 関数のラップ(cvThreshold) • オブジェクトを引数に取るものは 内部ポインタを(必要なら*をつけて)渡す。 • 基本型引数はそのまま渡せる。 わんくま同盟 東京勉強会 #69
  • 24. 表示用関数郡 using namespace System::Runtime::InteropServices; public ref class GUI { public: static int cvNamedWindow( System::String^ str, int flags ) { const char* chars = (const char*) Marshal::StringToHGlobalAnsi(str).ToPointer(); int r = ::cvNamedWindow(chars,flags); Marshal::FreeHGlobal(System::IntPtr((void*)chars)); return r; } わんくま同盟 東京勉強会 #69
  • 25. 表示用関数 static void cvDestroyWindow( System::String^ str) { const char* chars = (const char*) Marshal::StringToHGlobalAnsi(str).ToPointer(); ::cvDestroyWindow( chars ); Marshal::FreeHGlobal( System::IntPtr((void*)chars)); } static void cvShowImage( System::String^ name,IplImage^ image) { const char* chars = (const char*) Marshal::StringToHGlobalAnsi(name).ToPointer(); ::cvShowImage( chars ,image->ptr); Marshal::FreeHGlobal( System::IntPtr((void*)chars)); } static int cvWaitKey(int delay) { return ::cvWaitKey(delay); } }; わんくま同盟 東京勉強会 #69
  • 26. 実行コード C# class Program { static void Main( string[] args) { GUI.cvNamedWindow( "window" , 1); IplImage loadedImage = new IplImage( "wankuma.png” , 0); GUI.cvShowImage( "window” , loadedImage); GUI.cvWaitKey(0); CvSize size = new CvSize(); size.width = loadedImage.width; size.height = loadedImage.height; IplImage img2 = new IplImage(size, loadedImage.depth, loadedImage.nChannels ); わんくま同盟 東京勉強会 #69
  • 27. 実行コード C# IplImage.cvThreshold(loadedImage, img2, 205, 255, 0); GUI.cvShowImage( "window" , img2); GUI.cvWaitKey(0); GUI.cvDestroyWindow( "window" ); } } わんくま同盟 東京勉強会 #69
  • 31. 二値画像から輪郭と その外接矩形を取得する わんくま同盟 東京勉強会 #69
  • 32. Wrap6 輪郭クラスはメモリストレージ CvMemStorage内に確保されるので まずはCvMemStorageをラップ わんくま同盟 東京勉強会 #69
  • 33. Wrap6 メモリストレージ CvMemStorage public ref class CvMemStorage { public: CvMemStorage( int block_size) { ptr_ = ::cvCreateMemStorage(block_size); } ~CvMemStorage() { this->!CvMemStorage();} !CvMemStorage() { pin_ptr< ::CvMemStorage* > p = &(this->ptr_ ); ::cvReleaseMemStorage( p ) ; } static void cvClearMemStorage(CvMemStorage^ storage) { ::cvClearMemStorage(storage->ptr_ ); } internal: ::CvMemStorage* ptr_; }; わんくま同盟 東京勉強会 #69
  • 34. Wrap6 メモリストレージ CvMemStorage • コンストラクタでストレージ生成 • ファイナライザで全解放 • 割り当て済みストレージの解放関数 – void cvClearMemStorage(CvMemStorage^ storage); わんくま同盟 東京勉強会 #69
  • 35. wrap7 輪郭クラスCvContourは生成・破棄の方法が 特殊なので 先にそのメンバ変数rect(CvRect型)をラップ し プロパティとして公開する わんくま同盟 東京勉強会 #69
  • 36. Wrap7 複合型メンバ変数をプロパティとして公開 (CvContour::rect) Class AとそのメンバBをラップしたものが わんくま同盟 東京勉強会 #69
  • 37. Wrap7 複合型メンバ変数をプロパティとして公開 (CvContour::rect) ClassAのラッパーがBのラッパーをメンバに持つよ うに見えるようにする必要がある わんくま同盟 東京勉強会 #69
  • 38. Wrap7 複合型メンバ変数をプロパティとして公開 (CvContour::rect) public ref class CvContour { public: // ~~略~~ property CvRect^ rect { CvRect^ get() { return gcnew CvRect(&ptr_->rect); } void set(CvRect^ value) { *ptr_->rect = *(value->ptr_); } } internal: ::CvContour* ptr_; }; わんくま同盟 東京勉強会 #69
  • 39. Wrap7 複合型メンバ変数をプロパティとして公開 (CvContour::rect) public ref class CvRect ~CvRect() { { this->!CvRect();} public: // ~~略~~ !CvRect() CvRect() { { if(!noalloc_) noalloc_ = false; { this-> ptr_ = new ::CvRect; delete ptr_; } } this->ptr_ = nullptr; CvRect( ::CvRect* pRect) } { private: noalloc_ = true; bool noalloc_ ; this->ptr_ = pRect; } Internal: ::CvRect* ptr_; } わんくま同盟 東京勉強会 #69
  • 40. Wrap7 複合型メンバ変数をプロパティとして公開 (CvContour::rect) • プロパティのgetterではポインタを受け取 るコンストラクタを用いて、 ラッパーオブジェクトを作成(gcnew)する。 • ポインタを受け取るコンストラクタで初期 化した場合は非破壊フラグを立て、ファイ ナライザでdeleteしないようにする。 • setterではvalue側の内部ポインタの指す値 をコピーする。 わんくま同盟 東京勉強会 #69
  • 42. Wrap8 輪郭スキャン関連 使い方が複雑なので まずC++側でどのように使うかを確認 わんくま同盟 東京勉強会 #69
  • 43. Wrap8 輪郭スキャン関連(C++) IplImage img_th = 閾値処理後の画像 CvContourScanner scanner = cvStartFindContours(img_th, storage); CvContour* contour; do { contour = reinterpret_cast< CvContour*> ( cvFindNextContour(scanner) ); if(contour != nullptr) { CvRect rect = contour->rect; } } while (contour != nullptr); contour = reinterpret_cast<CvContour*>(cvEndFindContours(&scanner)); cvClearMemStorage(contour->storage); わんくま同盟 東京勉強会 #69
  • 44. Wrap8 輪郭スキャン関連(C++) • CvContourScanner は_CvContourScanner*のtypedef • CvContourScannerの定義はヘッダファイルにない • cvStartFindContours(img,storage)は閾値処理済み画像と、 輪郭オブジェクトの確保につかうメモリストレージを引数にとり CvContourScanner を生成 • cvFindNextContourは、輪郭を一つ取得 • cvEndFindContoursはCvContourScanner を破棄し、 全部の輪郭データを取得 メンバ変数をたどって別の輪郭の取得も 可能 • cvClearMemStorage(storage)で輪郭データの解放+メモリストレー ジにメモリを返還 • cvFindNextContour,cvEndFindContourの戻り値は オブジェクトの先頭のメモリ構造を同じにすることで行う疑似継承 における 疑似基底クラス わんくま同盟 東京勉強会 #69
  • 45. Wrap8 輪郭抽出関数のラップ public ref class CvContourScanner { public: static const int DEFAULT_HEADER_SIZE = sizeof( ::CvContour); CvContourScanner(IplImage^ image,CvMemStorage^ storage, int header_size, int mode, int method,CvPoint^ offset) { ptr_ =::cvStartFindContours(image->ptr_,storage->ptr_, header_size,mode,method,*offset->ptr_); } static CvContourScanner^ cvStartFindContours( IplImage^ image,CvMemStorage^ storage, int header_size, int mode, int method,CvPoint^ offset) { return = gcnew CvContourScanner(image,storage,header_size, mode,method,offset); } わんくま同盟 東京勉強会 #69
  • 46. Wrap8 輪郭抽出関数のラップ CvContour^ cvFindNextContour() { ::CvContour* contour= reinterpret_cast< ::CvContour*> ( ::cvFindNextContour(this->ptr_) ); if(contour != nullptr) { return gcnew CvContour(contour); } else { return nullptr; } } static CvContour^ cvFindNextContour(CvContourScanner^ scanner) { return scanner->cvFindNextContour(); } わんくま同盟 東京勉強会 #69
  • 47. Wrap8 輪郭抽出関数のラップ CvContour^ cvEndFindContours() { pin_ptr< ::_CvContourScanner* > pp = &(this->ptr_); ::CvContour* ptr = reinterpret_cast< ::CvContour*> (::cvEndFindContours(pp)); pp = nullptr; this->ptr_ = nullptr; CvContour^ contour = gcnew CvContour(); contour->ptr_ = ptr; return contour; } static CvContour^ cvEndFindContours(CvContourScanner^ scanner) { return scanner->cvEndFindContours(); } わんくま同盟 東京勉強会 #69
  • 48. Wrap8 輪郭抽出関数のラップ ~CvContourScanner() { this->!CvContourScanner(); } !CvContourScanner() { if(!noalloc_) { if(this->ptr_ != nullptr) { pin_ptr< ::_CvContourScanner* > pp = &(this->ptr_); ::CvContour* contour = reinterpret_cast< ::CvContour*> ( ::cvEndFindContours(pp) ); ::cvClearMemStorage(contour->storage); this->ptr_ = nullptr; } } } わんくま同盟 東京勉強会 #69
  • 49. Wrap8 輪郭抽出関数のラップ(C++/CLI) private: bool noalloc_; internal: _CvContourScanner* ptr_; } struct _CvContourScanner{}; わんくま同盟 東京勉強会 #69
  • 50. Wrap8 輪郭抽出関数のラップ(C++/CLI) public ref class CvContour { public: CvContour(){ noalloc_ = false; this->ptr_ = nullptr; } ~CvContour() {this->!CvContour();} !CvContour() { if(!noalloc_) { ::cvClearMemStorage(ptr_->storage); } } private: bool noalloc_; internal: CvContour( ::CvContour* ptr) {noalloc_ = true; this->ptr_ = ptr; } ::CvContour* ptr_; }; わんくま同盟 東京勉強会 #69
  • 51. 輪郭抽出関数のラップ • CvContourScannerがヘッダファイルに定義がないので、 自分で空の構造体を作成する。 (ポインタのサイズが分かれば正常に動作するはず) • CvContourScannerオブジェクトはcvStartFindContours()で作成し、 cvEndFindContours()で破棄する cvEnd~を呼び忘れても良いように、データが破棄されていなけれ ば、 ファイナライザで呼び出す。 • 輪郭データの破壊はcvEndFindContours()で返されるオブジェクト (全輪郭の代表に)に対して::cvClearMemStorage()を呼び出すこと で行う。(ファイナライザで呼び出す。) • cvFindNextContourの戻り値の輪郭データは開放してはいけないので cvFindNext~では非破壊コンストラクタ、 cvEndFind~では通常のコンストラクタで輪郭オブジェクトを 作成する(cvContour) わんくま同盟 東京勉強会 #69
  • 53. 輪郭・矩形 描画関連 (C++/CLI) public ref class IplImage { public: // ~~略~~ static void cvDrawContours(IplImage^ img, CvContour^ contour, CvScalar^ external_color,CvScalar^ hole_color, int max_level, int thickness ,int line_type, CvPoint^ offset) { ::cvDrawContours(img->ptr_,reinterpret_cast<CvSeq*> (contour->ptr_), *external_color->ptr_, *hole_color->ptr_, max_level,thickness,line_type, *offset->ptr_); } static void cvRectangleR(IplImage^ img, CvRect^ r, CvScalar^ color,int thickness, int line_type, int shift) { ::cvRectangleR(img->ptr_,*r->ptr_,*color->ptr_, thickness,line_type,shift); } }; わんくま同盟 東京勉強会 #69
  • 54. 続・実行コード C# class Program { static void Main(string[] args) { // ~~略~~ CvMemStorage storage = new CvMemStorage(0); CvContourScanner scanner = new CvContourScanner (img2,storage,CvContourScanner.DEFAULT_HEADER_SIZE, 1,2,new CvPoint(0,0)); List<CvRect> list = new List<CvRect>(); CvContour scanningContour = null; わんくま同盟 東京勉強会 #69
  • 55. 続・実行コード C# do { scanningContour = scanner.cvFindNextContour(); if (scanningContour!= null) { list.Add(scanningContour.rect); } } while (scanningContour != null); CvContour endContour = scanner.cvEndFindContours(); IplImage.cvDrawContours(img2,endContour,new CvScalar(64,64,64,64), new CvScalar(255,255,255,255), 1,2,8, new CvPoint(0, 0)); GUI.cvShowImage( "window" , img2); GUI.cvWaitKey(0); わんくま同盟 東京勉強会 #69
  • 56. 続・実行コード C# foreach(CvRect r in list) { IplImage.cvRectangleR(img2, r, new CvScalar(128, 128,128,128), 2, 8, 0); } endContour.Dispose(); storage.Dispose(); GUI.cvShowImage( "window" , img2); GUI.cvWaitKey(0); img2.Dispose(); GUI.cvDestroyWindow( "window" ); } }; わんくま同盟 東京勉強会 #69
  • 63. まとめ • ネイティブオブジェクトはポインタで管理し コンストラクタでnew,ファイナライザでdeleteす る • ポインタ等の露出する部分はinternalにして 外部から見えないようにする。 • メンバ変数はプロパティとして公開 • ダブルポインタはpin_ptrでアドレスを固定 • char*↔System.String等はMarshalクラスで変換 わんくま同盟 東京勉強会 #69
  • 64. まとめ • 複合型メンバ変数は、ポインタを受け取るコン ストラクタを使い、マネージド型(ラッパークラ ス)のプロパティとして公開する。 解放は親が解放される事で行われるので、 ファイナライザで開放しないようにする。 • マネージドオブジェクトが消滅するとき ラップしたネイティブオブジェクトも 解放(delete)してよいのか注意し、 • よくない場合は、 コンストラクタでフラグを立てるなどの方法で、 ファイナライザでの解放を防ぐ わんくま同盟 東京勉強会 #69
  • 65. ご清聴ありがとうございました わんくま同盟 東京勉強会 #69