※前ページからの続きです。
RGBカメラ、距離カメラ、スケルトンのフレームが更新された時の処理
新しいフレームのRGBカメラの情報、新しいフレームの距離カメラの情報、新しいフレームのスケルトンの情報を取得します。距離カメラのイメージフォーマットが、距離カメラのイメージフレームのデータタイプ、解像度、フレームレートと同じでなかった場合は、距離カメラのイメージフォーマットを距離カメラのイメージフレームのフォーマットと同じにします。
同様に、RGBカメラのイメージフォーマットが、RGBカメラのイメージフレームのデータタイプ、解像度、フレームレートと同じでなかった場合は、RGBカメラのイメージフォーマットをRGBカメラのイメージフレームのフォーマットと同じにします。
距離カメラのフレームのピクセルデータを取得するためのShort配列を確保(Short配列の長さはmyDepthImageFrame.PixelDataLength- 1で取得)し、myDepthImageに格納します。
同様に、RGBカメラのフレームのピクセルデータを取得するためのバイト配列を確保(バイト配列の長さはmyColorImageFrame.PixelDataLength - 1で取得)し、myColorImageに格納します。
スケルトンのフレームの、スケルトン配列を確保(スケルトン配列の長さはmySkeletonFrame. SkeletonArrayLength - 1で取得)し、skeletonDataに格納します。
各フレームからCopyPixelDataToメソッドを呼び出し、ピクセルデータを取得します。CopyPixelDataToメソッドは、ピクセルデータの長さを使用して、事前に割り当てられた配列へ、ピクセルごとの深度データやRGBデータをコピーします。
追跡者と、現在のフレーム情報を持つ追跡者のリストを更新します。スケルトンの情報を持つskeletonData内をスケルトン型の変数mySkeletonで反復処理しながら以下の処理を行います。
スケルトンのトラッキング状態が、トラッキングされているか、ジョイントのトラッキングはしておらず、プレイヤー位置のみトラッキングしている(Nearモード時)場合の処理です。追跡されたスケルトンにトラッキングIDが含まれていた場合は、Addメソッドで、追跡されているスケルトンに、指定したキー(スケルトンID)と値(SkeletonFaceTrackerのインスタンス)をディクショナリに追加します。
SkeletonFaceTrackerの新しいインスタンスmySkeletonFaceTrackerを作成します。TryGetValueメソッドで、スケルトンのトラッキングIDに関連付けられている値があった場合は、RGBフォーマット、RGBイメージのバイト配列、距離カメラのフォーマット、深度イメージのShort配列、スケルトン、を引数にOnFrameReadyプロシージャを実行します。最後に追跡されたフレームにスケルトンフレームのフレームナンバーを格納します。
各追跡者に更新されたフレームを与えます。
要素の描画を無効にして、完全に新しいレイアウトパスを強制するInvalidateVisualメソッドを呼び出します。
01 | Private Sub OnAllFramesReady(sender As Object, e As AllFramesReadyEventArgs) |
02 | Dim myColorImageFrame As ColorImageFrame = Nothing |
03 | Dim myDepthImageFrame As DepthImageFrame = Nothing |
04 | Dim mySkeletonFrame As SkeletonFrame = Nothing |
07 | myColorImageFrame = e.OpenColorImageFrame() |
08 | myDepthImageFrame = e.OpenDepthImageFrame() |
09 | mySkeletonFrame = e.OpenSkeletonFrame() |
11 | If myColorImageFrame Is Nothing OrElse myDepthImageFrame Is Nothing OrElse mySkeletonFrame Is Nothing Then |
14 | If myDepthImageFormat <> myDepthImageFrame.Format Then |
15 | myDepthImage = Nothing |
16 | myDepthImageFormat = myDepthImageFrame.Format |
19 | If myColorImageFormat <> myColorImageFrame.Format Then |
20 | myColorImage = Nothing |
21 | myColorImageFormat = myColorImageFrame.Format |
24 | If myDepthImage Is Nothing Then |
25 | myDepthImage = New Short(myDepthImageFrame.PixelDataLength - 1) {} |
28 | If myColorImage Is Nothing Then |
29 | myColorImage = New Byte(myColorImageFrame.PixelDataLength - 1) {} |
32 | If skeletonData Is Nothing OrElse skeletonData.Length <> mySkeletonFrame.SkeletonArrayLength Then |
33 | skeletonData = New Skeleton(mySkeletonFrame.SkeletonArrayLength - 1) {} |
36 | myColorImageFrame.CopyPixelDataTo(myColorImage) |
37 | myDepthImageFrame.CopyPixelDataTo(myDepthImage) |
38 | mySkeletonFrame.CopySkeletonDataTo(skeletonData) |
40 | For Each mySkeleton As Skeleton In skeletonData |
41 | If mySkeleton.TrackingState = SkeletonTrackingState.Tracked OrElse mySkeleton.TrackingState = SkeletonTrackingState.PositionOnly Then |
42 | If trackedSkeletons.ContainsKey(mySkeleton.TrackingId) = False Then |
43 | trackedSkeletons.Add(mySkeleton.TrackingId, New SkeletonFaceTracker()) |
46 | Dim mySkeletonFaceTracker As New SkeletonFaceTracker |
47 | If trackedSkeletons.TryGetValue(mySkeleton.TrackingId, mySkeletonFaceTracker) = True Then |
48 | mySkeletonFaceTracker.OnFrameReady(Kinect, myColorImageFormat, myColorImage, myDepthImageFormat, myDepthImage, mySkeleton) |
49 | mySkeletonFaceTracker.LastTrackedFrame = mySkeletonFrame.FrameNumber |
56 | If myColorImageFrame Is Nothing = False Then |
57 | myColorImageFrame.Dispose() |
60 | If myDepthImageFrame Is Nothing = False Then |
61 | myDepthImageFrame.Dispose() |
64 | If mySkeletonFrame Is Nothing = False Then |
65 | mySkeletonFrame.Dispose() |
Kinectセンサーのデータを受信した時の処理
古いセンサーが動いている場合は、RemoveHandlerステートメントで、イベントを解除します。新しいKinectセンサーが動いている場合は、AddHandlerステートメントで、RGBカメラ、距離カメラ、スケルトンのフレームが更新された時に発生するAllFramesReadyイベントにイベントハンドラを追加します。
01 | Private Sub OnSensorChanged(oldSensor As KinectSensor, newSensor As KinectSensor) |
02 | If oldSensor Is Nothing = False Then |
03 | RemoveHandler oldSensor.AllFramesReady, AddressOf OnAllFramesReady |
06 | If newSensor Is Nothing = False Then |
07 | AddHandler newSensor.AllFramesReady, AddressOf OnAllFramesReady |
次に、ソリューションエクスプローラー内のMainWindow.xamlを展開して表示される、MainWindow.xaml.vbをダブルクリックしてリスト3のコードを記述します。
ロジックコードを記述する
リスト2 (MainWindow.xaml.vb)
2 | Imports Microsoft.Kinect |
3 | Imports Microsoft.Kinect.Toolkit |
Bgr32形式は1ピクセルあたりのビット数が32bitのRGB形式で、先頭から1バイト(8bit)ずつに青、緑、赤、の情報が入っています。各カラーチャネルに割り当てられるbits per pixel(BBP)が8であるため、Bgr32を8で除算した4バイトの値をメンバ変数BrgPixelに格納しておきます。青、緑、赤では24ビットしか使用されません、残りの8ビットはAlphaに使用されることが多いですが、このサンプルでは使用していません。また32ビットを8で除算した4(バイト)を直接指定しても問題ありません。
1 | Dim Bgr32BytesPerPixel As Integer = CInt((PixelFormats.Bgr32.BitsPerPixel) / 8) |
KinectSensorChooserクラスの新しいインスタンスsensorChooserメンバ変数を宣言します。KinectSensorChooserクラスはアプリケーションからKinectセンサーを検出するクラスです。
1 | Dim sensorChooser As New KinectSensorChooser() |
Kinectセンサーを表すメンバ変数Kinectを宣言しておきます。
1 | Dim Kinect As KinectSensor |
ウィンドウが読み込まれた時の処理
FaceTrackingViewer1コントロールに、ユーザーコントロールのFaceTrackingViewer.xaml.vb内で定義したWPFプロパティシステムに登録される依存関係プロパティを表す、DependencyPropertyクラス型のKinectPropertyをバインドします。AddHandlerステートメントでKinectセンサーが検出された時に発生するKinectChangedイベントにイベントハンドラを追加します。KinectSensorChooserを開始します。
1 | Public Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded |
2 | Dim faceTrackingViewerBinding = New Binding("Kinect") With {.Source = sensorChooser} |
3 | FaceTrackingViewer1.SetBinding(FaceTrackingViewer.KinectProperty, faceTrackingViewerBinding) |
4 | AddHandler sensorChooser.KinectChanged, AddressOf SensorChooserOnKinectChanged |
Kinectセンサーが検出された時の処理
新しいKinectセンサーをメンバ変数Kinectに格納します。新しいKinectセンサーが検出された場合は、RGBと距離カメラを有効にします。Nearモードを有効にします。SkeletonTrackingMode.Seatedで、椅子に座った上半身の状態でもスケルトンの認識可能にします。スケルトンを有効にします。AddHandlerステートメントで、RGBカメラ、距離カメラ、スケルトンフレームが更新された時に発生するAllFramesReadyイベントにイベントハンドラを追加します。
01 | Private Sub SensorChooserOnKinectChanged(sender As Object, e As KinectChangedEventArgs) |
03 | If Kinect Is Nothing = False Then |
04 | Kinect.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30) |
05 | Kinect.DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30) |
06 | Kinect.DepthStream.Range = DepthRange.Near |
07 | Kinect.SkeletonStream.EnableTrackingInNearRange = True |
08 | Kinect.SkeletonStream.TrackingMode = SkeletonTrackingMode.Seated |
09 | Kinect.SkeletonStream.Enable() |
10 | AddHandler Kinect.AllFramesReady, AddressOf KinectSensorOnAllFramesReady |
RGBカメラ、距離カメラ、スケルトンフレームが更新された時に発生するイベント
e.OpenColorImageFrameで、新しいフレームのRGBカメラの情報を取得し、e.OpenDepthImageFrameで、新しいフレームの距離カメラの情報を取得します。これらのメソッドで取得するcolorImagerFrameやdepthImageFrameは、Usingで括るか、明示的にDisposeする必要があります。Kinect センサーを取得します。
RGBカメラと距離カメラの両方に情報があった場合は、バイト配列型の変数getBackgroundMaskを宣言して、BackgroundMask関数を実行して戻り値を取得します。BackgroundMask関数には引数として、Kinect センサー、RGBカメラのフレーム情報、距離カメラのフレーム情報を渡します。
NameがcolorImageのImageコントロールのSourceプロパティに、ピクセルデータをビットマップに変換して指定します。
BitmapSource.Createメソッドの書式は下記の通りです。
BitmapSource.Create(RGBカメラで取得したフレーム幅,RGBカメラで取得したフレームの高さ,ビットマップの水平ドット(dpi),ビットマップの垂直ドット(dpi),ビットマップのピクセルフォーマット,ビットマップのパレット,ビットマップイメージのコンテンツを表すバイト配列,ビットマップのストライド)
「ビットマップのピクセルフォーマット」には、PixelFormats.Bgr32を指定します。Bgr32 ピクセル形式を取得します。Bgr32 は、bits per pixel(BPP)が 32 の sRGB 形式です。各カラー チャネル(青、緑、および赤)に割り当てられる bits per pixel(BPP)は 8 です。
「ビットマップイメージのコンテンツ」を表すバイト配列にBackgroundMaskの戻り値である、バイト配列を指定します。
「ビットマップのストライド」は1ラインあたりのバイト数を表しますので、この場合、colorImageFrame.Width * colorImageFrame.BytesPerPixelと指定します。BytesPerPixelプロパティでRGBカメラの1ピクセルあたりのバイト数を取得して、それにRGBカメラで取得されたフレームの幅を乗算した値を指定します。
BytesPerPixelは1ピクセルあたりのビット数が32bitのRGB形式で、先頭から1バイト(8bit)ずつに青、緑、赤、(残りの1バイト(8bit)はAphaチャネル等に利用されることがありますが、このサンプルでは使用していません)の情報が入っています。1バイトは8ビットですので32÷8=4バイトの値になります。よって640×4=2560となり、2560の値を直接指定しても問題ありません。
01 | Private Sub KinectSensorOnAllFramesReady(sender As Object, e As AllFramesReadyEventArgs) |
02 | Using colorImageFrame = e.OpenColorImageFrame() |
03 | Using depthImageFrame = e.OpenDepthImageFrame |
04 | Dim kinect As KinectSensor = TryCast(sender, KinectSensor) |
05 | If kinect Is Nothing = True Then |
09 | If colorImageFrame Is Nothing = True Then |
13 | If (colorImageFrame IsNot Nothing) AndAlso (depthImageFrame IsNot Nothing) Then |
14 | Dim getBackgroundMask As Byte() = BackgroundMask(kinect, colorImageFrame, depthImageFrame) |
15 | colorImage.Source = BitmapSource.Create(colorImageFrame.Width, colorImageFrame.Height, 96, 96, PixelFormats.Bgr32, Nothing, getBackgroundMask, colorImageFrame.Width * colorImageFrame.BytesPerPixel) 'Me.colorImageWritableBitmap |
ウィンドウが閉じられた時の処理
Kinectセンサーの検出を停止します。ユーザーコントロールであるFaceTrackingViewer1のリソースを解放します。
1 | Private Sub MainWindow_Closing(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles Me.Closing |
3 | FaceTrackingViewer1.Dispose() |
表示用のコンテンツを表すバイト配列を取得する関数
RGBカメラの処理を行うクラスのインスタンスを取得します。バイト配列変数colorPixelにRGBカメラのピクセルデータのバイト長分の配列を作成します。CopyPixelDataToメソッドで、RGBカメラのフレームのピクセルデータを取得します。CopyPixelDataToメソッドは、ピクセルデータの長さを使用して、事前に割り当てられた配列へ、ピクセルごとの深度データやRGBデータをコピーします。
Dim outputColor As Byte() = New Byte(colorPixel.Length - 1) {}で、RGBカメラの画像を基に表示用のバッファを作成します。
変数indexを、0から、RGBカメラのピクセルデータのバイト長分の配列を保持している、outputColor.Length分、反復処理を行い、下記の処理を実行します。
繰り返し変数iに対応する、Byte型の配列変数であるRGBカメラの画像(outputColor)に、0を指定し背景を黒にしています。変数iを4バイト分ずつ加算します。戻り値は、表示用のコンテンツを表すバイト配列を取得したoutputColorです。
01 | Private Function BackgroundMask(kinect As KinectSensor, colorFrame As ColorImageFrame, depthFrame As DepthImageFrame) As Byte() |
02 | Dim colorStream As ColorImageStream = kinect.ColorStream |
03 | Dim colorPixel As Byte() = New Byte(colorFrame.PixelDataLength - 1) {} |
04 | colorFrame.CopyPixelDataTo(colorPixel) |
05 | Dim outputColor As Byte() = New Byte(colorPixel.Length - 1) {} |
08 | While i < outputColor.Length |
10 | outputColor(i + 1) = 0 |
11 | outputColor(i + 2) = 0 |
12 | i = i + Bgr32BytesPerPixel |
以上で今回のサンプルは終了です。
編集部より:サンプルプログラムへのリンクが誤っていたため、修正しました。(2012.10.01)