vvvv RealSenseプラグイン開発 その3

Pocket

前回に引き続きvvvvでRealSenseを扱えるようにプラグインを開発していきます。

vvvvのプラグイン開発&RealSenseネタというニッチ過ぎてもはや誰得なんだという状況なので、
RealSenseプラグインそのものの作り方というよりは、
開発中に気づいた出来るだけ汎用的に使えるようなトピックについて書いていければと思います。
プラグインはそのうちGithubに上げるのでブログに細かくコード載せてもしょうがないですし。

前回まででDepth画像が取得できるようになりました。
今回は手の検出、顔の検出、声の検出まで一気にいきます。
やっつけ感出てます…。

作ったノード

以下作ったノードと動作時の挙動です。

引き続き教科書はこちら。

これのコードを元にvvvv向けにアレンジしていくだけなので割とC#初心者でも簡単です。

手の検出: HandNode

手の骨格情報、ジェスチャーを検出することが出来ます。
get_hand1

顔の検出: FaceNode

顔をトラッキングして画像を表示しています。(隠してます…。)
あとは顔の表出情報を検出することが出来ます。
表出情報とは眉の上がり加減、口の開き加減、目の閉じ加減などの顔のパーツの状態のことです。
これら複数の表出情報を計測して表情を分析することが出来ます。
教科書(上で紹介した本)だと自分で表出情報を分析しなくても直接表情情報を取得してくれるPXC(M)Emotionというクラスを使っていますが、
この記事を書いているときのSDKのバージョン(R5)では無くなっている様です。
なので表情は自分で表出情報を分析しなくてはいけないかも。
違ってたら誰か教えてくださいm(__)m

それとちょっと面白いのは心拍数が測定できます。
どういう仕組みなんだろ?

顔の検出系のノードはF200などのRealSenseカメラが無くても普通のWebカメラでも使用可能です。

get_face2

get_face1

get_face3

声の検出: SpeechRecognitionNode

Google先生にしゃべってもらった言葉をちゃんと聞き取れています。。。

vvvvプラグイン書き方Tips

コンストラクタ・初期化

Dynamic Plugins ReferenceのThe Constructor: Initializing stuffを参照。
[Input(…)]/[Output(…)]で指定したピンとなる変数を初期化する場合はOnImportsSatisfied()を使わなければならない模様。
またはこういう書き方もあるみたい。

[ImportingConstructor]
public MyNode()
{
    // ピンとなる変数の初期化処理
}

Enumの入力ピンの準備

AudioInノードとかにあるこういう↓入力ピンを作るにはどうしたら良いのでしょうか?
enum

コンストラクタで使用できる音声入力デバイスを列挙して、ピン変数を初期化する必要があるのでこんな感じで書きます。
EnumEntryとEnumManagerがキモです。

// ピン変数の宣言
// EnumEntryを使いEnumEntryの名前をEnumName属性で指定する
[Input("Audio Device", EnumName = "AudioDevice", IsSingle = true)]
protected ISpread FInAudioDevice;

// 上述したピン変数の初期化処理を行うメソッド
public void OnImportsSatisfied()
{
    // このPCで使用できるデバイスを列挙して配列に格納する
    // RealSense SDKの場合は以下の様に書く
    int deviceNum = this.audioSource.QueryDeviceNum();
    string[] deviceNames = new string[deviceNum];
    for (int i = 0; i < deviceNum; ++i)
    {
        PXCMAudioSource.DeviceInfo tmpDviceInfo;
        sts = this.audioSource.QueryDeviceInfo(i, out tmpDviceInfo);
        if (sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
        {
            throw new Exception("デバイス情報の取得に失敗しました");
        }

        deviceNames[i] = tmpDviceInfo.name;
    }

    // EnumManagerを使ってEnumName属性で指定したEnumEntryを更新する
    EnumManager.UpdateEnum("AudioDevice", deviceNames[0], deviceNames);
}

こちら→tebjan/VVVV.Audioのコードを参考にさせて頂きました。
ありがたい。

終了処理

Dynamic Plugins ReferenceのThe Destructor: Disposing stuffを参照。
IDisposeインタフェースのDispose()メソッドを実装します。
Dispose()メソッドはノードを消したりパッチを閉じた時に1度だけ呼ばれます。
この時にちゃんとRealSense関連のインスタンス(PXC(M)Sessionとか)のリソースを開放する処理をしておかないと挙動がおかしくなります。

スプレッドの数

ISpreadの値を増減させた場合ISpread.SliceCountも増減させなければなりません。
ISpread.SliceCount = 0;とすると出力はΦ(NIL)になります。
またこの状態でISpreadに値を入れようとすると例外が発生してしまいます。
うーん、個人的にはSliceCountは値の数に応じて自動で変動してくれた方が嬉しい気がするんだけど。

ちなみにIDiffSpreadというのもありますがISpreadとどう違うのかいまいちわかっていません、汗

RealSense SDKはまりどころまとめ

ちょっと誰の役に立つかわかりませんがRealSense SDK関連の自分がはまったところをまとめます。

SDK付属のサンプルがなかなか正常起動しない

よく初期化に失敗します。自分のプラグインでは成功するまで毎フレーム初期化を試みるという荒業(?)をしているので放っておけばそのうち成功します。
何が原因なのかなぁ。。不安定です。

使用するモジュールごとにセッションを分けると上手くいく場合がある

RealSense SDKでの開発はまず、
PXC(M)Sessionというオブジェクトを作成します。
このオブジェクトが各モジュール(画像の読み込みモジュール、手の検出モジュール、などなど)を制御します。
HandNodeの実装ではカラー画像を取得するモジュールと手を検出するモジュールを同じセッションオブジェクトで使うと上手く初期化できなかったので、
分けてみたところなんとか動くようになりました。
一応SDKのリファレンスなど目を通しましたがそのような説明は無かったのでこれも謎です。。。

Segmentationが使用できない

RealSenseにはユーザを検出して背景画像だけをマスクするSegmentationという機能が使えると書いていますが
SDK付属のサンプル、自分のプラグイン共にうまくいきません。
とりあえず諦めてます、笑

音声認識にはSDK Runtime Distributable (Optional)が必要

音声認識するには言語毎の設定ファイルが必要でした。
Developer Zoneからダウンロードしてインストールします。
インストール時に使用したい言語を選択します。

おわり

全3回の投稿で教科書に載っているサンプルをざっと実装してみました。
取りあえずRealSenseプラグイン開発ネタは以上。
もう少し開発が進んだらGithubに上げられそうです。

折角だからこれを使って作品を作りたいですが安定性が問題ですね、汗
ではでは。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください