Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
Kinect for Windows v2で徹底的に遊ぶ

Kinect for Windows v2で徹底的に遊ぶ

Face Tracking入門

2014年9月5日

Kinect for Windows Public PreviewとSDKのFace Tracking(顔追跡)機能を使用したプログラミング方法を、.NET開発者向けに解説する。

Microsoft Student Partners 五十嵐 祐貴
  • このエントリーをはてなブックマークに追加

注意事項

“This is preliminary software and/or hardware and APIs are preliminary and subject to change.”
 本連載で紹介する次世代型Kinect for WindowsおよびKinect for Windows SDK 2.0 Public Previewの仕様は暫定的なものであり、ソフトウェア、ハードウェアおよびAPIは製品版で変更される可能性があることをご了承いただきたい。

本連載について

 本連載では、次世代型のKinect for Windows(以下、Kinect v2プレビュー版)と、そのSDKであるKinect for Windows SDK 2.0 Public Preview(以下、SDK2.0プレビュー版)*1のFace Tracking(顔追跡)機能を使用したプログラミング方法を.NET開発者向けに紹介する。

Kinect for Windows SDK 2.0 Public Previewのインストール方法

 2014年8月現在、SDK2.0プレビュー版(2014年8月20日発行版)は「Kinect for Windows公式サイト」(図1.1)にあるリンクを通じてMicrosoftダウンロードセンターからダウンロードできる。

図1.1 Kinect for Windows公式サイト

画面右上部の[Download SDK]、画面上部の[Develop]メニューにある[Download the SDK]、中部にある[Download the latest SDK]のバナーにあるいずれかのリンクからダウンロードセンターへアクセスできる。

 なお、Kinect v2プレビュー版のシステム要求は以下の通りである。特に「USB 3.0がIntelのものとRenesasのチップセットを採用していること」、また「GPUがDirectX 11対応のものであること」が求められていることに注意が必要である。

項目要件
OS Windows 8または Windows 8.1(Embeddedを含む)
CPU 物理デュアルコア 3.1GHz以上
RAM 2GBytes以上
GPU DirectX 11対応のGPU
USB USB 3.0ホストコントローラー(IntelまたはRenesasのチップセット)
開発環境 Visual Studio 2012、Visual Studio 2013(Expressも可)、Unity 4 Pro
表1.1 Kinect v2プレビュー版のシステム要件

 この他にも注意したい事項がフォーラムにまとめられている。Kinect v2プレビュー版が動作しない場合は参照してほしい。

Kinect v2プレビュー版におけるFace Tracking

 Kinect v2プレビュー版は、Kinect v1と比べ、認識精度や解像度が大きく改善されている。またプログラミングのスタイルに関しても、SDK2.0プレビュー版ではさまざまな工夫がなされており、Kinect for Windows SDK v1.x(以下、SDK1)と比べ、手順が大きく変化している。

 Face Trackingにおいては、詳細な分析に対応したHighDefinitionFaceFrameReaderクラスや、顔のモデル作成を行うFaceModelBuilderクラスなどが用意され、通常のFrameReaderと同様に、独立してデータを扱えるようになった。

 今回はFace Trackingに関するサンプルを2つ取り上げ、Face Trackingを用いたプログラミングに関する理解を深めることを目標とする。

基本的なFace Tracking(顔のパーツの座標と表情を取得するサンプル)

サンプルプログラムのダウンロード

サンプルプログラムの概要

 このサンプルプログラム(図2.1)では、Kinectセンサーが人を検知すると、その人の顔に対してFace Trackingを行い、顔のパーツ(右目、左目、鼻、口の左端と右端)の座標を特定し、それらをセンサーから取得したカラー情報上にマーカーで示す。また表情分析も行い結果をリアルタイムに表示する。

図2.1 FaceTrackingBasicの実行例

体が認識されるとFace Trackingが開始され、顔のパーツがそれぞれ異なる色のマーカーで示される。また同時に表情分析も行い、その結果を画面右側に表示している。

 それでは実際にサンプルプログラムのコードを見ていこう。

変数宣言部

 内部的に使用する変数は以下の通りである。Face Trackingに関して特に重要な変数だけを抜粋している。コード中で使われているそれ以外の変数に関しては、その都度、コードに記載されているコメントを参照してほしい。

C#
private KinectSensor kinect;            //……1
private MultiSourceFrameReader reader;  //……2

private FaceFrameSource faceSource;     //……3
private FaceFrameReader faceReader;     //……4

