参照の追加
VS2010のメニューから「プロジェクト(P)/参照の追加(R)」と選択して、各種コンポーネントを追加しておきます。今回追加するのは、Microsoft.KinectとMicrosoft.Kinect.Toolkit、Microsoft.Kinect.Toolkit.FaceTrackingの3つです。「.NET」タブ内に表示されていないDLLファイルは「参照」タブからDLLファイルを指定します。
Microsoft.Kinect.dllは、C:\Program Files\Microsoft SDKs\Kinect\v1.5\Assemblies内に存在しますので、これを指定します。
Microsoft.Kinect.Toolkit.dllは
C:\Program Files\Microsoft SDKs\Kinect\Developer Toolkit v1.5.1\Samples\bin\
に存在しますので、これを指定してください。
Microsoft.Kinect.Toolkit.FaceTracking.dllは、Kinect for Windows Developer Toolkit 1.5.1に含まれる、Face Tracking Basics-WPFをインストールしたディレクトリの、\Face\FaceTrackingBasics-WPF\bin\x86\Debug\フォルダ内に存在していますので、これを指定してください。
次に、ソリューションエクスプローラー内のFaceTrackingViewer.xamlを展開して表示される、FaceTrackingViewer.xaml.vbをダブルクリックしてリスト2のコードを記述します。
※注意事項:このサンプルを作成して実行する前に、必ず下記のことを行ってください。これは筆者の環境では発生していますが、ひょっとしたら、皆さんの環境では発生しないかもしれません。
「C:\Program Files\Microsoft SDKs\Kinect\Developer Toolkit v1.5.1\Redist\x86\フォルダ内のFaceTrackData.dllとFaceTrackLib.dllを、作成したプロジェクトのbin\Debugフォルダ内に手動でコピーする」
この手続きを行っていないと、筆者の環境では「コピーできません」というエラーが発生しました。エラーが出ない場合はこの作業は不要です。
ユーザーコントロールのロジックコードを記述する
リスト2 (FaceTrackingViewer.xaml.vb)
2 | Imports Microsoft.Kinect |
3 | Imports Microsoft.Kinect.Toolkit |
4 | Imports Microsoft.Kinect.Toolkit.FaceTracking |
5 | Public Class FaceTrackingViewer |
WPFプロパティシステムに登録される依存関係プロパティを表す、DependencyPropertyクラス型のKinectPropertyを宣言します。依存プロパティとは「ほかの要素の値に依存してプロパティの値を決定する機構」のことを差します。Public Static Readonly フィールドで宣言します。DependencyProperty.Register メソッドで依存関係プロパティを登録します。
DependencyProperty.Register メソッドの書式は下記の通りです。
DependencyProperty.Register(登録する依存プロパティ名,プロパティの型,依存プロパティを登録している所有者型,依存関係プロパティのプロパティメタデータ)
ここでは、「登録する依存プロパティ名」に「Kinect」、「プロパティの型」に「KinectSensor」、「所有者型」に「FaceTrackingVierer」を指定しています。「プロパティメタデータ」内では以下の処理を行っています。PropertyMetadata を定義することにより、クラスで依存関係プロパティの動作(既定値、プロパティ システム コールバックなど)を定義できます。
指定した既定値と PropertyChangedCallback(依存関係プロパティの有効なプロパティ値が変更された時に呼び出されるコールバックを表す)実装参照を使用して、PropertyMetadata クラスの新しいインスタンスを初期化します。PropertyChangedCallbackでプロパティの有効値が変更される時(OnSensorChanged)にプロパティシステムによって必ず呼び出されるハンドラ実装への参照を行います。
1 | Public Shared ReadOnly KinectProperty As DependencyProperty = DependencyProperty.Register("Kinect", GetType(KinectSensor), GetType(FaceTrackingViewer), New PropertyMetadata(Nothing, Sub(o, args) |
3 | DirectCast(o, FaceTrackingViewer).OnSensorChanged(DirectCast(args.OldValue, KinectSensor), DirectCast(args.NewValue, KinectSensor)) |
符号なし32 ビット(4 バイト)の整数型MaxMissedFramesを定数メンバ変数として宣言し、100で初期化しておきます。
1 | Const MaxMissedFrames As UInteger = 100 |
キーがInteger型で、値がSkeletonFaceTrackerのコレクションを表す、新しいDictionary ジェネリック クラスのインスタンスtrackedSkeletonsメンバ変数を宣言します。SkeletonFaceTrackerクラスは、このコード内で定義されている、顔の骨格を追跡する者を表すクラスです。
1 | Dim trackedSkeletons As New Dictionary(Of Integer, SkeletonFaceTracker)() |
Byte型の配列メンバ変数myColorImageを宣言します。
1 | Dim myColorImage As Byte() |
RGBカメラの解像度とフレームレートを表す列挙体であるColorImageFormat型のメンバ変数myColorImageFormatを宣言し、ColorImageFormatを未定義のフォーマットに指定しておきます。
1 | Dim myColorImageFormat As ColorImageFormat = ColorImageFormat.Undefined |
Short型の配列メンバ変数myDepthImageを宣言します。
1 | Dim myDepthImage As Short() |
距離カメラの解像度とフレームレートを表す列挙体であるDepthImageFormat型のメンバ変数myDepthImageFormatを宣言し、DepthImageFormatを未定義のフォーマットに指定しておきます。
1 | Dim myDepthImageFormat As DepthImageFormat = depthImageFormat.Undefined |
ブール型のメンバ変数myDisposeを宣言します。
1 | Dim myDisposed As Boolean |
スケルトン型の配列メンバ変数skeletonDataを宣言します。
1 | Dim skeletonData As Skeleton() |
SkeletonFaceTrackerクラスを定義します。
1 | Private Class SkeletonFaceTracker |
顔を三角形で構成するクラスであるFaceTriangleクラス型のShared配列変数faceTrianglesを宣言します。
1 | Shared faceTriangles As FaceTriangle() |
特徴点と2 次元平面に点を定義する浮動小数点座標ペア(x座標とy座標)を表す型を持つ、EnumIndexableCollectionクラス型の変数facePointsを宣言します。
1 | Dim facePoints As EnumIndexableCollection(Of FeaturePoint, PointF) |
顔の追跡者を表すクラスであるFaceTrackerクラス型のmyFaceTracker変数を宣言します。
1 | Dim myFaceTracker As FaceTracker |
ブール型の変数lastFaceTrackSucceededを宣言します。
1 | Dim lastFaceTrackSucceeded As Boolean |
スケルトンの追跡状態を表す列挙型のmySkeletonTrackingState変数を宣言します。
1 | Dim mySkeletonTrackingState As SkeletonTrackingState |
Integer型のプロパティLastTrackedFrameを宣言します。
1 | Property LastTrackedFrame As Integer |
全てのリソースを解放するDisposeメソッド
顔が追跡されている場合は、リソースを解放し、オブジェクトとの関連付けを解除します。
2 | If myFaceTracker Is Nothing = False Then |
3 | myFaceTracker.Dispose() |
4 | myFaceTracker = Nothing |
顔を三角形の集合体で描画する処理
2次元空間でのx 座標とy 座標のペアを表すSystem.Windows.Point型の新しいリストである、faceModelPtsを作成します。同様に、顔を三角形で構成するFaceModelTriangle型の新しいリストであるfaceModelを作成します。
反復変数iで、特徴点と2 次元平面に点を定義する浮動小数点座標ペア(x 座標と y 座標)を表す型を持つ、EnumIndexableCollectionクラス型の変数facePointsで顔の座標を取得し、その個数をCountプロパティで取得して、個数分反復処理を行います。変数iに対応する、取得した顔のxとy座標をAddメソッドで、System.Windows.Point型のリストであるfaceModelPtsオブジェクトに追加していきます。
顔を三角形で構成するクラスであるFaceTriangleクラス型のShared配列変数faceTriangles内を変数tで反復処理しながら以下の処理を繰り返します。
System.Windows.Point型を P1、P2、P3というPublic変数で定義しているFaceModelTriangle構造体の新しいインスタンス、triangleを作成し、三角形を作成します。作成した三角形をFaceModelTriangle構造体のインスタンスであるfaceModelオブジェクトにAddメソッドで追加していきます。
Geometryオブジェクトで構成される複合ジオメトリーを表す、新しいGeometryGroupのインスタンス、faceModelGroupを作成します。変数iで、三角形の追加されたfaceModelオブジェクトの個数分反復処理を行います。新しいGeometryGroupのインスタンス、faceTriangleを作成し、線のジオメトリーを表す、新しいLineGeometryを作成して、生成された三角形をつないでいきます。つないだ三角形をGeometryGroupのインスタンス、faceModelGroupオブジェクトのAddメソッドで追加していきます。
ビジュアルコンテンツを記述するDrawingContextクラスのDrawGeometry メソッドで、指定したBrush およびPen を使用して、指定したGeometry を描画します。書式は下記の通りです。
DrawingContext.DrawGeometry(Geometry の塗りつぶしに使用する Brush[省略可能], Geometry のストロークに使用するPen[省略可能], 描画する Geometry)
上記の書式に従って、描画するGeometryを赤のBrushで太さ2の赤のPenで描画しています。
01 | Public Sub DrawFaceModel(drawingContext As DrawingContext) |
02 | If lastFaceTrackSucceeded = False OrElse mySkeletonTrackingState <> SkeletonTrackingState.Tracked Then |
06 | Dim faceModelPts = New List(Of System.Windows.Point)() |
07 | Dim faceModel = New List(Of FaceModelTriangle)() |
09 | For i As Integer = 0 To facePoints.Count - 1 |
10 | faceModelPts.Add(New System.Windows.Point(CInt(Me.facePoints(i).X), CInt(Me.facePoints(i).Y))) |
13 | For Each t In faceTriangles |
14 | Dim triangle = New FaceModelTriangle() |
15 | triangle.P1 = faceModelPts(t.First) |
16 | triangle.P2 = faceModelPts(t.Second) |
17 | triangle.P3 = faceModelPts(t.Third) |
18 | faceModel.Add(triangle) |
21 | Dim faceModelGroup = New GeometryGroup() |
22 | For i As Integer = 0 To faceModel.Count - 1 |
23 | Dim faceTriangle = New GeometryGroup() |
24 | faceTriangle.Children.Add(New LineGeometry(faceModel(i).P1, faceModel(i).P2)) |
25 | faceTriangle.Children.Add(New LineGeometry(faceModel(i).P2, faceModel(i).P3)) |
26 | faceTriangle.Children.Add(New LineGeometry(faceModel(i).P3, faceModel(i).P1)) |
27 | faceModelGroup.Children.Add(faceTriangle) |
30 | drawingContext.DrawGeometry(Brushes.Red, New Pen(Brushes.Red, 2.0), faceModelGroup) |
顔の追跡情報を更新する処理
追跡されたスケルトンの状態を変数mySkeletonTrackingStateに保持させておきます。スケルトンの状態が追跡されていない場合は、何も行いません。
顔が追跡されていない場合は、これ以後に定義しているkinectプロパティで初期化された新しいFaceTrackerのインスタンスを作成します。
顔が追跡されている場合は、顔の追跡されたフレームを表すクラスであるFaceTrackFrame型の変数frameを宣言し、顔を追跡するFaceTrackerクラスのTrackメソッドで、指定したRGBフォーマット、Byte型のRGBイメージデータ、距離カメラのフォーマット、Short型の距離カメラのデータ、およびスケルトンを使用して追跡を行います。
ブール型のlastFaceTrackSucceeded変数には、顔のフレーム追跡が成功した値を格納しておきます。
顔のフレーム追跡が成功し、顔を三角形で構成するクラスであるFaceTriangleの変数faceTrianglesにデータがあった場合は、三角形を取得してfaceTrianglesに格納します。特徴点と2 次元平面に点を定義する浮動小数点座標ペア(x 座標と y 座標)を表す型を持つ、EnumIndexableCollectionクラス型の変数facePointsに、frame.GetProjected3DShapeで、FeaturePointがPointF データ構造体とぺアになっているフレームが返されます。
01 | Friend Sub OnFrameReady(kinect As KinectSensor, myColorImageFormat As ColorImageFormat, myColorImage As Byte(), myDepthImageFormat As DepthImageFormat, myDepthImage As Short(), mySkeleton As Skeleton) |
02 | mySkeletonTrackingState = mySkeleton.TrackingState |
04 | If mySkeletonTrackingState <> SkeletonTrackingState.Tracked Then |
07 | If myFaceTracker Is Nothing = True Then |
09 | myFaceTracker = New FaceTracker(kinect) |
10 | Catch generatedExceptionName As InvalidOperationException |
11 | myFaceTracker = Nothing |
15 | If myFaceTracker Is Nothing = False Then |
16 | Dim frame As FaceTrackFrame = myFaceTracker.Track(myColorImageFormat, myColorImage, myDepthImageFormat, myDepthImage, mySkeleton) |
18 | lastFaceTrackSucceeded = frame.TrackSuccessful |
19 | If lastFaceTrackSucceeded = True Then |
20 | If faceTriangles Is Nothing = True Then |
21 | faceTriangles = frame.GetTriangles() |
24 | facePoints = frame.GetProjected3DShape() |
FaceModelTriangle構造体の定義
System.Windows.Point型のP1,P2,P3をPublic変数として宣言しています。
1 | Private Structure FaceModelTriangle |
2 | Public P1 As System.Windows.Point |
3 | Public P2 As System.Windows.Point |
4 | Public P3 As System.Windows.Point |
Finalizeをオーバーライドします。
Finalizeメソッドは、Object がガベージ コレクションにより収集される前に、その Objectのリソースを解放し、その他のクリーンアップ操作を実行できるようにするメソッドです。
1 | Protected Overrides Sub Finalize() |
KinectSensor型のプロパティKinectを定義しておきます。
1 | Public Property Kinect() As KinectSensor |
3 | Return DirectCast(Me.GetValue(KinectProperty), KinectSensor) |
6 | Set(value As KinectSensor) |
7 | Me.SetValue(KinectProperty, value) |
全てのリソースを解放するDisposeメソッド
オーバーライドされたDisposeを実行します。 GC.SuppressFinalizeメソッドで、 指定したオブジェクトに対して、ファイナライザを呼び出さないことをシステムに要求します
3 | GC.SuppressFinalize(Me) |
Disposeメソッドをオーバーライドする処理
メンバ変数myDisposeがFalseならmyDisposeをTrueで初期化します。
1 | Protected Overridable Sub Dispose(disposing As Boolean) |
2 | If myDisposed = False Then |
OnRenderメソッドをオーバーライドする処理
OnRender メソッドは、DrawingContext 型のオブジェクトを使用して呼び出されます。これは、DrawText などの描画メソッドの完全なセットを定義するクラスです。
Dictionaryジェネリック型のtrackedSkeletonsの値の中を、スケルトンの追跡者であるfaceInformation変数で反復処理しながら、顔を三角形で描画する処理を行います。
1 | Protected Overrides Sub OnRender(drawingContext As DrawingContext) |
2 | MyBase.OnRender(drawingContext) |
3 | For Each faceInformation As SkeletonFaceTracker In trackedSkeletons.Values |
4 | faceInformation.DrawFaceModel(drawingContext) |