Kinectを使って、顔の動きを認識して画面に表示する

2012年10月1日(月)
薬師寺 国安

参照の追加

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)

1Option Strict On
2Imports Microsoft.Kinect
3Imports Microsoft.Kinect.Toolkit
4Imports Microsoft.Kinect.Toolkit.FaceTracking
5Public 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)
2 
3DirectCast(o, FaceTrackingViewer).OnSensorChanged(DirectCast(args.OldValue, KinectSensor), DirectCast(args.NewValue, KinectSensor))
4 
5End Sub))

符号なし32 ビット(4 バイト)の整数型MaxMissedFramesを定数メンバ変数として宣言し、100で初期化しておきます。

1Const MaxMissedFrames As UInteger = 100

キーがInteger型で、値がSkeletonFaceTrackerのコレクションを表す、新しいDictionary ジェネリック クラスのインスタンスtrackedSkeletonsメンバ変数を宣言します。SkeletonFaceTrackerクラスは、このコード内で定義されている、顔の骨格を追跡する者を表すクラスです。

1Dim trackedSkeletons As New Dictionary(Of Integer, SkeletonFaceTracker)()

Byte型の配列メンバ変数myColorImageを宣言します。

1Dim myColorImage As Byte()

RGBカメラの解像度とフレームレートを表す列挙体であるColorImageFormat型のメンバ変数myColorImageFormatを宣言し、ColorImageFormatを未定義のフォーマットに指定しておきます。

1Dim myColorImageFormat As ColorImageFormat = ColorImageFormat.Undefined

Short型の配列メンバ変数myDepthImageを宣言します。

1Dim myDepthImage As Short()

距離カメラの解像度とフレームレートを表す列挙体であるDepthImageFormat型のメンバ変数myDepthImageFormatを宣言し、DepthImageFormatを未定義のフォーマットに指定しておきます。

1Dim myDepthImageFormat As DepthImageFormat = depthImageFormat.Undefined

ブール型のメンバ変数myDisposeを宣言します。

1Dim myDisposed As Boolean

スケルトン型の配列メンバ変数skeletonDataを宣言します。

1Dim skeletonData As Skeleton()

SkeletonFaceTrackerクラスを定義します。

1Private Class SkeletonFaceTracker

顔を三角形で構成するクラスであるFaceTriangleクラス型のShared配列変数faceTrianglesを宣言します。

1Shared faceTriangles As FaceTriangle()

特徴点と2 次元平面に点を定義する浮動小数点座標ペア(x座標とy座標)を表す型を持つ、EnumIndexableCollectionクラス型の変数facePointsを宣言します。

1Dim facePoints As EnumIndexableCollection(Of FeaturePoint, PointF)

顔の追跡者を表すクラスであるFaceTrackerクラス型のmyFaceTracker変数を宣言します。

1Dim myFaceTracker As FaceTracker

ブール型の変数lastFaceTrackSucceededを宣言します。

1Dim lastFaceTrackSucceeded As Boolean

スケルトンの追跡状態を表す列挙型のmySkeletonTrackingState変数を宣言します。

1Dim mySkeletonTrackingState As SkeletonTrackingState

Integer型のプロパティLastTrackedFrameを宣言します。

1Property LastTrackedFrame As Integer

全てのリソースを解放するDisposeメソッド

顔が追跡されている場合は、リソースを解放し、オブジェクトとの関連付けを解除します。

1Public Sub Dispose()
2  If myFaceTracker Is Nothing = False Then
3     myFaceTracker.Dispose()
4     myFaceTracker = Nothing
5  End If
6End Sub

顔を三角形の集合体で描画する処理

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で描画しています。

01Public Sub DrawFaceModel(drawingContext As DrawingContext)
02  If lastFaceTrackSucceeded = False OrElse mySkeletonTrackingState <> SkeletonTrackingState.Tracked Then
03  Return
04  End If
05 
06  Dim faceModelPts = New List(Of System.Windows.Point)()
07  Dim faceModel = New List(Of FaceModelTriangle)()
08 
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)))
11  Next
12 
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)
19  Next
20 
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)
28  Next
29 
30  drawingContext.DrawGeometry(Brushes.Red, New Pen(Brushes.Red, 2.0), faceModelGroup)
31End Sub

顔の追跡情報を更新する処理

追跡されたスケルトンの状態を変数mySkeletonTrackingStateに保持させておきます。スケルトンの状態が追跡されていない場合は、何も行いません。

顔が追跡されていない場合は、これ以後に定義しているkinectプロパティで初期化された新しいFaceTrackerのインスタンスを作成します。

顔が追跡されている場合は、顔の追跡されたフレームを表すクラスであるFaceTrackFrame型の変数frameを宣言し、顔を追跡するFaceTrackerクラスのTrackメソッドで、指定したRGBフォーマット、Byte型のRGBイメージデータ、距離カメラのフォーマット、Short型の距離カメラのデータ、およびスケルトンを使用して追跡を行います。