private byte[] colorPixels = null;      //……5
private const FaceFrameFeatures DefaultFaceFrameFeatures = 
    FaceFrameFeatures.PointsInColorSpace | 
    FaceFrameFeatures.Happy | 
    FaceFrameFeatures.FaceEngagement | 
    FaceFrameFeatures.Glasses | 
    FaceFrameFeatures.LeftEyeClosed | 
    FaceFrameFeatures.RightEyeClosed | 
    FaceFrameFeatures.MouthOpen | 
    FaceFrameFeatures.MouthMoved | 
    FaceFrameFeatures.LookingAway | 
    FaceFrameFeatures.RotationOrientation;  //……6
リスト2.1 変数の宣言部分抜粋
  • 1Kinectセンサーとの接続を格納しておくための変数を宣言する。
  • 2Kinectセンサーから複数のセンサーデータを受け取ることができるFrameReaderを宣言する。
  • 3顔情報データの取得元を格納しておくための変数を宣言する。
  • 4顔情報データ用のFrameReaderを宣言する。
  • 5色情報を画面に描画するための一時的なバッファーを宣言する。
  • 6表情分析を行う項目を宣言する。

センサー初期化部分(抜粋)

 続いてセンサーを初期化するコードを見てみよう。

C#
private void Initialize()
{
  this.kinect = KinectSensor.GetDefault();  //……1
  if (this.kinect == null) return;

  var desc = kinect.ColorFrameSource.FrameDescription;  //……2
  this.colorPixels = new byte[desc.Width * desc.Height * bytePerPixel];  //……3
  _ColorBitmap = new WriteableBitmap(desc.Width,
    desc.Height,
    96.0,
    96.0, 
    PixelFormats.Bgr32,
    null);  //……4
  _FacePointBitmap = new RenderTargetBitmap(desc.Width, 
    desc.Height,
    96.0, 
    96.0,
    PixelFormats.Default);  //……5

  this.reader = kinect.OpenMultiSourceFrameReader(FrameSourceTypes.Body | FrameSourceTypes.Color);  //……6
  reader.MultiSourceFrameArrived += OnMultiSourceFrameArrived;  //……7
  this.faceSource = new FaceFrameSource(kinect,0, DefaultFaceFrameFeatures);  //……8
  this.faceReader = faceSource.OpenReader();  //……9
  faceReader.FrameArrived += OnFaceFrameArrived;  //……10
  faceSource.TrackingIdLost += OnTrackingIdLost;  //……11
  kinect.Open();  //……12
}
リスト2.2 Kinectセンサーの初期化部分
  • 1接続されているKinectセンサーを取得する。
  • 2Kinectセンサーの色情報に関するフレーム情報(サイズや解像度など)を取得する。
  • 345Kinectセンサーのフレーム情報に合わせて、各種表示用の変数を初期化する。
  • 6Kinectセンサーから取得したいデータを指定し、それらのFrameReaderを初期化する(今回は骨格情報と色情報を指定している)。
  • 7Kinectセンサーからフレームが発生した際の処理を登録する。
  • 8トラッキング対象を0として、FaceFrameSourceを初期化する。
  • 9FaceFrameSourceから情報を読み取るFrameReaderを取得する。
  • 10KinectセンサーからFace Trackingに関するフレームが発生した際の処理を登録する。
  • 11Face Trackingを行う対象をロストした際の処理を登録する。
  • 12Kinectセンサーの読み取りを開始する。

 変数faceSourcefaceReaderの初期化を行っているが、この時点ではFace Trackingの対象を設定しておらず、このままの状態ではFace Trackingは正常に機能しない。これらの変数に対して設定を行うのは、Kinectセンサーから骨格・色データに関するフレームが発生した時の処理部分である。

骨格・色データ処理部分

