Kinectで人体を認識して棒人間を動かすサンプル

2012年7月5日(木)
薬師寺 国安

棒人間

実写の上に棒人間を重ねて表示してみます。棒人間とは、文字通り棒で描いた人体のことですが、ちょっと説明しづらいので、下記の動画をご覧ください。雰囲気がわかっていただけると思います。

実行した動画は以下です。実写の人間(筆者)の動きに合わせて、棒人間も同じ動きをします。

棒人間の動画

サンプル一式は、会員限定特典としてダウンロードできます。記事末尾をご確認ください。

プロジェクトの作成

VS 2010のメニューから[ファイル(F)/新規作成(N)/プロジェクト(P)]と選択します。次に、「WPF アプリケーション」を選択して、「名前(N)」に任意のプロジェクト名を指定します。ここでは「KINECT_StickMan」という名前を付けています。

ツールボックスからデザイン画面上にKinectColorViewerとImage(NameはstickManImage)とKinectSensorChooserコントロールを1個ずつ配置します。KinectColorViewerコントロールを選択し、プロパティの[その他]パネルにあるKinectプロパティから、「データバインドの適用」を選択し、「ソース」のElementNameにKinectSensorChooser1を選択し、「パス」にKinectを指定します(図4)。

 図4:KinectColorViewerの「データバインドの適用」を設定する(クリックで拡大)

書き出されるXAMLはリスト4、レイアウトは図5のようになります。

リスト4 (MainWindow.xaml)

  • (1)KinectColorViewerのKinectプロパティにデータバインドが設定されている。
  • (2)棒人間を表示させる要素の領域。要素の後に記述しているため、より前面に表示されることになる。
<Window x:Class="MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="棒人間" Height="692" Width="720" xmlns:my="clr-namespace:Microsoft.Samples.Kinect.WpfViewers;assembly=Microsoft.Samples.Kinect.WpfViewers">
  <Grid>
    <my:KinectColorViewer Height="480" HorizontalAlignment="Left" Name="KinectColorViewer1" VerticalAlignment="Top" Width="640" Kinect="{Binding ElementName=KinectSensorChooser1, Path=Kinect}" />  ■(1)
    <Image Height="480" HorizontalAlignment="Left" Name="stickManImage" Stretch="Fill" VerticalAlignment="Top" Width="640" />  ■(2)
    <my:KinectSensorChooser Height="137" HorizontalAlignment="Left" Margin="422,328,0,0" Name="KinectSensorChooser1" VerticalAlignment="Top" Width="195" />
  </Grid>
</Window>
 図5:各コントロールを配置した(クリックで拡大)

参照の追加

VS2010のメニューから「プロジェクト(P)/参照の追加(R)」と選択して、各種コンポーネントを追加しておきます。今回追加するのは、Microsoft.Samples.Kinect.WpfViewersとMicrosoft.Kinectの2つです。

Microsoft.Samples.Kinect.WpfViewersは、 Kinectforwindowssdkv1.zipを下記のURLよりダウンロードし、解凍してできる、
\KinectforWindowsSDKV1\KinectforWindowsSDKV1\2.Setting Up Dev Environment\SettingUpDevEnvironmentVB\SettingUpDevEnvironment\bin\Release内にある、
Microsoft.Samples.Kinect.WpfViewers.dllを参照の追加で追加してください。
→参照:Kinectforwindowssdkv1(RAPID LIBRARY)

(*)KinectSensorChooserをデザイン画面上に追加した時点で、このMicrosoft.Samples.Kinect.WpfViewers.dllは既に参照されていることになります。

Microsoft.Kinect.dllは、C:\Program Files\Microsoft SDKs\Kinect\v1.5\Assemblies内に存在しますので、これを指定します。

次に、ソリューションエクスプローラー内のMainWindow.xamlを展開して表示される、MainWindow.xaml.vbをダブルクリックしてリスト5のコードを記述します。

ロジックコードを記述する

リスト5(MainWindow.xaml.vb)

Option Strict On
Imports Microsoft.Kinect
Imports System.IO
Class MainWindow

表示する画面の幅を640とする定数メンバ変数ScreenWidthを宣言します

  Const ScreenWidth As Single = 640.0F

表示する画面の高さを480とする定数メンバ変数ScreenHeightを宣言します

  Const ScreenHeight As Single = 480.0F

描画する線の太さを3とする定数メンバ変数「関節の太さ」を宣言します

  Const 関節の太さ As Double = 3

ボディーセンターの楕円の太さを10とする定数メンバ変数「ボディーセンターの太さ」を宣言します

  Const ボディーセンターの太さ As Double = 10

Brushクラスのメンバ変数centerPointBrushを宣言し、スケルトンの中心点の描画に使用されるブラシを青で初期化しておきます。

  Dim centerPointBrush As Brush = Brushes.Blue

追跡された関節の色で初期化されたメンバ変数「追跡された関節のブラシ」を宣言します

  Dim 追跡された関節のブラシ As Brush = New SolidColorBrush(Color.FromArgb(255, 68, 192, 68))

