Kinectで人体を認識して棒人間を動かすサンプル
今回はKinectを使って、自分の動きに合わせて棒人間を動かすことにチャレンジします。最初は、「スケルトンの認識」です。スケルトンとは骨組みのことを指します。
スケルトンの認識
Kinect for Windows SDK 1.5では同時に2人までのスケルトンを認識できます。1人のスケルトンについて、20箇所のジョイント(関節)を認識できます。人間の身体に対応したスケルトンジョイントの位置と名称は図1のようになります。
図1:人間の身体に対応したスケルトンジョイントの位置と名称(クリックで拡大) ※Kinect for Windowsのヘルプより抜粋 |
では、図1のように各ジョイント(関節)を取得して表示させみましょう。実際に表示させると以下の動画のようになります。
人体の各関節の位置情報を取得して表示した動画
サンプル一式は、会員限定特典としてダウンロードできます。記事末尾をご確認ください。
プロジェクトの作成
VS 2010のメニューから[ファイル(F)/新規作成(N)/プロジェクト(P)]と選択します。次に、「WPF アプリケーション」を選択して、「名前(N)」に任意のプロジェクト名を指定します。ここでは「KINECT_SkeletonShow」という名前を付けています。
ツールボックスからデザイン画面上にCanvasコントロールを1個配置します。名前はskeletonShowとします。次にImageコントロールを1個配置します。名前はrgbImageとします。
XAMLコードはリスト1、レイアウトは図2のようなります。
リスト1 (MainWindow.xaml)
- (1)20箇所のジョイントを表示する領域
- (2)RGBカメラのデータ(実写)を表示するImageコントロール
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="スケルトン表示" Height="650" Width="650"> <Grid> <Canvas Name="skeletonShow" HorizontalAlignment="Left" VerticalAlignment="Top" Width="628" Height="480"/> ■(1) <Image Height="120" HorizontalAlignment="Left" Margin="454,23,0,0" Name="rgbImage" Stretch="Fill" VerticalAlignment="Top" Width="160" /> ■(2) </Grid> </Window>
図2:CanvasとImageコントロールを配置した(クリックで拡大) |
参照の追加
VS2010のメニューから「プロジェクト(P)/参照の追加(R)」と選択して、各種コンポーネントを追加しておきます。今回追加するのは、Microsoft.KinectとCodingFun4.Kinect.Wpf.dllの2つです。.NETタブ内に表示されていないDLLファイルは「参照」タブからDLLファイルを指定します。
Microsoft.Kinect.dllは、C:\Program Files\Microsoft SDKs\Kinect\v1.5\Assemblies内に存在しますので、これを指定します。CodingFun4.Kinect.Wpf.dllはCoding4Fun.Kinect.Toolkitをダウンロードして解凍したフォルダ内に存在していますので、それを指定してください。
次に、ソリューションエクスプローラー内のMainWindow.xamlを展開して表示される、MainWindow.xaml.vbをダブルクリックしてリスト2のコードを記述します。
ロジックコードを記述する
リスト2 (MainWindow.xaml.vb)
Option Strict On Imports Microsoft.Kinect Imports Coding4Fun.Kinect.Wpf Class MainWindow
1つのKinectセンサーを表すクラスである、KinectSensorクラス用メンバ変数kinectを宣言します。
Dim kinect As KinectSensor
ウィンドウが読み込まれた時の処理
Kinectが接続されているかどうかを確認し、接続されていない場合は警告メッセージを出して処理を抜けます。接続されている場合は、一番目のKinectを利用するよう指定します。PCには複数のKinectを同時に接続(最大で4台)できますので、もし複数のKinect接続が確認された場合は、一番目のKinectを利用します。
次に、ColorStream.EnableメソッドでKinectセンサーのRGBカメラの利用を開始します。ColorImageFormat.RgbResolution640x480Fps30列挙体で「RGBフォーマット、解像度は640×480、フレームレートは毎秒30フレーム」と設定します。
DepthStream.Enableで距離カメラの利用を開始します。「解像度は640×480で、フレームレートは毎秒30フレーム」と設定します。
SkeletonStream.Enableメソッドでスケルトンを有効にし、AddHandlerステートメントでRGBカメラ、距離カメラ、スケルトンのフレームが更新されたことを通知するイベントAllFramesReadyにイベントハンドラを追加します。骨格生成を行う場合は、深度センサー(距離カメラ)を有効にする必要があります。StartメソッドでKinectセンサーを開始します。
Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded Try If KinectSensor.KinectSensors.Count = 0 Then MessageBox.Show("KINECTが接続されておりません。") Exit Sub Else kinect = KinectSensor.KinectSensors(0) kinect.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30) kinect.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30) kinect.SkeletonStream.Enable() AddHandler kinect.AllFramesReady, AddressOf kinect_AllFramesReady kinect.Start() End If Catch ex As Exception MessageBox.Show(ex.Message) Exit Sub End Try End Sub
RGBカメラ、距離カメラ、スケルトンのフレームが更新されたことを通知するイベント
rgbImageという名前を持つImageコントロールのSourceプロパティに、e.OpenColorImageFrameメソッドで、新しいフレームのRGBカメラの情報を取得します。情報を取得したら、Coding4Fun.Kinect.Wpfの拡張メソッドであるToBitmapSourceで、myColorImageをBitmapSourceに変換して指定します。これで、Imageコントロール内にカラー画像(実写)が表示されます。このメソッドで取得するColorImageFrameは、Usingで括るか、明示的にDisposeする必要があります。
各ジョイントに矩形を表示するmySkeletonShowプロシージャを実行します。引数として、RGBカメラ、距離カメラ、スケルトンデータ更新を同期したイベントで渡される引数である、AllFramesReadyEventArgsを渡します。
Private Sub kinect_AllFramesReady(sender As Object, e As AllFramesReadyEventArgs) Using myColorImage As ColorImageFrame = e.OpenColorImageFrame rgbImage.Source = myColorImage.ToBitmapSource mySkeletonShow(e) End Using End Sub
各ジョイント(関節)に矩形を表示するプロシージャ
skeletonShowという名前のCanvas内を一度クリアします。AllFramesReadyイベントは、全てのストリームのフレームデータが更新された時発生するイベントですので、このクリア処理を記述していないと、ジョイントデータが何重にもダブって表示されますので、注意してください。
e.OpenSkeletonFrameメソッドでスケルトンフレームを開き、スケルトンのフレームを取得します。スケルトンのフレームが取得できた時の処理を行います。
スケルトンのデータを保持するのに必要なメモリを確保するため、mySkeletonFrame.SkeletonArrayLength-1で初期化された新しいSkeletonのインスタンス、skeletonData配列オブジェクトを作成します。
skeletonArrayLengthプロパティは、スケルトン配列の長さを取得します。これで、スケルトンデータのバイト列を確保し、CopySkeletonDataToメソッドで、スケルトンフレームからスケルトンのデータを取り出します。取得されるデータはプレイヤー分取得されますので、それぞれのトラッキング状態を確認します。
CopySkeletonDataToメソッドは、指定した配列、この場合skeletonData配列変数に、現在のスケルトンフレーム、この場合mySkeletonFrameオブジェクトをコピーするメソッドです。skeletonData配列は、現在の SkeletonFrameで、1つの追跡されたスケルトンについて、各骨格のデータを受け取るスケルトンオブジェクトの配列です。
変数mySkeletonでスケルトンフレームデータの中を反復処理しながら、トラッキング(追跡)されているスケルトンのジョイント(関節)を描画していきます。スケルトンのトラッキングの状態は表1を参照してください。RGBカメラのXとY座標のデータを表す、ColorImagePoint型の変数myPointを宣言し、MapSkeletonPointToColorメソッドで、スケルトンの各関節の座標をRGBカメラの座標に変換します。書式は下記の通りです。戻り値はColorImagePointです。
kinect.MapSkeletonPointToColor(変換するスケルトンの座標, RGBカメラのフォーマット)
新しいRectangleクラスのインスタンスmyRectangleオブジェクトを作成し、Marginプロパティに、スケルトンの各関節のXとY座標を指定します。
Fillプロパティには赤色、WidthとHeightプロパティには15と指定します。各プロパティの設定されたmyRectangleオブジェクトをAddメソッドでCanvasに追加すると各関節部分に赤色の矩形が表示されます。
Private Sub mySkeletonShow(e As AllFramesReadyEventArgs) skeletonShow.Children.Clear() Using mySkeletonFrame As SkeletonFrame = e.OpenSkeletonFrame If mySkeletonFrame Is Nothing = False Then Dim skeletonData As Skeleton() = New Skeleton(mySkeletonFrame.SkeletonArrayLength - 1) {} mySkeletonFrame.CopySkeletonDataTo(skeletonData) For Each mySkeleton In skeletonData If mySkeleton.TrackingState = SkeletonTrackingState.Tracked Then For Each myJoint As Joint In mySkeleton.Joints Dim myPoint As ColorImagePoint = kinect.MapSkeletonPointToColor(myJoint.Position, kinect.ColorStream.Format) Dim myRectangle As New Rectangle With myRectangle .Margin = New Thickness(myPoint.X, myPoint.Y, 0, 0) .Fill = New SolidColorBrush(Colors.Red) .Width = 15 .Height = 15 End With skeletonShow.Children.Add(myRectangle) Next End If Next End If End Using End Sub
ウィンドウが閉じられる時に発生するイベント
Kinectセンサーが動いている時は、AllFramesReadyイベントの登録を解除し、StopメソッドでKinectセンサーの動作を停止します。最後にDisposeメソッドでリソースを解放します。
Private Sub MainWindow_Closing(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles Me.Closing If kinect Is Nothing = False Then If kinect.IsRunning = True Then RemoveHandler kinect.AllFramesReady, AddressOf kinect_AllFramesReady kinect.Stop() kinect.Dispose() End If End If End Sub End Class
表1:スケルトンのトラッキングの状態(SkeletonTrackingState)
メンバ名 | 説明 |
---|---|
NotTracked | スケルトンをトラッキング(追跡)していない。 |
PositionOnly | 全体の位置はトラッキングされているが、個別のジョイント位置はトラッキングされない。 |
Tracked | 全てのジョイント位置がトラッキングされる。 |
では、次にこのスケルトン ジョイントをRGBの画像データ(実画像)と重ねて表示してみましょう。
スケルトン ジョイントをRGB画像データに重ねて表示する
プロジェクト名はKINECT_SkeletonRGBShowです。動作させると、以下の動画のようになります。
スケルトン ジョイントをRGB画像データに重ねて表示した動画
(*)Kinectセンサーは光に大いに影響を受けるため、動作の確認には日光の差していない部屋での動作確認が望まれます。日光がさしていると、ノイズが入ったり誤動作を起こす原因となりますので、注意してください。
サンプル一式は、会員限定特典としてダウンロードできます。記事末尾をご確認ください。
読み込むDLLもプログラムコードも前回の記事と全く同じです。異なるのはデザイン画面上に配置するImageコントロールの位置とサイズです。ImageコントロールをCanvasより後に持ってきます。ImageのWidthとHeightの値をCanvasのWidthとHeightの値と同じにします(リスト3)。
リスト3 編集したXAMLコード(MainWindows.xaml)
(1)
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="実画像上にスケルトン表示" Height="650" Width="650"> <Grid> <Grid> <Image Height="480" Name="rgbImage" Stretch="Fill" Width="628" Canvas.Left="0" Canvas.Top="0" Margin="0,1,0,130" /> ■(1) <Canvas Name="skeletonShow" HorizontalAlignment="Left" VerticalAlignment="Top" Width="628" Height="480"/> ■(1) </Grid> </Grid> </Window>
デザインは図3のようになります。
図3:ImageとCanvasコントロールを同じサイズで同じ位置に配置した(クリックで拡大) |
次に、各関節部分(ジョイント部分)をつないだ「棒人間の表示」を紹介します。
Kinectで人間の身体に対応したスケルトンジョイントを取得するサンプル
スケルトン ジョイントをRGB画像データに重ねて表示するサンプル
人間の動きに合わせて棒人間を動かすサンプル
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Kinectで手の動きに合わせてモニタ上の画像を動かすサンプル
- Kinectを使って、顔の動きを認識して画面に表示する
- Kinectを使って、自分の手のひらに小さな分身を出現させてみる
- プレイヤーの身体パーツを判別するKinectサンプル
- Kinectで得た人体情報を転送して、Windows Phoneの画面上に関節の位置を表示してみる
- 声で選んだアイテムをプレイヤーの身体に装着・連動させるKinectサンプル
- Kinectで手の動きとカーソルを連動して操作するサンプル
- Kinectで手の動きに合わせて波紋を発生させるサンプル
- Kinectの音声認識を使って、プレイヤーを分離、結合させるデモを試してみる
- 人体の連続した動作を音声でキャプチャするKinectのサンプルプログラム