C#
private void OnMultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
{
  var frame = e.FrameReference.AcquireFrame();
  if (frame == null) return;

  using (var bodyFrame = frame.BodyFrameReference.AcquireFrame())  //……1
  {
    if (bodyFrame != null)
    {
      if (bodies == null)
        bodies = new Body[bodyFrame.BodyCount];

      bodyFrame.GetAndRefreshBodyData(bodies);  //……2
      if (!this.faceSource.IsTrackingIdValid)   //……3
      {
        var target = (from body in this.bodies where body.IsTracked select body).FirstOrDefault(); //……4
        if (target != null)
          this.faceSource.TrackingId = target.TrackingId;  //……5
      }
    }
  }
……後略……
リスト2.3 骨格情報、色情報の処理部分
  • 1骨格情報に関するフレームを取得する。
  • 2骨格情報を取得し、骨格データを変数bodiesに格納する。
  • 3Face Trackingが正常に行われているか確認する(Face Trackingが対象を認識し、正常に行われている場合、faceSourceIsTrackingIdValidプロパティはtrueを返す)。
  • 4取得した骨格データからトラッキングに成功しているものを1つ選択する。
  • 5選択した骨格を、その顔情報の取得元として登録する。

 ここでは骨格情報と色情報を取得すると同時に、取得できた骨格に対してfaceSourceおよびfaceReaderを初期化し、Face Trackingを開始している。

顔情報の処理部分

 次は顔情報の処理に関するコードである。

C#
private void OnFaceFrameArrived(object sender, FaceFrameArrivedEventArgs e)
{
  using (var faceFrame = e.FrameReference.AcquireFrame())
  {
    if (faceFrame == null) return;

    var result = faceFrame.FaceFrameResult;  //……1
    if (result == null) return;

    this.Happy = result.FaceProperties[FaceProperty.Happy].ToString();  //……2
    this.FaceEngagement = result.FaceProperties[FaceProperty.Engaged].ToString();
    ……中略……
    this.FaceRotation = result.FaceRotationQuaternion;  //……3

    var drawContext = drawVisual.RenderOpen();
    foreach (var point in result.FacePointsInColorSpace)
    {
      if (point.Key == FacePointType.None) continue;
      drawContext.DrawEllipse(facePointColor[(int)point.Key], null, new Point(point.Value.X, point.Value.Y), 5, 5);  //……4
    }
    drawContext.Close();
    _FacePointBitmap.Clear();  //……5
    _FacePointBitmap.Render(drawVisual);  //……6
    ……後略……
リスト2.4 顔情報の処理部分
  • 1顔情報のフレームから顔に関する分析結果を取得する。
  • 2各種表情分析の結果を取得し、表示用の値を更新する。
  • 3顔に関する分析結果から、顔の回転情報を取得する。
  • 4顔のパーツの座標を描画する。
  • 5表示用のRenderTargetBitmapを初期化する。
  • 6表示用のRenderTargetBitmapへ描画する。

 変数FaceFrameFeaturesに指定した項目に関して、ターゲットの表情をYesMaybeNoUnknownのいずれかの値で取得可能である。

 以上でコードは完成だが、実際に実行すると表情分析の項目が“No”としかならない。これは表情分析を行うためのデータベースがないためである。そこで出力されるバイナリと同じフォルダーに、表情分析を含む、各種分析を行うためのデータベースであるNUIDatabaseをコピーする操作が必要となる。また現状では64bitのみでしか動作しないため、ターゲットプラットフォームの項目を「x64」に忘れずに設定することにも注意が必要である。

NUIDatabaseのコピー

 さまざまな方法があるが、ここではビルド完了後のアクションに追加する方法を紹介する。

 以下の手順に従って、ビルド完了後のアクションとして以下のコマンドを実行しよう。

コンソール
xcopy "$(KINECTSDK20_DIR)Redist\Face\$(Platform)\NuiDatabase" "$(TargetDir)\NuiDatabase" /S /R /Y /I
ビルド完了後のアクション
C#の場合

 C#の場合、[ソリューション エクスプローラー]内のプロジェクト項目を右クリックし、コンテキストメニューから[プロパティ]を選択しよう。プロジェクトに関するウィンドウが表示されたら、左側の[ビルド イベント]タブを選択すると[ビルド後に実行するコマンドライン]テキストボックスがある。このテキストボックスに上記のアクションを入力する。

図2.2 ビルドイベント入力方法(C#)
  • 1 [ソリューション エクスプローラー]内のプロジェクト項目を右クリックする。
  • 2 コンテキストメニューにある[プロパティ]をクリックする。
  • 3 [ビルド イベント]タブを選択する。
  • 4 [ビルド後に実行するコマンドライン]に上記ビルド完了後のアクションを入力する。

 また、[ビルド]タブに[プラットフォーム ターゲット]コンボボックスがあるため、その値を「x64」に設定する。

Visual Basic(VB)の場合

 VBの場合、[ソリューション エクスプローラー]内のプロジェクト項目を右クリックし、コンテキストメニューから[プロパティ]を選択しよう。プロジェクトに関するウィンドウが表示されたら、左側の[コンパイル]タブから[ビルドイベント]ボタンをクリックすると、[ビルド イベント]ウィンドウが表示される。このウィンドウの[ビルド後に実行するコマンドライン]テキストボックスに上記のアクションを入力する。

図2.3 ビルドイベント入力方法(VB)
  • 1 [ソリューション エクスプローラー]内のプロジェクト項目を右クリックし、コンテキストメニューにある[プロパティ]をクリックする。
  • 2 [コンパイル]タブを選択する。
  • 3 [ビルド イベント]ボタンをクリックする。
  • 4 [ビルド後に実行するコマンドライン]に上記ビルド完了後のアクションを入力する。

 また、[コンパイル]タブに[対象のCPU]コンボボックスがあるため、その値を「x64」に設定する。

より詳細なFace Tracking(顔の特徴点とモデルを構築するサンプル)

サンプルプログラムのダウンロード

サンプルプログラムの概要

 このサンプルプログラム(図3.1)では、Kinectセンサーが人を検知すると、その人の顔に対してより詳細なFace Tracking(以下、HDFaceTracking)を行い、顔の各部位のゆがみを検出し、モデルに反映させる。

図3.1 HDFaceTrackingの実行例

体が認識されるとHDFaceTrackingが開始され、顔のゆがみを反映させた3Dモデルが表示される。

 それでは実際にサンプルプログラムのコードを見ていこう。

変数宣言部

 基本的な流れは先ほどのサンプルと同様である。ここでは今回重要となる変数のみを抜粋する。

C#
private FaceAlignment faceAlignment = null; //……1
private FaceModel faceModel = null; //……2
private HighDefinitionFaceFrameSource hdFaceFrameSource = null; //……3
private HighDefinitionFaceFrameReader hdFaceFrameReader = null; //……4
private FaceModelBuilder faceModelBuilder = null; //……5
リスト3.1 変数の宣言部分抜粋
  • 12センサーから取得した顔情報に関するデータを格納しておくための変数を宣言する。
  • 3顔情報データの取得元を格納しておくための変数を宣言する。
  • 4顔情報データ用のFrameReaderを宣言する。
  • 5顔のモデルの作成を管理するための変数を宣言する。

センサー初期化部分(抜粋)

 続いてセンサーを初期化するコードを見てみよう。

C#
private void Initialize()
{
  this.hdFaceFrameSource = new HighDefinitionFaceFrameSource(this.kinect);  //……1
  this.hdFaceFrameSource.TrackingIdLost += this.OnTrackingIdLost;  //……2
  
  this.hdFaceFrameReader = this.hdFaceFrameSource.OpenReader();    //……3
  this.hdFaceFrameReader.FrameArrived += this.OnFaceFrameArrived;  //……4
  ……後略……
リスト3.2 センサーを初期化するコード
  • 1顔情報データの取得元を格納しておくための変数を宣言する。
  • 2HDFaceTrackingのターゲットをロストした時の処理を登録する。
  • 3顔情報データ用のFrameReaderを宣言する。
  • 4Kinectセンサーからフレームが発生した際の処理を登録する。

 FaceTrackingBasicのコードと同様、この時点ではHDFaceTrackingのターゲットを指定しておらず、正常に動作しない。このターゲットを設定するのも、同様にKinectセンサーから骨格データに関するフレームが発生した時の処理部分である。

骨格データ処理部分

C#
if (!this.hdFaceFrameSource.IsTrackingIdValid)
{
  var target = (from body in this.bodies where body.IsTracked select body).FirstOrDefault();  //……1
  if (target != null)
  {
    hdFaceFrameSource.TrackingId = target.TrackingId;  //……2
    this.faceModelBuilder = this.hdFaceFrameSource.OpenModelBuilder(
        FaceModelBuilderAttributes.HairColor | FaceModelBuilderAttributes.SkinColor);  //……3
    this.faceModelBuilder.CollectionCompleted += this.OnModelBuilderCollectionCompleted;  //……4
    this.faceModelBuilder.BeginFaceDataCollection();  //……5
  }
}
……後略……
リスト3.3 変数の宣言部分抜粋
  • 1取得した骨格データからトラッキングに成功しているものを1つ選択する。
  • 2選択した骨格を、その顔情報の取得元として登録する。
  • 3SkinColor(肌の色)やHairColor(髪の色)も分析項目として含めた、FaceModelBuilderを開始する。
  • 4FaceModelBuilderがFaceModelに関するデータの収集を終えた際に発生する処理を登録する。
  • 5FaceModelBuilderの収集処理を開始する。

 最後に、Kinectセンサーから取得した顔情報を処理する部分を見ていく。

顔情報の処理部分

C#
private void OnFaceFrameArrived(object sender, HighDefinitionFaceFrameArrivedEventArgs e)
{
  if (isCaptureCompleted) return;

  using (var faceFrame = e.FrameReference.AcquireFrame())
  {
    if (faceFrame == null) return;

    faceFrame.GetAndRefreshFaceAlignmentResult(this.faceAlignment);  //……1
    UpdateMesh();  //……2
    ……後略……
  }
}
リスト3.4 顔情報の処理部分
  • 1顔情報のフレームからFaceAlignmentを分析し、更新する。
  • 2FaceAlignmentに格納された情報を用いて描画を更新する。

 FaceModelBuilderクラスでは、GetCollectionStatus()関数からモデルを完成させるために必要な顔の部分、GetCaptureStatus()関数から現在のセンサーからの取得状況(MovingTooFastOtherViewsNeededGoodFrameCaptureなど)を取得できる(リスト3.5)。その値を表示することにより、利用者の顔のモデル収集状況を把握でき、より効率的に顔情報の処理が行える。

 この値の変更は、これらの変更を通知するイベントを購読することで知ることができる。

C#
private void faceModelBuilder_CollectionStatusChanged(object sender, FaceModelBuilderCollectionStatusChangedEventArgs e)
{
  this.FaceModelBuilderStatus = GetCollectionStatus(this.faceModelBuilder.CollectionStatus);  //……1
}

private void faceModelBuilder_CaptureStatusChanged(object sender, FaceModelBuilderCaptureStatusChangedEventArgs e)
{
  this.FaceModelCaptureStatus = this.faceModelBuilder.CaptureStatus.ToString();  //……2
}
リスト3.5 顔情報の処理部分
  • 1顔に関するモデルデータの収集状況を更新する。
  • 2顔に関するモデルデータの取得状況を更新する。

 その他、描画の更新部分などに関する説明は、コードに記載されたコメントを参照してほしい。

活用例

 これまでに紹介した機能だけでも、さまざまな応用が考えられるであろう。実際に筆者が作成したサンプルを以下で紹介したい。

絶対に負ける!あっちむいてほいゲーム

図4.1 実行例

コンピューターはプレーヤーが向いた方向と必ず同じ向きに指を向ける。

 このゲームは、Face Tracking機能を用いて顔の向きを判定している。コードを見ると、複雑なプログラミングは一切行っておらず、このような情報を非常に簡単に利用できることがよく分かる。

 また、Hand Trackingを用いたじゃんけん機能の追加や、自分の体をキャラクターにマッピングさせ、よりリアルなじゃんけんゲームを行うなど、Kinectの機能を組み合わせるだけでも、さまざまな改良ができるであろう。

ベストショット撮影ツール

図4.2 実行例

プレーヤーだけを抽出し、リアルタイムに指定された画像上に合成する。またプレーヤーの状態が最適と判断された時点でスクリーンショットを撮影し、ピクチャフォルダーに保存する。

 このプログラムでは、Hand Trackingによるピースの判定と、Face Tracking機能を用いて表情分析を行い、ベストと思われる状態(どちらかの手がピースを行い、かつ笑っていると判定できる状態)にスクリーンショットを行い、画像を保存する。またDepthデータを組み合わせることでリアルタイムに人物を抽出し、指定した画像にマッピングを行っている。今回紹介したHDFaceTrackingを行うことで、さらに詳細な判定や合成を行うことも改善点として考えられる。

終わりに

 今回は、Face Trackingに焦点を当て、サンプルプログラムを用いながら、その使い方を学んだ。

 現在のSDK2.0プレビュー版は開発途中であり、特にFace Trackingに関しては機能の実装途中でもある(そのため定義されている機能の一部は動作しない場合がある)。しかしながら、現時点でも非常に多くの情報を手軽に取得でき、その性能・可能性を十分に体験できるであろう。

 今後もKinect SDKはアップデートされ、Face Trackingに関する機能も日々改善されていくものと思われる。今後の機能追加と性能向上に期待すると同時に、より多くの開発者が、センサーを用いたプログラミングが気軽に行えるようになることを期待している。今回紹介したFace Tracking機能を最大限に活用していってほしい。

サイトからのお知らせ

Twitterでつぶやこう!