接合部分が黄色で初期化されたメンバ変数「推測された関節のブラシ」を宣言します

  Dim 推測された関節のブラシ As Brush = Brushes.Yellow

指定した Brush(赤)とWidth(6)を使用して、Pen クラスの新しいインスタンスで初期化されたメンバ変数「追跡された骨のペン色と幅」を宣言します。これは関節と関節をつなぐ線になります。Penクラスは、直線および曲線の描画に使用するオブジェクトを定義するクラスです。

  Dim 追跡された骨のペン色と幅 As New Pen(Brushes.Red, 6)

指定した Brush(Navy)とWidth(10)を使用して、Pen クラスの新しいインスタンスで初期化されたメンバ変数「推測された骨のペン色と幅」を宣言します。これは、推測される骨格の色を表します。

  Dim 推測された骨のペン色と幅 As New Pen(Brushes.Navy, 10)

Kinectセンサーを表すメンバ変数newSensorを宣言します

  Dim newSensor As KinectSensor

スケルトン表示出力のグループを描画するDrawingGroup型のメンバ変数、myDrawingGroupを宣言します。DrawingGroupを使った描画は、線を引く、丸を描くといった描画命令をDrawingGroupに追加していきます。

  Dim myDrawingGroup As DrawingGroup

表示する画像を描画するDrawingImageクラス型のメンバ変数imageSourceを宣言します

  Dim imageSource As DrawingImage

ウィンドウが読み込まれた時の処理

描画のための新しいDrawingGroupインスタンス、myDrawingGroupオブジェクトを作成します。

DrawingImageクラス型のimageSourceに、描画するための新しいDrawingGroupのインスタンス、myDrawingGroupオブジェクトで初期化された、DrawingImageクラスの新しいインスタンスを指定します。

stickManImageというImageコントロールのSourceプロパティに、DrawingImage型のオブジェクトimageSourceを指定します。イメージ コントロールを使用して、画像を表示します。

KinectSensorChooser1.KinectSensorChangedイベントで、Kinectが接続されているかどうかを確認します。古いセンサーが動いている場合は停止させ、新しいセンサーを取得します。

ColorStream.Enableメソッドで、KinectセンサーのRGBカメラの動作を開始します。

ColorImageFormat.RgbResolution640x480Fps30列挙体で「RGBフォーマット、解像度は640×480、フレームレートは毎秒30フレーム」と設定します。

AddHandlerステートメントで、スケルトンのフレームが更新されたことを通知するSkeletonFrameReadyイベントに、SkeletonsReadyイベントハンドラを追加します。

newSensor.SkeletonStreamでスケルトンストリームを有効にします。その際、スケルトン・トラッキングの移動パラメータを指定するTransformSmoothParametersクラスの各プロパティを設定します。

Smoothingで平滑化の程度を指定します。値は0~1.0の間で、デフォルトは0.5です。値が大きいほど平滑化されます。

Correctionで、平滑化の緩急を指定します。値は0~1.0の間で、デフォルトは0.5です。値が小さいほど、平滑化に緩急をつけるため動作が低下します。

Predictionでスムーズに動作させるために予測されたフレームの数を取得します。

JitterRadiusでジッタ処理の対象にする半径(メートル)を指定します。デフォルトは0.05(5cm)です。

MaxDeviationRadiusで、フィルタされた値と生データとの誤差の許容最大値(メートル)を設定します。デフォルトは0.04(4cm)です。

Kinectセンサーを動作します。

    Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
      myDrawingGroup = New DrawingGroup()
      imageSource = New DrawingImage(myDrawingGroup)
      stickManImage.Source = imageSource
      AddHandler KinectSensorChooser1.KinectSensorChanged, Sub(kinectSender As Object, kinectArgs As DependencyPropertyChangedEventArgs)
                     Dim oldSensor = DirectCast(kinectArgs.OldValue, KinectSensor)
                     If oldSensor Is Nothing = False Then
                       StopKinect(oldSensor)
                     End If
                     newSensor = DirectCast(kinectArgs.NewValue, KinectSensor)
                     If newSensor Is Nothing = True Then
                       Return
                     End If
      
      newSensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30)
      AddHandler newSensor.SkeletonFrameReady, AddressOf SkeletonsReady
    newSensor.SkeletonStream.Enable(New TransformSmoothParameters() With {.Smoothing = 0.5F,
                                     .Correction = 0.5F,
                                     .Prediction = 0.5F,
                                     .JitterRadius = 0.05F,
                                     .MaxDeviationRadius = 0.04F})
                       Try
                         newSensor.Start()
                       Catch
                         MessageBox.Show("KINECTが接続されておりません。")
                         Exit Sub
                         End Try
                     End Sub
  End Sub

スケルトンのフレームが更新されたことを通知するイベント

Skeletonクラス型の配列変数skeletonsを宣言します。

e.OpenSkeletonFrameメソッドで、新しいフレームのスケルトン情報を取得します。スケルトンフレームが確認された場合は、スケルトンの配列の長さである、skeletonFrame.SkeletonArrayLength - 1で初期化された新しいSkeletonのインスタンス、skeletons配列オブジェクトを作成します。このメソッドで取得されるSkeletonFrameは、Usingで括るか、明示的にDisposeする必要があります。

