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)

Option Strict On
Imports Microsoft.Kinect
Imports Microsoft.Kinect.Toolkit
Imports Microsoft.Kinect.Toolkit.FaceTracking
Public Class FaceTrackingViewer

WPFプロパティシステムに登録される依存関係プロパティを表す、DependencyPropertyクラス型のKinectPropertyを宣言します。依存プロパティとは「ほかの要素の値に依存してプロパティの値を決定する機構」のことを差します。Public Static Readonly フィールドで宣言します。DependencyProperty.Register メソッドで依存関係プロパティを登録します。

DependencyProperty.Register メソッドの書式は下記の通りです。

DependencyProperty.Register(登録する依存プロパティ名,プロパティの型,依存プロパティを登録している所有者型,依存関係プロパティのプロパティメタデータ)

ここでは、「登録する依存プロパティ名」に「Kinect」、「プロパティの型」に「KinectSensor」、「所有者型」に「FaceTrackingVierer」を指定しています。「プロパティメタデータ」内では以下の処理を行っています。PropertyMetadata を定義することにより、クラスで依存関係プロパティの動作(既定値、プロパティ システム コールバックなど)を定義できます。

指定した既定値と PropertyChangedCallback(依存関係プロパティの有効なプロパティ値が変更された時に呼び出されるコールバックを表す)実装参照を使用して、PropertyMetadata クラスの新しいインスタンスを初期化します。PropertyChangedCallbackでプロパティの有効値が変更される時(OnSensorChanged)にプロパティシステムによって必ず呼び出されるハンドラ実装への参照を行います。

  Public Shared ReadOnly KinectProperty As DependencyProperty = DependencyProperty.Register("Kinect", GetType(KinectSensor), GetType(FaceTrackingViewer), New PropertyMetadata(Nothing, Sub(o, args)

DirectCast(o, FaceTrackingViewer).OnSensorChanged(DirectCast(args.OldValue, KinectSensor), DirectCast(args.NewValue, KinectSensor))

End Sub))

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

  Const MaxMissedFrames As UInteger = 100

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

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

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

  Dim myColorImage As Byte()

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

  Dim myColorImageFormat As ColorImageFormat = ColorImageFormat.Undefined

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

  Dim myDepthImage As Short()

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

  Dim myDepthImageFormat As DepthImageFormat = depthImageFormat.Undefined

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

  Dim myDisposed As Boolean

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

  Dim skeletonData As Skeleton()

SkeletonFaceTrackerクラスを定義します。

Private Class SkeletonFaceTracker

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

  Shared faceTriangles As FaceTriangle()

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

  Dim facePoints As EnumIndexableCollection(Of FeaturePoint, PointF)

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

  Dim myFaceTracker As FaceTracker

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

  Dim lastFaceTrackSucceeded As Boolean

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

  Dim mySkeletonTrackingState As SkeletonTrackingState

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

  Property LastTrackedFrame As Integer

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

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

  Public Sub Dispose()
    If myFaceTracker Is Nothing = False Then
       myFaceTracker.Dispose()
       myFaceTracker = Nothing
    End If
  End 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で描画しています。

  Public Sub DrawFaceModel(drawingContext As DrawingContext)
    If lastFaceTrackSucceeded = False OrElse mySkeletonTrackingState <> SkeletonTrackingState.Tracked Then
    Return
    End If
 
    Dim faceModelPts = New List(Of System.Windows.Point)()
    Dim faceModel = New List(Of FaceModelTriangle)()
 
    For i As Integer = 0 To facePoints.Count - 1
        faceModelPts.Add(New System.Windows.Point(CInt(Me.facePoints(i).X), CInt(Me.facePoints(i).Y)))
    Next
 
    For Each t In faceTriangles
      Dim triangle = New FaceModelTriangle()
      triangle.P1 = faceModelPts(t.First)
      triangle.P2 = faceModelPts(t.Second)
      triangle.P3 = faceModelPts(t.Third)
      faceModel.Add(triangle)
    Next
 
    Dim faceModelGroup = New GeometryGroup()
    For i As Integer = 0 To faceModel.Count - 1
      Dim faceTriangle = New GeometryGroup()
      faceTriangle.Children.Add(New LineGeometry(faceModel(i).P1, faceModel(i).P2))
      faceTriangle.Children.Add(New LineGeometry(faceModel(i).P2, faceModel(i).P3))
      faceTriangle.Children.Add(New LineGeometry(faceModel(i).P3, faceModel(i).P1))
      faceModelGroup.Children.Add(faceTriangle)
    Next
 
    drawingContext.DrawGeometry(Brushes.Red, New Pen(Brushes.Red, 2.0), faceModelGroup)
  End 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 データ構造体とぺアになっているフレームが返されます。

    Friend Sub OnFrameReady(kinect As KinectSensor, myColorImageFormat As ColorImageFormat, myColorImage As Byte(), myDepthImageFormat As DepthImageFormat, myDepthImage As Short(), mySkeleton As Skeleton)
      mySkeletonTrackingState = mySkeleton.TrackingState
 
      If mySkeletonTrackingState <> SkeletonTrackingState.Tracked Then
        Return
      End If
      If myFaceTracker Is Nothing = True Then
        Try
          myFaceTracker = New FaceTracker(kinect)
        Catch generatedExceptionName As InvalidOperationException
          myFaceTracker = Nothing
        End Try
      End If
 
      If myFaceTracker Is Nothing = False Then
        Dim frame As FaceTrackFrame = myFaceTracker.Track(myColorImageFormat, myColorImage, myDepthImageFormat, myDepthImage, mySkeleton)
 
        lastFaceTrackSucceeded = frame.TrackSuccessful
        If lastFaceTrackSucceeded = True Then
          If faceTriangles Is Nothing = True Then
             faceTriangles = frame.GetTriangles()
          End If
 
          facePoints = frame.GetProjected3DShape()
        End If
      End If
    End Sub

FaceModelTriangle構造体の定義

System.Windows.Point型のP1,P2,P3をPublic変数として宣言しています。

    Private Structure FaceModelTriangle
      Public P1 As System.Windows.Point
      Public P2 As System.Windows.Point
      Public P3 As System.Windows.Point
    End Structure
  End Class

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

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

  Protected Overrides Sub Finalize()
    Try
      Dispose(False)
    Finally
      MyBase.Finalize()
    End Try
  End Sub

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

  Public Property Kinect() As KinectSensor
    Get
      Return DirectCast(Me.GetValue(KinectProperty), KinectSensor)
    End Get
 
    Set(value As KinectSensor)
      Me.SetValue(KinectProperty, value)
    End Set
  End Property

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

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

Public Sub Dispose()
    Me.Dispose(True)
    GC.SuppressFinalize(Me)
  End Sub

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

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

  Protected Overridable Sub Dispose(disposing As Boolean)
    If myDisposed = False Then
      myDisposed = True
    End If
  End Sub

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

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

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

  Protected Overrides Sub OnRender(drawingContext As DrawingContext)
    MyBase.OnRender(drawingContext)
    For Each faceInformation As SkeletonFaceTracker In trackedSkeletons.Values
      faceInformation.DrawFaceModel(drawingContext)
    Next
  End 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メルマガ会員のサービス内容を見る

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