Kinectで人体を認識して棒人間を動かすサンプル
棒人間
実写の上に棒人間を重ねて表示してみます。棒人間とは、文字通り棒で描いた人体のことですが、ちょっと説明しづらいので、下記の動画をご覧ください。雰囲気がわかっていただけると思います。
実行した動画は以下です。実写の人間(筆者)の動きに合わせて、棒人間も同じ動きをします。
棒人間の動画
サンプル一式は、会員限定特典としてダウンロードできます。記事末尾をご確認ください。
プロジェクトの作成
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画像データに重ねて表示するサンプル
人間の動きに合わせて棒人間を動かすサンプル
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Kinectで手の動きに合わせてモニタ上の画像を動かすサンプル
- Kinectを使って、顔の動きを認識して画面に表示する
- Kinectを使って、自分の手のひらに小さな分身を出現させてみる
- プレイヤーの身体パーツを判別するKinectサンプル
- Kinectで得た人体情報を転送して、Windows Phoneの画面上に関節の位置を表示してみる
- 声で選んだアイテムをプレイヤーの身体に装着・連動させるKinectサンプル
- Kinectで手の動きとカーソルを連動して操作するサンプル
- Kinectで手の動きに合わせて波紋を発生させるサンプル
- Kinectの音声認識を使って、プレイヤーを分離、結合させるデモを試してみる
- 人体の連続した動作を音声でキャプチャするKinectのサンプルプログラム