SkeletonArrayLengthプロパティは、スケルトン配列の長さを取得します。これで、スケルトンデータのバイト列を確保し、CopySkeletonDataToメソッドで、スケルトンフレームからスケルトンのデータを取り出します。

drawingGroup.Open メソッドで、DrawingGroup を開いて、DrawingGroupのDrawingオブジェクトを設定し、DrawingContextクラスの型の変数myDrawingContextで参照します。DrawingContextクラスは、描画、プッシュ、およびポップコマンドを使用してビジュアルコンテンツを記述するクラスです。

DrawingRectangleメソッドで、指定した Brush および Pen を使用して四角形を描画します。書式は下記の通りです。

DrawingContext.DrawRectangle(四角形の塗りつぶしに使用するブラシ[これは省略可能であり、Nothing を指定できます], 四角形のストロークを描画する際に使用するペン[これは省略可能であり、Nothing を指定できます], 描画する四角形)

「四角形の塗りつぶしに使用するブラシ」には透明色を指定しています。「四角形のストロークを描画する際に使用するペン」にはNothingを、「描画する四角形」には640×480のサイズのRectを指定しています。Rect構造体では、四角形の幅、高さ、および位置を指定します。書式は下記の通りです。

Rect(四角形の左辺の x 座標の位置, 四角形の上辺の y 座標の位置, 四角形の Width を表す負でない値, 四角形の Height を表す負でない値)

「四角形の左辺の x 座標の位置」と 「四角形の上辺の y 座標の位置」には0を指定し、「四角形の Width を表す負でない値」と「四角形の Height を表す負でない値」にはメンバ変数ScreenWidth(640)と ScreenHeight(480)の値を指定しています。

スケルトンの配列の長さが0でなかった場合は、スケルトンの情報を持つ配列変数skeletonsの中を、繰り返し変数mySkeletonで反復処理しながら以下の処理を行います。

スケルトンが追跡されている場合は、骨格と接合部分を描画するDrawBonesAndJointsプロシージャを実行します。

引数として、追跡されたスケルトンのデータを持つmySkeletonとDrawingContextクラス型のmyDrawingContextを渡します。スケルトンがPositionOnlyの場合、つまり、全体の位置はトラッキングされているが、個別のジョイント位置はトラッキングされない場合は、DrawingContext.DrawEllipse メソッドで、指定した Brush および Pen を使用して楕円を描画します。書式は下記の通りです。

DrawingContext.DrawEllipse(楕円の塗りつぶしに使用するブラシ[これは省略可能であり、Nothing を指定できます], 楕円のストロークを描画する際に使用するペン[これは省略可能であり、Nothing を指定できます], 楕円の中心の位置, 楕円の横半径, 楕円の縦半径)

「楕円の塗りつぶしに使用するブラシ」にはメンバ変数centerPointBrushのBrushの値、青を指定します。「楕円のストロークを描画する際に使用するペン」にはNothingを指定し、「楕円の中心の位置」には、スケルトンポイントを出力するSkeletonPointToScreen関数の戻り値を指定します。「楕円の横半径」と「楕円の縦半径」にはメンバ定数変数[ボディーセンターの太さ]の10を指定します。

  Private Sub SkeletonsReady(ByVal sender As Object, ByVal e As SkeletonFrameReadyEventArgs)
    Dim skeletons(-1) As Skeleton
 
    Using skeletonFrame As SkeletonFrame = e.OpenSkeletonFrame()
      If skeletonFrame Is Nothing = False Then
        skeletons = New Skeleton(skeletonFrame.SkeletonArrayLength - 1) {}
        skeletonFrame.CopySkeletonDataTo(skeletons)
      End If
    End Using
 
    Using myDrawingContext As DrawingContext = drawingGroup.Open()
      myDrawingContext.DrawRectangle(Brushes.Transparent, Nothing, New Rect(0.0, 0.0, ScreenWidth, ScreenHeight))
 
      If skeletons.Length <> 0 Then
        For Each mySkeleton As Skeleton In skeletons
          If mySkeleton.TrackingState = SkeletonTrackingState.Tracked Then
            DrawBonesAndJoints(mySkeleton, myDrawingContext)
          ElseIf mySkeleton.TrackingState = SkeletonTrackingState.PositionOnly Then
            myDrawingContext.DrawEllipse(centerPointBrush, Nothing, SkeletonPointToScreen(mySkeleton.Position), ボディセンターの太さ, ボディセンターの太さ)
          End If
        Next 
      End If
    End Using
  End Sub
  • Kinectで人間の身体に対応したスケルトンジョイントを取得するサンプル

  • スケルトン ジョイントをRGB画像データに重ねて表示するサンプル

  • 人間の動きに合わせて棒人間を動かすサンプル

薬師寺国安事務所

薬師寺国安事務所代表。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メルマガ会員のサービス内容を見る

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