More Related Content
C++CLIで、ネイティブCの保守開発に.NETを使って楽をしよう データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3 PostgreSQLのパラレル化に向けた取り組み@第30回(仮名)PostgreSQL勉強会 Ssl証明書を設定したらapacheが起動しない? Intel RealSense for ROSConJP20221121.pdf What's hot (20)
SmartNews TechNight Vol5 : SmartNews AdServer 解体新書 / ポストモーテム constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ Enterprise agile dev ops-and-xr-techonology-adoption-for-fintech-20180324 アセット生成AIで作成したキャラクターをリリースした事例 ~これが『逆転オセロニア』のエイプリルフール!~ PlaySQLAlchemy: SQLAlchemy入門 徳丸本に学ぶ 安全なPHPアプリ開発の鉄則2011 SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File) Kubernetes Meetup Tokyo #35_GitOps Toolkit による Kubernetes マニフェスト CD Viewers also liked (8)
Introduction to C++ over CLI 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 Similar to T69 c++cli ネイティブライブラリラッピング入門 (20)
Handson opencv! 画像処理ライブラリを使って面白いプログラムを作ろう!その2 T69 c++cli ネイティブライブラリラッピング入門
- 2. 自己紹介
• HN 暁 紫電
• Twitter @akatukisiden
• 年齢 25歳
• フリープログラマー
• 使用言語
C++、C++/CLI、C#
• 現在のお仕事
Kinect(OpenNI)、OpenCV、MIDI
わんくま同盟 東京勉強会 #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
- 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
- 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
- 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
- 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
- 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
- 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