C#でgLupeSDKを使う

1. C#からgLupeSDKを使いたい

本記事のタイトルをご覧になって内容を閲覧している方には不要な説明かもしれませんが、gLupeSDKとは、gLupeのご紹介 (https://wazalabo.com/glupe-introduction.html) 後半に記載されている「推論ソフトウェア開発キット」にあたります。

gLupeSDKはC++で実装されているWin32プラットフォーム向けのライブラリです。そのため、Win32プラットフォームでC++からgLupeSDKの各関数を呼び出すことは容易です。しかし、gLupeSDKを使った推論アプリケーションのGUIを作成するとなると他のプラットフォーム(例えばC#/WPFなど)で作成したいという状況も予想されます。その際、gLupeSDKの各関数を呼び出す場合にいくつか注意する点があります。

本記事では例としてC#からgLupeを呼び出す簡単なサンプルを通して、その手順を紹介していきます。

2. C#からgLupeSDKを呼び出すために

2.1. マーシャリング

では、C++のAPIを備えるgLupeSDKをWin32/C++以外の異なるプラットフォームから呼び出す場合、どのような点に気を付ける必要があるのでしょうか?

例えば、Win32/C++のAPIを備えるgLupeSDKを.NetFramework/C#から呼び出す場合、C#とC++間のデータ形式の違いに注意して適切にデータ変換する必要があります。具体的には、C++の関数をC#から呼び出すとき、C++の関数の引数や戻り値をC#側で使用できるよう適切な変換を行うことが必要となります。この変換操作はマーシャリングと呼ばれます。

一般にマーシャリングは、異なるシステム間でデータの受け渡しをするときに、受け渡し先でもデータが使えるようにデータの変換を行うことを差します。C++/C#間では整数型や浮動小数点型などはあまり気にする必要はありませんが、文字列型や構造体、クラスなどは適切なマーシャリングを行う必要があります。

ここではC#のデータ型をC++で使えるようにマーシャリングを行う簡単なサンプルを紹介します。

2.2. マーシャリングのサンプル

ここでは、文字列型の引数をマーシャリングする例を紹介します(後述するgLupeSDKのC#からの呼び出しで使用するため)。

例として、C++で実装されたDLL(sample.dll)に以下のような関数APIが定義されているとします。

C++
int testMethod(const char* testChar, int testInt, float testFloat, double testDouble){
  return 0;
}

C#から上記の関数APIを使用する場合、以下のように呼び出します。

C#
public class Sharp{
[DllImport(“sample.dll”)]
  public static extern int testMethod(string testString, int testInt = 2, float testFloat = 1.25F, double testDouble = 0.5);

  string testString = “test”;
  int testIntRet = testMethod(testString);
}

C++で const char*型 を引数としている場合、 C#からは string型 を渡す必要があります。
string以外では、C++とC#で型名が同じもの、同じでないものがありますので、その点は注意が必要です。

3. C#からgLupeSDKを呼び出してみた

3.1. サンプルプログラムについて

C#からgLupeSDKを呼びだすサンプルプログラムをこちらに用意しました(実行環境などは後述)。

サンプルプログラムは「CallSDK」と「gLupeSDK_wrapper」の2つに分かれています。

CallSDKはC#で実装されたGUI(WindowsForm)を備えたアプリケーションで、内部的にgLupeSDK_wrapperを呼び出しています。GUIの画面イメージは以下となります。「推論評価する画像を選択」「学習済みモデルを選択」「推論実行」の3ステップで対象画像の最大異常度を出力します。

glupe_sdk_pic

gLupeSDK_wrapperはC++で実装されており、内部的にはgLupeSDKを呼び出しているWrapper DLLです。gLupeSDKで推論を実行するためには初期化やモデルファイル読み込み、推論、終了処理など一連の手続きが必要となります。gLupeSDK_wrapperではこれらの一連の手続きを1つの関数にまとめて、より簡単に呼び出すための工夫をしています。

ざっくりとGUIアプリケーションとラッパーDLLの関係を図で描くと以下となります。

glupe_sdk_pic2

3.2. ソースコードを見てみる

では各ソリューションの中身のソースコードを見ていきましょう。

gLupeSDK_wrapper

gLupeSDKをラップするDLLです。
gLupeSDKのAPIをC#から使用する際、各関数をそれぞれマーシャリングする方法もありますが、手間がかかってしまいます。そこで、今回は前述の通り、gLupeSDKの一連の手続きをひとまとめにした関数を用意しています。
具体的には、学習済みモデルファイルパスと推論対象の画像ファイルパスを引数として渡すと推論対象画像の最大異常度を返すようなラッパー関数を用意しました。

gLupeSDK_wrapper.cppの125-240行目にその関数があります。1関数としては長いですが、、、手続きを記述しているのでここでは目をつぶってください。

DLLEXPORT float testgLupeSDKwrapper(const char* model_filepath, const char* img_filepath) {
…
}

ラッパー関数の引数には学習済みモデルパスと推論対象の画像パスを指定する必要があります。これらは文字列ですので、const char*型で定義してあります。C#側から呼び出す場合は、前述のサンプルのようにstring型の引数でパス文字列を渡す必要があります。

CallSDK

GUI側のソリューションです。Form1.csにgLupeSDK_wrapper呼び出し処理が記述されています。

まず、Form1.csの16-17行目にgLupeSDK_wrapper.dllのエントリーポイントを指定しています。関数名の変更は行っていません。

[DllImport("gLupeSDK_wrapper.dll", EntryPoint = "testgLupeSDKwrapper")]
public static extern float testgLupeSDKwrapper(string modelfile_path, string imagefile_path);

指定したエントリーポイントは前述したgLupeSDKの一連の手続きをまとめた関数です。引数のconst char*型は前述したサンプルのようにstring型に変更しています。

Form1.csの55行目で上記エントリーポイントを呼び出しています。戻り値はfloat型で推論対象画像の最大異常度が返ります。float型はC++/C#間では型変換を行う必要はありません。

上記エントリーポイントを呼び出しているこのメソッド「start_button_Click(object sender, EventArgs e)」は「推論開始」ボタンのイベントハンドラです。処理自体は非常にシンプルです。

4. プログラムのダウンロード

4.1. ダウンロードリンク

https://glupe.jp/ja/sample/v1/call_glupe_sdk_sample.zip

4.2. 実行環境

Windows 10 Pro
Visual Studio 2013
GPU (CUDA対応NVIDIA製GPU メモリ2GB以上 Maxwell以降)