手を動かして画面上の写真を左右にスライドさせるKinectサンプル
ソリューションエクスプローラー内のMainPage.xamlを展開して表示される、MainPage.xaml.vbをダブルクリックしてリスト4のコードを記述します。
ロジックコードを記述する
リスト4 (MainWindow.xaml.vb)
Option Strict On Imports Microsoft.Kinect
左右の手の動きを認識するエンジンの含まれる、Microsoft.Samples.Kinect.SwipeGestureRecognizer名前空間をインポートします。
Imports Microsoft.Samples.Kinect.SwipeGestureRecognizer Imports System.Windows.Media.Imaging
ストーリーボードを利用するためのクラスの含まれる、System.Windows.Media.Animation名前空間をインポートします。
Imports System.Windows.Media.Animation Imports System.IO Imports Coding4Fun.Kinect.Wpf Class MainWindow
認識エンジンクラス用メンバ変数を宣言します。
Dim myRecognizer As Recognizer
追跡されたスケルトンの一意のIDを格納するメンバ変数mySkeletonIDを宣言し-1で初期化しておきます。
Dim mySkeletonID As Integer = -1
Kinectセンサーを表すKinectSensorクラス用メンバ変数を宣言します。
Dim kinect As KinectSensor
BitmapImage型のリストである、新しいインスタンスのimageListメンバ変数を宣言します。
Dim imageList As New List(Of BitmapImage)
XML要素を表すクラスであるXElementクラス用メンバ変数を宣言します。
Dim xmldoc As XElement
スケルトンのデータを保持するのに必要なメモリを確保するために、新しいSkeletonのインスタンス、mySkeletons配列オブジェクトを作成します。
Dim mySkeletons As Skeleton() = New Skeleton() {}
XML文書から画像名の要素を取り出すShared型変数indexを宣言しておきます。
Shared index As Integer = 0
ページが読み込まれた時の処理
Kinectセンサーを動作させ、RGBカメラ、スケルトンを有効にするInitializeKinectプロシージャを実行します。
XElement.LoadメソッドでXML文書ファイル(Photo.xml)を読み込みます。
Descendants メソッドで、子孫要素である全ての 要素のコレクションに対して、各要素を変数result に格納していきます。
BitmapImage型のリストであるimageListにAddメソッドで、画像を配置しているフォルダ名のImagesを連結した、result.Element(要素名).Valueの値を、BitmapImageとして追加していきます。
カレント画像を表示するcurrentImageのSourceプロパティにimageListに格納されている0番目の画像を指定します。すると最初の画像が表示されるので、認識エンジンクラス用メンバ変数myRecognizerでCreateRecognizer関数を実行します。
Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded InitializeKinect() xmldoc = XElement.Load("Photo.xml") For Each result In From c In xmldoc.Descendants("画像名") Select c imageList.Add(New BitmapImage(New Uri("Images/" & result.Value, UriKind.Relative))) Next currentImage.Source = imageList(index) TextBlock1.Text = (index + 1).ToString & "枚目=" & xmldoc.Descendants("画像名")(index).Value myRecognizer = CreateRecognizer() End Sub
左右の手の動きで画像をスライドさせる関数(Recognizerクラス型の関数)
新しい認識エンジンである、Recognizerクラスのインスタンスを作成します。
要素を選択するクエリを定義し、Countプロパティで要素の個数を取得します。取得した個数を変数imageCountに格納しておきます。
右手で、右から左に手をワイプさせた時に発生するSwipeRightDetectedイベントに、イベントハンドラを指定します。イベントハンドラ内では下記の処理を行います。スケルトンの追跡IDが、追跡されたスケルトンの一意のIDである場合の処理です。
メンバ変数indexの値が要素の個数から-3した値であった場合は、indexに要素の個数から-3した値を格納し、「最後」という文字を表示して処理を抜けます。
-3しているのは、一度に表示する画像が、カレント画像、ひとつ前の画像、次の画像の3つだからです。画像を配置しているImagesフォルダ内には2枚のダミーのPNG画像を配置しています。
最後の画像をcurrentImageに表示するには、要素の値である20から-3した値の、17番目の画像、インデックスは0から始まるため、0、1、2・・・・・17となり、最後の画像は18番目の画像が表示されることになります。
リスト1のXMLファイルにも最後の2つはダミーの画像を指定し、Imagesフォルダ内にもダミーの画像を配置しています。これを行っていないと、最後の画像を表示する場合nextImageに表示する画像が読み取れずエラーになりますので注意してください。
メンバ変数indexの値が要素の個数から-3した値以外であった場合は、indexの値を1ずつ加算します。
ひとつ前の画像を表示するpreviousImageのSourceプロパティにはimageListの画像データからindex-1した画像を指定します。カレントの画像を表示するcurrentImageにはindexに該当する画像を指定し、次の画像を表示するnextImageにはindex+1の画像を指定します。
TextBlockコントロールに何枚目か、どんな画像ファイル名かを表示します。
Resourcesで名前がLeftAnimationStoryboard(右手を右から左にワイプさせる時のストーリーボード)という名前のストーリーボードを取得し、実行します。
次に、左手で左から右に向かってワイプする時に発生する、SwipeLeftDetectedイベントの処理です。スケルトンの追跡IDが、追跡されたスケルトンの一意のIDである場合の処理になります。
変数indexの値が1かそれより小さい場合はindexに1を格納し、先頭と表示します。
ひとつ前の画像を表示するpreviousImageのSourceプロパティにはimageListのindex+1した画像を指定します。
カレントの画像を表示するcurrentImageにはindex-1に該当する画像を指定し、次の画像を表示するnextImageにはindexの画像を指定します。何枚目か、どんなファイル名かを表示し、変数indexの値を1ずつ減算します。
Resourcesで名前がRightAnimationStoryboard(左手を左から右にワイプさせる時のストーリーボード)という名前のストーリーボードを取得し、実行します。
認識エンジンであるmyRecognizer変数を戻り値とします。
Private Function CreateRecognizer() As Recognizer myRecognizer = New Recognizer() Dim query = From c In xmldoc.Descendants("画像名") Select c Dim imageCount As Integer = query.Count AddHandler myRecognizer.SwipeRightDetected, Sub(rightSender As Object, rightArgs As KinectGestureEventArgs) If rightArgs.Skeleton.TrackingId = mySkeletonID Then TextBlock3.Text = String.Empty If index = query.Count - 3 Then index = query.Count - 3 TextBlock3.Text = "最後" Exit Sub Else index = index + 1 previousImage.Source = imageList(index - 1) currentImage.Source = imageList(index) nextImage.Source = imageList(index + 1) TextBlock1.Text = (index + 1).ToString & "枚目=" & xmldoc.Descendants("画像名")(index).Value End If Dim myStoryboard = TryCast(Resources("LeftAnimateStoryboard"), Storyboard) If myStoryboard IsNot Nothing Then myStoryboard.Begin() End If End If End Sub AddHandler myRecognizer.SwipeLeftDetected, Sub(leftSender As Object, leftArgs As KinectGestureEventArgs) If leftArgs.Skeleton.TrackingId = mySkeletonID Then TextBlock3.Text = String.Empty If index <= 1 Then index = 1 TextBlock3.Text = "先頭" End If previousImage.Source = imageList(index + 1) currentImage.Source = imageList(index - 1) nextImage.Source = imageList(index) TextBlock1.Text = index.ToString & "枚目=" & xmldoc.Descendants("画像名")(index - 1).Value index = index - 1 Dim myStoryboard = TryCast(Resources("RightAnimateStoryboard"), Storyboard) If myStoryboard IsNot Nothing Then myStoryboard.Begin() End If End If End Sub Return myRecognizer End Function
Kinectセンサーを動作させ、RGBカメラ、スケルトンを有効にする処理
イベントを解除し、Kinectセンサーが動作している場合は、動作を停止してリソースを解放するUninitializeKinectプロシージャを実行します。Kinectセンサーが接続されている場合はKinecセンサーを動作させます。
RGBカメラの動作を有効にし、スケルトンの動作を有効にします。
AddHandlerステートメントで、RGBカメラのフレームが更新されたことを通知するイベント、ColorFrameReadyにイベントハンドラを追加します。またスケルトンのフレームが更新されたことを通知するイベント、SkeletonFrameReadyにイベントハンドラを追加します。
Private Sub InitializeKinect() UninitializeKInect() If KinectSensor.KinectSensors.Count <> 0 Then Try kinect = KinectSensor.KinectSensors(0) kinect.Start() Catch kinect = Nothing kinect.Stop() kinect.Dispose() End Try End If If kinect IsNot Nothing Then kinect.ColorStream.Enable() kinect.SkeletonStream.Enable() AddHandler kinect.ColorFrameReady, AddressOf kinect_ColorFrameReady AddHandler kinect.SkeletonFrameReady, AddressOf kinect_SkeletonFrameReady End If End Sub
RGBカメラのフレームが更新されたことを通知するイベント
rgbImageという名前を持つImageコントロールのSourceプロパティに、e.OpenColorImageFrameメソッドで、新しいフレームのRGBカメラの情報を取得し、Coding4Fun.Kinect.Wpfの拡張メソッドであるToBitmapSourceで、myColorImageをBitmapSourceに変換して指定します。
これで、Imageコントロール内にカラー画像(実写)が表示されます。このメソッドで取得するColorImageFrameは、Usingで括るか、明示的にDisposeする必要があります。
Private Sub kinect_ColorFrameReady(sender As Object, e As ColorImageFrameReadyEventArgs) Using myColorImage As ColorImageFrame = e.OpenColorImageFrame rgbImage.Source = myColorImage.ToBitmapSource End Using End Sub
イベントを解除し、Kinectセンサーが動作している場合は、動作を停止してリソースを解放する処理
Kinectセンサーが動作している時は、イベントを解除し、Kinectの動作を停止してリソースを解放します。
Private Sub UninitializeKinect() If kinect Is Nothing = False Then RemoveHandler kinect.ColorFrameReady, AddressOf kinect_ColorFrameReady RemoveHandler kinect.SkeletonFrameReady, AddressOf kinect_SkeletonFrameReady If kinect.IsRunning = True Then kinect.Stop() kinect.Dispose() End If End If End Sub
スケルトンのフレームが更新されたことを通知するイベント
e.OpenSkeletonFrameメソッドでスケルトンフレームを開き、スケルトンのフレームを取得します。スケルトンのフレームが取得できた時の処理を行います。
新しいSkeletonのインスタンスである、mySkeletons配列の長さが、スケルトンフレームの配列の長さと異なる場合は、スケルトンのデータ保持に必要なメモリを確保するため、mySkeletonFrame.SkeletonArrayLength-1で初期化された新しいSkeletonのインスタンス、mySkeletons配列オブジェクトを作成します。
skeletonArrayLengthプロパティは、スケルトン配列の長さを取得します。これで、スケルトンデータのバイト列を確保し、CopySkeletonDataToメソッドで、スケルトンフレームからスケルトンのデータを取り出します。
Integer型の変数newTrackingIdを宣言し-1で初期化しておきます。この変数にはスケルトンの追跡された一意のIDが格納されます。
変数mySkeletonでスケルトンフレームデータ(mySkeletons)の中を反復処理しながら、トラッキング(追跡)されているスケルトンのみを判別します。スケルトンの追跡された一意のIDを変数newTrackingIdに格納します。変数mySkeletonIDが、スケルトンの追跡された一意のIDでなかった場合は、mySkeletonIDにスケルトンの追跡された一意のIDを格納します。
認識エンジンのRecognizerクラスのRecognizeメソッドを実行して、手の動きの認識を開始します。書式は下記の通りです。
myRecognizer.Recognize(sender As Object,frame As Microsoft.Kinect.SkeletonFrame,skeletons() As Microsoft.Kinect.Skeleton)
Private Sub kinect_SkeletonFrameReady(sender As Object, e As SkeletonFrameReadyEventArgs) Using mySkeletonFrame As SkeletonFrame = e.OpenSkeletonFrame() If mySkeletonFrame IsNot Nothing Then If mySkeletons.Length <> mySkeletonFrame.SkeletonArrayLength Then mySkeletons = New Skeleton(mySkeletonFrame.SkeletonArrayLength - 1) {} End If mySkeletonFrame.CopySkeletonDataTo(mySkeletons) Dim newTrackingId As Integer = -1 For Each mySkeleton In mySkeletons If mySkeleton.TrackingState = SkeletonTrackingState.Tracked Then newTrackingId = mySkeleton.TrackingId End If End If Next If mySkeletonID <> newTrackingIdThen mySkeletonID = newTrackingId End If myRecognizer.Recognize(sender, mySkeletonFrame, mySkeletons) End If End Using End Sub End Class
今回でKinectの基本編は終了です。いかがでしたか?Kinectで何ができるかの理解に、少しでもお役に立てたでしょうか?
ぜひ皆さんもKinectプログラミングに挑戦してみてください。未知の世界が広がり、きっと楽しい世界を体験できると思います。どうもありがとうございました。
PROJECT KySS 薬師寺国安
「スライドによるページナビゲーション」サンプルプログラム
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Kinectで人体を認識して棒人間を動かすサンプル
- センサーの範囲内にいる人間を見つけて撮影・保存するKinectサンプル
- Kinectを使って、顔の動きを認識して画面に表示する
- Kinectで手の動きに合わせてモニタ上の画像を動かすサンプル
- Kinectを使ったバーチャル試着室で着せ替えシミュレーション
- プレイヤーの身体パーツを判別するKinectサンプル
- Kinectの音声認識を使って、プレイヤーを分離、結合させるデモを試してみる
- Kinectで得た人体情報を転送して、Windows Phoneの画面上に関節の位置を表示してみる
- Kinectを使って、自分の手のひらに小さな分身を出現させてみる
- 声で選んだアイテムをプレイヤーの身体に装着・連動させるKinectサンプル