やってみよう!Kinectアプリ開発 - 第13回 FaceTracking(後編)

2012-08-24 18:37

zigsow 長谷川 勇
 

はじめに

前回は、Kinect™ for Windows® SDK同梱のFace Tracking SDKを使った顔認識の基本について説明しました。今回は引き続き、Face Tracking SDKの他の機能として、画像情報からの顔の検出方法を紹介します。

DetectFacesをC#から利用する

連載第12回で紹介した通り、Face Tracking SDKには画面内の人物の顔を検出するAPI(以下DetectFaces APIと呼びます)があります。ただし、これはC++から利用するAPIのため、C#からは利用できません。C#からFace Tracking SDKを利用する場合は、連載第12回で紹介した、Microsoft.Kinect.Toolkit.FaceTrackingというコンポーネントを利用することになりますが、残念ながらこのコンポーネントもDetectFaces APIに対応するインタフェースを提供していないようです。

アプリケーションを開発する際にはこういった問題に遭遇することが多々あります。ここでは解決方法の一例として、DetectFaces APIをC#から利用するための調査・作業手順を紹介します。

※ ここで紹介する手順は、Kinect for Windows SDK v.1.5.0で行ったものです。今後のバージョンでは異なる可能性もあります(あるいは何もせずにC#から使えるようになっているかもしれません)。

Microsoft.Kinect.Toolkit.FaceTrackingの調査

まずは、Microsoft.Kinect.Toolkit.FaceTrackingコンポーネントでDetectFaces APIに相当する機能を提供していないか、あるいは使っていないかを調べてみます。これは、このコンポーネントのディレクトリ以下で”DetectFaces”という文字列を検索すればよいでしょう。すると、FtInterop.cs内で以下が見つかります。

$ grep -r DetectFaces *
FtInterop.cs:        /// STDMETHOD(DetectFaces)(THIS_ const FaceTrackingSensorData* pSensorData, const RECT* pRoi, FT_WEIGHTED_RECT* pFaces, UINT* pFaceCount) PURE;
FtInterop.cs:        int DetectFaces(ref FaceTrackingSensorData sensorData, ref Rect roi, IntPtr faces, ref uint facesCount);

見つかったソースの周辺を確認することで、IFTFaceTrackerインタフェースにDetectFacesメソッドがあることが分かります。これを呼べばDetectFaces APIを利用できそうです。

※ FtInterop.csで定義されているIFTFaceTrackerインタフェースや、DetectFacesメソッドは、COMコンポーネントをマネージコードから使うためのラッパーです。ここでは、これらを呼び出すとC++用のAPIの対応するIFTFaceTrackerインタフェースや、DetectFacesメソッドが呼び出されると理解しておけばよいでしょう。興味がある方はMSDNの資料(http://msdn.microsoft.com/en-us/library/aa645736.aspx)もご覧ください。

次に、IFTFaceTrackerオブジェクトを取得する方法を調べましょう。こちらもまずは” IFTFaceTracker”という文字列を検索してみます。

$ grep -r IFTFaceTracker *
FaceTracker.cs:        private IFTFaceTracker faceTrackerInteropPtr;
FaceTracker.cs:        internal IFTFaceTracker FaceTrackerPtr
FaceTracker.cs:        /// Resets IFTFaceTracker instance to the clean state (like it is in right after the call
FtInterop.cs:    /// IFTFaceTracker is the main interface used for face tracking. An IFTFaceTracking object is created by using the FTCreateFaceTracker Function.
FtInterop.cs:    internal interface IFTFaceTracker
FtInterop.cs:        /// Initializes an IFTFaceTracker instance.
FtInterop.cs:        /// Resets the IFTFaceTracker instance to a clean state (the same state that exists after calling the Initialize() method).
FtInterop.cs:        public static extern IFTFaceTracker FTCreateFaceTracker(IntPtr reserved);

見つかったソースの周辺を確認してIFTFaceTrackerオブジェクトを取得するには、以下2つの方法があることが分かります。

  1. FaceTrackerオブジェクトのメンバ変数faceTrackerInteropPtrを利用する。
  2. NativeMethodsクラスのクラスメソッドFTCreateFaceTrackerを呼ぶ。

どちらかの方法でIFTFaceTrackerオブジェクトを取得し、それのDetectFacesメソッドを呼べばよいでしょう。

IFTFaceTrackerインタフェースDetectFacesメソッドの引数の調査

DetectFacesメソッドを呼ぶ方法は分かりましたので、渡す引数を調べましょう。DetectFacesメソッドには以下の4つの引数があります。

  1. ref FaceTrackingSensorData sensorData
  2. ref Rect roi
  3. IntPtr faces
  4. ref uint facesCount

これらは、DetectFaces APIの以下4つの引数に対応しているはずです。(http://msdn.microsoft.com/en-us/library/microsoft.kinect.facetracking.iftfacetracker.detectfaces 参照)

  1. pSensorData
    • Type: FT_SENSOR_DATA
    • Input from the video camera and depth sensor (currently, depth input is ignored).
  2. pRoi
    • Type: RECT
    • Optional, NULL if not provided. Region of interest in the video frame where the detector must look for faces. If NULL, the detector uses the full frame.
  3. pFaces
    • Type: FT_WEIGHTED_RECT
    • Returned array of weighted face rectangles (where weight is a detection confidence level).
  4. pFaceCount
    • Type: UINT
    • On input, it must have a size of the pFaces array. On output, it contains the number of faces detected and returned in pFaces.

以下でこれらの引数として何を渡せばよいかを順に説明します。

第1引数 ref FaceTrackingSensorData sensorData

第一引数の型FaceTrackingSensorDataはMicrosoft.Kinect.Toolkit.FaceTrackingコンポーネントで定義する独自の型です。この場合一番簡単なのは、類似のAPIを使っているところを真似することです。

FtInterop.csから、FaceTrackingSensorData構造体を使っている他のAPIを探すと、StartTrackingとContinueTrackingで使っていることが分かります。

…
 [PreserveSig]
int StartTracking(ref FaceTrackingSensorData sensorData, ref Rect roi, HeadPoints headPoints, IFTResult faceTrackResult);
…
 [PreserveSig]
int ContinueTracking(ref FaceTrackingSensorData sensorData, HeadPoints headPoints, IFTResult faceTrackResult);
…

さらに、FaceTracker.csからStartTracking/ContinueTrackingを使っているところを探すと以下が見つかります。

…
var sensorData = new SensorData(this.colorFaceTrackingImage, this.depthFaceTrackingImage, DefaultZoomFactor, Point.Empty);
FaceTrackingSensorData faceTrackSensorData = sensorData.FaceTrackingSensorData;
…
hr = this.faceTrackerInteropPtr.ContinueTracking(ref faceTrackSensorData, headPointsObj, this.frame.ResultPtr);
…

このコード片を利用することで、第1引数として必要なデータを作成できそうです。

第2引数 ref Rect roi

第2引数に関しても第1引数と同様の方法で調べることもできますが、Rect構造体の定義(Utils.csにあります)を見ると以下のコンストラクタが見つかります。

public Rect(int left, int top, int right, int bottom) : this()
…

このコンストラクタは利用するのも簡単ですし、意味も明確なのでそのまま使えそうです。

第3, 4引数 IntPtr faces, ref uint facesCount

第3, 4引数は、DetectFacesから顔検出の処理結果であるデータを受け取るためのポインタ(C#ではこのためにIntPtrという型を用意しています)と受け取るデータの数を格納する変数を渡します。このため、第3, 4引数として必要な処理は「データを受け取るための領域確保」と「受け取ったデータをC#で扱える構造体にコピー」の2点になります。なお、このIntPtrを使ったアンマネージコードからのデータの受け取り時の処理概要は以下の通りです。

  1. Marshal.AllocHGlobalで、データを受け取る領域を確保する
  2. 対象の関数(ここではDetectFaces)に1で確保した領域を指すポインタと、領域のサイズを渡す
  3. 2で渡した引数に、処理結果のデータとその数が入っているので、それをC#の構造体にコピーする(マーシャリング)

※ これらはこのAPI特有の処理ではなくC#一般の話題なので、興味のある方はMSDNのガイド(http://msdn.microsoft.com/ja-jp/library/eadtsekz.aspxなど)もご覧ください。

ここで問題になるのが、ポインタで指すデータの型です。この型は1のサイズ計算や、3の構造体に必要になります。APIのドキュメントを見ると、C++ではFT_WEIGHTED_RECTという構造体を使っているようです。しかし、Microsoft.Kinect.Toolkit.FaceTrackingコンポーネント内にこれに類する構造体はありません。

この場合は、C++ではどうなっているかを見てみましょう。Developer Toolkit v1.5.0 のディレクトリ以下のinc\FaceTrackLib.hを見てみると、以下の定義が見つかります。

struct FT_WEIGHTED_RECT
{
    FLOAT Weight;   // some weight 
    RECT  Rect;     // rectangle coordinates as RECT structure
};

これと同等の構造体をC#で定義すればよいでしょう。

 

 

 

 

4件の質問スレッド
  • ハンドルされていない例外が発生 tokiko 2013-07-05 11:29 いつもお世話になっています。
    いまさらながら、このサイトでKinectについて勉強させていただき、やっと最後のここまでたどり着くことができました。

    ところがこの回まで来て、うまくいかず行き詰っています。
    ビルトして実行し、”顔検出”のボタンをクリックすると、以下のようなエラーが出ます。

    --------------------------------------------------------------------------
    'System.InvalidCastException' のハンドルされていない例外が Microsoft.Kinect.dll で発生しました。

    追加情報: 型 'System.__ComObject' の COM オブジェクトをインターフェイス型 'Microsoft.Kinect.Interop.INuiFrameTexture' にキャストできません。IID '{13EA17F5-FF2E-4670-9EE5-1297A6E880D1}' が指定されたインターフェイスの COM コンポーネント上での QueryInterface 呼び出しのときに次のエラーが発生したため、この操作に失敗しました: インターフェイスがサポートされていません (HRESULT からの例外: 0x80004002 (E_NOINTERFACE))。
    ---------------------------------------------------------------------------

    MainWindow.xaml.csのbutton1_Clickの中の、
    DepthImageFrame depthFrame
    = kinectChooser.Kinect.DepthStream.OpenNextFrame(1000);

    のところで引っかかっているようです。
    解決方法があればご教示いただければ幸いです。

    ちなみに、Kinect for SDKのバージョンは1.7です。
    ヘルプファイルでは、DepthImageFrame Membersについては、1.5、1.6、1.7共通で書かれているので、違うところに
    原因があるのかな、と考えています。

    よろしくお願いいたします。
    1件のコメント
    • drksugi 私もこの問題で困っています. スレッド絡みの問題で厄介です. 有効な解決法がなく本当に困ります. 2014-09-22 20:09
  • 画面内にボタンで出てこない 某支配人 2012-08-28 23:32 ソースコードの修正(MainWindow.xaml)
    MainWindow.xamlの修正はボタンの追加のみです。


    において、コピペしてもボタンが表示されません。
    1件のコメント
    • kinection.jp管理人 ちょっと見てみないとわからないのですが、例えばVisual C#上で、MainWindow.xamlを開き[デザイン]ビュー上では表示されているでしょうか?表示されていない場合、XAMLソース上で"Button"タグをクリックしてプロパティは表示されているでしょうか?プロパティが見える場合は画面外に出ている可能性もあるので、HorizontalAlignmentとVerticalAlignmentを確認してみてください。 2012-08-29 15:45
  • ソースへの追加場所 某支配人 2012-08-24 23:56 第13回講義に於いて
    FaceTracker.cs (追加部分のみ)のソースへの追加場所がよく解りません。

    {}の数も合わないためにいまいち推測がつきません。

    もしかすると最後の}を2つ消してから、
    今回のソースを全部追加するのでしょうか?

    お手数ですがお教え願います。

    3件のコメント (すべて表示)
    • kinection.jp管理人 ご指摘ありがとうございます。分かりにくかったようで申し訳ありません。
      説明を追加しておきましたが、"}"を1つ削除してソースを貼り付けでいけるかと思いますがいかがでしょうか。
      2012-08-29 15:33
    • 某支配人 自己完結は勘違いでした。
      申し訳ありません。

      とりあえずFaceTracker.cs については問題はなさそうなのですが、正式な回答をいただきたいです。

      また2点、動作がおかしいので別途質問スレッドを建てます。
      2012-08-28 22:29
    • 某支配人 自己完結しました。

      FaceTracker.cs の追加部分は最後の}}を消して、
      掲載されているソースを全部追加で問題なく動作しました。

      私の確認時には}の消し方を間違えていたようでエラーが出まくりでしたが、再度元ソースをいじったら通りました。
      2012-08-27 11:07
  • コンテキストが無い 某支配人 2012-08-28 23:37 MainWindow.xaml.cs
    にて
    名前'InitializeComponent'は現在のコンテキスト内に存在しません。
    名前'rgbImage'は現在のコンテキスト内に存在しません。

    ともでます。


    namespace FaceTrackingSample_01


    この末尾_01を消すとエラーは出なくなります。

    前回のソースの流用ってことですが、フォルダ名の変更指示等が無いため、ネームスペースの方の_01が余分かと思います。

    立て続けに質問して申し訳ありませんが、なにとぞ回答のほどよろしくお願いします。
    1件のコメント
    • kinection.jp管理人 ご指摘ありがとうございます。"_01"は余分です。
      ソースの方を修正しました。
      2012-08-29 15:19

ジグソープレミアムレビュー

  • やってみようKinect(キネクト)アプリ開発 - ラボクルー集まれ!