ブール型のlastFaceTrackSucceeded変数には、顔のフレーム追跡が成功した値を格納しておきます。

顔のフレーム追跡が成功し、顔を三角形で構成するクラスであるFaceTriangleの変数faceTrianglesにデータがあった場合は、三角形を取得してfaceTrianglesに格納します。特徴点と2 次元平面に点を定義する浮動小数点座標ペア(x 座標と y 座標)を表す型を持つ、EnumIndexableCollectionクラス型の変数facePointsに、frame.GetProjected3DShapeで、FeaturePointがPointF データ構造体とぺアになっているフレームが返されます。

01Friend Sub OnFrameReady(kinect As KinectSensor, myColorImageFormat As ColorImageFormat, myColorImage As Byte(), myDepthImageFormat As DepthImageFormat, myDepthImage As Short(), mySkeleton As Skeleton)
02  mySkeletonTrackingState = mySkeleton.TrackingState
03 
04  If mySkeletonTrackingState <> SkeletonTrackingState.Tracked Then
05    Return
06  End If
07  If myFaceTracker Is Nothing = True Then
08    Try
09      myFaceTracker = New FaceTracker(kinect)
10    Catch generatedExceptionName As InvalidOperationException
11      myFaceTracker = Nothing
12    End Try
13  End If
14 
15  If myFaceTracker Is Nothing = False Then
16    Dim frame As FaceTrackFrame = myFaceTracker.Track(myColorImageFormat, myColorImage, myDepthImageFormat, myDepthImage, mySkeleton)
17 
18    lastFaceTrackSucceeded = frame.TrackSuccessful
19    If lastFaceTrackSucceeded = True Then
20      If faceTriangles Is Nothing = True Then
21         faceTriangles = frame.GetTriangles()
22      End If
23 
24      facePoints = frame.GetProjected3DShape()
25    End If
26  End If
27End Sub

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
5  End Structure
6End Class

Finalizeをオーバーライドします。

Finalizeメソッドは、Object がガベージ コレクションにより収集される前に、その Objectのリソースを解放し、その他のクリーンアップ操作を実行できるようにするメソッドです。

1Protected Overrides Sub Finalize()
2  Try
3    Dispose(False)
4  Finally
5    MyBase.Finalize()
6  End Try
7End Sub

KinectSensor型のプロパティKinectを定義しておきます。

1Public Property Kinect() As KinectSensor
2  Get
3    Return DirectCast(Me.GetValue(KinectProperty), KinectSensor)
4  End Get
5 
6  Set(value As KinectSensor)
7    Me.SetValue(KinectProperty, value)
8  End Set
9End Property

全てのリソースを解放するDisposeメソッド

オーバーライドされたDisposeを実行します。 GC.SuppressFinalizeメソッドで、 指定したオブジェクトに対して、ファイナライザを呼び出さないことをシステムに要求します

1Public Sub Dispose()
2    Me.Dispose(True)
3    GC.SuppressFinalize(Me)
4  End Sub

Disposeメソッドをオーバーライドする処理

メンバ変数myDisposeがFalseならmyDisposeをTrueで初期化します。

1Protected Overridable Sub Dispose(disposing As Boolean)
2  If myDisposed = False Then
3    myDisposed = True
4  End If
5End Sub

OnRenderメソッドをオーバーライドする処理

OnRender メソッドは、DrawingContext 型のオブジェクトを使用して呼び出されます。これは、DrawText などの描画メソッドの完全なセットを定義するクラスです。

Dictionaryジェネリック型のtrackedSkeletonsの値の中を、スケルトンの追跡者であるfaceInformation変数で反復処理しながら、顔を三角形で描画する処理を行います。

1Protected Overrides Sub OnRender(drawingContext As DrawingContext)
2  MyBase.OnRender(drawingContext)
3  For Each faceInformation As SkeletonFaceTracker In trackedSkeletons.Values
4    faceInformation.DrawFaceModel(drawingContext)
5  Next
6End Sub

  • 顔の動きを追跡するKinectサンプル

薬師寺国安事務所

薬師寺国安事務所代表。Visual Basic プログラミングと、マイクロソフト系の技術をテーマとした、書籍や記事の執筆を行う。
1950年生まれ。事務系のサラリーマンだった40歳から趣味でプログラミングを始め、1996年より独学でActiveXに取り組む。1997年に薬師寺聖とコラボレーション・ユニット PROJECT KySS を結成。2003年よりフリーになり、PROJECT KySS の活動に本格的に参加、.NETやRIAに関する書籍や記事を多数執筆する傍ら、受託案件のプログラミングも手掛ける。Windows Phoneアプリ開発を経て、現在はWindows ストア アプリを多数公開中

Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。Microsoft MVP for Development Platforms - Windows Phone Development(Oct 2012-Sep 2013)。Microsoft MVP for Development Platforms - Client Development(Oct 2013-Sep 2014)。Microsoft MVP for Development Platforms-Windows Platform Development (Oct 2014-Sep 2015)。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています