Kinect for Windows v2で徹底的に遊ぶ
Face Tracking入門
Kinect for Windows Public PreviewとSDKのFace Tracking(顔追跡)機能を使用したプログラミング方法を、.NET開発者向けに解説する。
注意事項
本連載で紹介する次世代型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開発者向けに紹介する。
- *1 Build Insiderの他の連載では、C++開発者/.NET開発者向けにKinect v2プレビュー版の全体像に関して情報を発信している。現行版のKinect for Windows(以下、Kinect v1)との比較や各種機能のプログラミングに関する情報は、「新型Kinect for Windows v2 Public Preview/Developer Previewプログラミング入門」および「Kinect for Windows v2 Developer Preview入門 ― C++プログラマー向け連載」を併せて参照してほしい。
Kinect for Windows SDK 2.0 Public Previewのインストール方法
2014年8月現在、SDK2.0プレビュー版(2014年8月20日発行版)は「Kinect for Windows公式サイト」(図1.1)にあるリンクを通じてMicrosoftダウンロードセンターからダウンロードできる。
画面右上部の[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 |
この他にも注意したい事項がフォーラムにまとめられている。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を行い、顔のパーツ(右目、左目、鼻、口の左端と右端)の座標を特定し、それらをセンサーから取得したカラー情報上にマーカーで示す。また表情分析も行い結果をリアルタイムに表示する。
体が認識されるとFace Trackingが開始され、顔のパーツがそれぞれ異なる色のマーカーで示される。また同時に表情分析も行い、その結果を画面右側に表示している。
それでは実際にサンプルプログラムのコードを見ていこう。
変数宣言部
内部的に使用する変数は以下の通りである。Face Trackingに関して特に重要な変数だけを抜粋している。コード中で使われているそれ以外の変数に関しては、その都度、コードに記載されているコメントを参照してほしい。
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
|
- 1Kinectセンサーとの接続を格納しておくための変数を宣言する。
- 2Kinectセンサーから複数のセンサーデータを受け取ることができるFrameReaderを宣言する。
- 3顔情報データの取得元を格納しておくための変数を宣言する。
- 4顔情報データ用のFrameReaderを宣言する。
- 5色情報を画面に描画するための一時的なバッファーを宣言する。
- 6表情分析を行う項目を宣言する。
センサー初期化部分(抜粋)
続いてセンサーを初期化するコードを見てみよう。
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
}
|
- 1接続されているKinectセンサーを取得する。
- 2Kinectセンサーの色情報に関するフレーム情報(サイズや解像度など)を取得する。
- 345Kinectセンサーのフレーム情報に合わせて、各種表示用の変数を初期化する。
- 6Kinectセンサーから取得したいデータを指定し、それらのFrameReaderを初期化する(今回は骨格情報と色情報を指定している)。
- 7Kinectセンサーからフレームが発生した際の処理を登録する。
- 8トラッキング対象を0として、FaceFrameSourceを初期化する。
- 9FaceFrameSourceから情報を読み取るFrameReaderを取得する。
- 10KinectセンサーからFace Trackingに関するフレームが発生した際の処理を登録する。
- 11Face Trackingを行う対象をロストした際の処理を登録する。
- 12Kinectセンサーの読み取りを開始する。
変数faceSource
、faceReader
の初期化を行っているが、この時点ではFace Trackingの対象を設定しておらず、このままの状態ではFace Trackingは正常に機能しない。これらの変数に対して設定を行うのは、Kinectセンサーから骨格・色データに関するフレームが発生した時の処理部分である。
骨格・色データ処理部分
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
}
}
}
……後略……
|
- 1骨格情報に関するフレームを取得する。
- 2骨格情報を取得し、骨格データを変数
bodies
に格納する。 - 3Face Trackingが正常に行われているか確認する(Face Trackingが対象を認識し、正常に行われている場合、
faceSource
のIsTrackingIdValid
プロパティはtrue
を返す)。 - 4取得した骨格データからトラッキングに成功しているものを1つ選択する。
- 5選択した骨格を、その顔情報の取得元として登録する。
ここでは骨格情報と色情報を取得すると同時に、取得できた骨格に対してfaceSource
およびfaceReader
を初期化し、Face Trackingを開始している。
顔情報の処理部分
次は顔情報の処理に関するコードである。
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
……後略……
|
- 1顔情報のフレームから顔に関する分析結果を取得する。
- 2各種表情分析の結果を取得し、表示用の値を更新する。
- 3顔に関する分析結果から、顔の回転情報を取得する。
- 4顔のパーツの座標を描画する。
- 5表示用のRenderTargetBitmapを初期化する。
- 6表示用のRenderTargetBitmapへ描画する。
変数FaceFrameFeatures
に指定した項目に関して、ターゲットの表情をYes
/Maybe
/No
/Unknown
のいずれかの値で取得可能である。
以上でコードは完成だが、実際に実行すると表情分析の項目が“No”としかならない。これは表情分析を行うためのデータベースがないためである。そこで出力されるバイナリと同じフォルダーに、表情分析を含む、各種分析を行うためのデータベースであるNUIDatabaseをコピーする操作が必要となる。また現状では64bitのみでしか動作しないため、ターゲットプラットフォームの項目を「x64」に忘れずに設定することにも注意が必要である。
NUIDatabaseのコピー
さまざまな方法があるが、ここではビルド完了後のアクションに追加する方法を紹介する。
以下の手順に従って、ビルド完了後のアクションとして以下のコマンドを実行しよう。
xcopy "$(KINECTSDK20_DIR)Redist\Face\$(Platform)\NuiDatabase" "$(TargetDir)\NuiDatabase" /S /R /Y /I
|
C#の場合
C#の場合、[ソリューション エクスプローラー]内のプロジェクト項目を右クリックし、コンテキストメニューから[プロパティ]を選択しよう。プロジェクトに関するウィンドウが表示されたら、左側の[ビルド イベント]タブを選択すると[ビルド後に実行するコマンドライン]テキストボックスがある。このテキストボックスに上記のアクションを入力する。
- 1 [ソリューション エクスプローラー]内のプロジェクト項目を右クリックする。
- 2 コンテキストメニューにある[プロパティ]をクリックする。
- 3 [ビルド イベント]タブを選択する。
- 4 [ビルド後に実行するコマンドライン]に上記ビルド完了後のアクションを入力する。
また、[ビルド]タブに[プラットフォーム ターゲット]コンボボックスがあるため、その値を「x64」に設定する。
Visual Basic(VB)の場合
VBの場合、[ソリューション エクスプローラー]内のプロジェクト項目を右クリックし、コンテキストメニューから[プロパティ]を選択しよう。プロジェクトに関するウィンドウが表示されたら、左側の[コンパイル]タブから[ビルドイベント]ボタンをクリックすると、[ビルド イベント]ウィンドウが表示される。このウィンドウの[ビルド後に実行するコマンドライン]テキストボックスに上記のアクションを入力する。
- 1 [ソリューション エクスプローラー]内のプロジェクト項目を右クリックし、コンテキストメニューにある[プロパティ]をクリックする。
- 2 [コンパイル]タブを選択する。
- 3 [ビルド イベント]ボタンをクリックする。
- 4 [ビルド後に実行するコマンドライン]に上記ビルド完了後のアクションを入力する。
また、[コンパイル]タブに[対象のCPU]コンボボックスがあるため、その値を「x64」に設定する。
より詳細なFace Tracking(顔の特徴点とモデルを構築するサンプル)
サンプルプログラムのダウンロード
サンプルプログラムの概要
このサンプルプログラム(図3.1)では、Kinectセンサーが人を検知すると、その人の顔に対してより詳細なFace Tracking(以下、HDFaceTracking)を行い、顔の各部位のゆがみを検出し、モデルに反映させる。
それでは実際にサンプルプログラムのコードを見ていこう。
変数宣言部
基本的な流れは先ほどのサンプルと同様である。ここでは今回重要となる変数のみを抜粋する。
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
|
- 12センサーから取得した顔情報に関するデータを格納しておくための変数を宣言する。
- 3顔情報データの取得元を格納しておくための変数を宣言する。
- 4顔情報データ用のFrameReaderを宣言する。
- 5顔のモデルの作成を管理するための変数を宣言する。
センサー初期化部分(抜粋)
続いてセンサーを初期化するコードを見てみよう。
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
……後略……
|
- 1顔情報データの取得元を格納しておくための変数を宣言する。
- 2HDFaceTrackingのターゲットをロストした時の処理を登録する。
- 3顔情報データ用のFrameReaderを宣言する。
- 4Kinectセンサーからフレームが発生した際の処理を登録する。
FaceTrackingBasicのコードと同様、この時点ではHDFaceTrackingのターゲットを指定しておらず、正常に動作しない。このターゲットを設定するのも、同様にKinectセンサーから骨格データに関するフレームが発生した時の処理部分である。
骨格データ処理部分
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
}
}
……後略……
|
- 1取得した骨格データからトラッキングに成功しているものを1つ選択する。
- 2選択した骨格を、その顔情報の取得元として登録する。
- 3
SkinColor
(肌の色)やHairColor
(髪の色)も分析項目として含めた、FaceModelBuilderを開始する。 - 4FaceModelBuilderがFaceModelに関するデータの収集を終えた際に発生する処理を登録する。
- 5FaceModelBuilderの収集処理を開始する。
最後に、Kinectセンサーから取得した顔情報を処理する部分を見ていく。
顔情報の処理部分
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
……後略……
}
}
|
- 1顔情報のフレームからFaceAlignmentを分析し、更新する。
- 2FaceAlignmentに格納された情報を用いて描画を更新する。
FaceModelBuilder
クラスでは、GetCollectionStatus()
関数からモデルを完成させるために必要な顔の部分、GetCaptureStatus()
関数から現在のセンサーからの取得状況(MovingTooFast
やOtherViewsNeeded
、GoodFrameCapture
など)を取得できる(リスト3.5)。その値を表示することにより、利用者の顔のモデル収集状況を把握でき、より効率的に顔情報の処理が行える。
この値の変更は、これらの変更を通知するイベントを購読することで知ることができる。
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
}
|
- 1顔に関するモデルデータの収集状況を更新する。
- 2顔に関するモデルデータの取得状況を更新する。
その他、描画の更新部分などに関する説明は、コードに記載されたコメントを参照してほしい。
活用例
これまでに紹介した機能だけでも、さまざまな応用が考えられるであろう。実際に筆者が作成したサンプルを以下で紹介したい。
絶対に負ける!あっちむいてほいゲーム
- ダウンロードリンク bonprosoft/Kinect2LookThisWay - GitHub
このゲームは、Face Tracking機能を用いて顔の向きを判定している。コードを見ると、複雑なプログラミングは一切行っておらず、このような情報を非常に簡単に利用できることがよく分かる。
また、Hand Trackingを用いたじゃんけん機能の追加や、自分の体をキャラクターにマッピングさせ、よりリアルなじゃんけんゲームを行うなど、Kinectの機能を組み合わせるだけでも、さまざまな改良ができるであろう。
ベストショット撮影ツール
- ダウンロードリンク bonprosoft/Kinect2BestShotTool - GitHub
このプログラムでは、Hand Trackingによるピースの判定と、Face Tracking機能を用いて表情分析を行い、ベストと思われる状態(どちらかの手がピースを行い、かつ笑っていると判定できる状態)にスクリーンショットを行い、画像を保存する。またDepthデータを組み合わせることでリアルタイムに人物を抽出し、指定した画像にマッピングを行っている。今回紹介したHDFaceTrackingを行うことで、さらに詳細な判定や合成を行うことも改善点として考えられる。
終わりに
今回は、Face Trackingに焦点を当て、サンプルプログラムを用いながら、その使い方を学んだ。
現在のSDK2.0プレビュー版は開発途中であり、特にFace Trackingに関しては機能の実装途中でもある(そのため定義されている機能の一部は動作しない場合がある)。しかしながら、現時点でも非常に多くの情報を手軽に取得でき、その性能・可能性を十分に体験できるであろう。
今後もKinect SDKはアップデートされ、Face Trackingに関する機能も日々改善されていくものと思われる。今後の機能追加と性能向上に期待すると同時に、より多くの開発者が、センサーを用いたプログラミングが気軽に行えるようになることを期待している。今回紹介したFace Tracking機能を最大限に活用していってほしい。