人物を切り抜いて画面に表示するKinectサンプル
※前ページからの続きです。
RGBカメラや距離カメラ、スケルトンのデータが更新された時に発生するイベント
Kinect センサーを取得します。e.OpenColorImageFrameで、新しいフレームのRGBカメラの情報を取得し、e.OpenDepthImageFrameで、新しいフレームの距離カメラの情報を取得します。
これらのメソッドで取得するColorImagerFrameやDepthImageFrameは、Usingで括るか、明示的にDisposeする必要があります。
RGBカメラと距離カメラの両方に情報があった場合は、バイト配列型の変数getBackgroundMaskを宣言して、BackgroundMask関数を実行して戻り値を取得します。BackgroundMask関数には引数として、Kinect センサー、RGBカメラのフレーム情報、距離カメラのフレーム情報を渡します。
ImageコントロールのSourceプロパティに、ピクセルデータをビットマップに変換して指定します。BitmapSource.Createメソッドの書式は下記の通りです。
BitmapSource.Create(RGBカメラで取得したフレーム幅,RGBカメラで取得したフレームの高さ,ビットマップの水平ドット(dpi),ビットマップの垂直ドット(dpi),ビットマップのピクセルフォーマット,ビットマップのパレット,ビットマップイメージのコンテンツを表すバイト配列,ビットマップのストライド)
ビットマップのピクセル形式には、PixelFormats.Bgra32を指定します。Bgra32 は、bits per pixel (BPP) が 32 の sRGB 形式です。各カラー チャネル(青、緑、赤、およびアルファ)に割り当てられるbits per pixel (BPP) は 8 です。ここに、Brg32を指定し、BackgroundMask関数の中で、
If player 0 Then
opacityColor(colorIndex) = colorPixel(colorIndex)
opacityColor(colorIndex + 1) = colorPixel(colorIndex + 1)
opacityColor(colorIndex + 2) = colorPixel(colorIndex + 2)
End If
と記述すると、背景が透明化されませんので注意してください。
ここでは、Brg32ではなく、Bgra32を指定し、BackgroundMask関数の中で、
If player 0 Then
opacityColor(colorIndex) = colorPixel(colorIndex)
opacityColor(colorIndex + 1) = colorPixel(colorIndex + 1)
opacityColor(colorIndex + 2) = colorPixel(colorIndex + 2)
opacityColor(colorIndex + 3) = &HFF
End If
と指定することで、プレイヤーの背景が透明化されます。詳細はBackgroundMask関数の中で解説しています。
ビットマップイメージのコンテンツを表すバイト配列にBackgroundMaskの戻り値である、バイト配列を指定します。
ビットマップのストライドは1ラインあたりのバイト数を表しますので、この場合、colorFrame.Width * colorFrame.BytesPerPixelと指定します。BytesPerPixelプロパティでRGBカメラの1ピクセルあたりのバイト数を取得して、それにRGBカメラで取得されたフレームの幅を乗算した値を指定します。
BytesPerPixelは1ピクセルあたりのビット数が32bitのRGB形式で、先頭から1バイト(8bit)ずつに青、緑、赤、(残りの1バイト(8bit)はAlphaチャネル等に利用されます)の情報が入っています。
1バイトは8ビットですので32÷8=4バイトの値になります。よって640×4=2560となり、2560の値を直接指定しても問題ありません。
Private Sub kinect_AllFramesReady(sender As Object, e As AllFramesReadyEventArgs) Try Dim kinect As KinectSensor = DirectCast(sender, KinectSensor) If kinect Is Nothing Then Return End If Using colorFrame As ColorImageFrame = e.OpenColorImageFrame() Using depthFrame As DepthImageFrame = e.OpenDepthImageFrame() If (colorFrame IsNot Nothing) AndAlso (depthFrame IsNot Nothing) Then Dim getBackgroundMask As Byte() = BackgroundMask(kinect, colorFrame, depthFrame) Image1.Source = BitmapSource.Create(colorFrame.Width, colorFrame.Height, 96, 96, PixelFormats.Bgra32, Nothing, getBackgroundMask, colorFrame.Width * colorFrame.BytesPerPixel) End If End Using End Using Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub
表示用のコンテンツを表すバイト配列を取得する関数
RGBカメラの処理を行うクラスのインスタンスを取得します。同様に、距離カメラの処理を行うクラスのインスタンスを取得します。バイト配列変数colorPixelに、RGBカメラのピクセルデータのバイト長分の配列を作成します。CopyPixelDataToメソッドで、RGBカメラのフレームのピクセルのデータを取得します。
次に、Short配列変数depthPixelに距離カメラのピクセルデータのバイト長分の配列を作成します。CopyPixelDataToメソッドで、距離カメラのフレームのピクセルデータを取得します。
DepthImageFrame.CopyPixelDataToメソッドで取得できる距離データはshort型の16ビットの値です。この16ビットのデータは、上位13ビットが距離データ、下位3ビットがプレイヤーIDで構成されていますので(図2参照)、ここからピクセルごとに距離データを画像データとして作成していきます。
※深度情報は、1ピクセルあたり2バイト(short)。画像情報はフルカラーなので1ピクセルあたり4バイト(byte)が必要です。
RGBデータのピクセル座標を表すColorImagePoint型のcolorPoint配列変数を宣言し、距離カメラのピクセルデータのバイト長分の配列で初期化します。
MapDepthFrameToColorFrameで、距離カメラのX,Y座標に対応する、RGBカメラのX,Y座標を取得します。これは、深度情報を実画像に変換してくれるメソッドです。書式は下記の通りです。
KinectSensor.MapDepthFrameToColorFrame(距離カメラのフォーマット,距離カメラのピクセルデータ,RGBカメラのフォーマット,距離カメラの座標に対応するRGBカメラの座標)
Dim opacityColor As Byte() = New Byte(colorPixel.Length - 1) {}で、RGBカメラの画像を基に表示用のバッファを作成します。
変数indexを、0から、距離カメラのピクセルデータのバイト長分の配列を保持している、depthPixel配列変数の長さ分、反復処理を行い、下記の処理を実行します。
繰り返し変数indexに対応する距離カメラのピクセルデータのバイト配列と、距離カメラのフレームデータから、PlayerIndexBitmaskで取得された、ビットマスクに含まれているプレイヤーIDとで論理積演算を行います。
ビット演算 AND は、2 つのビットを比較し、両方のビットの値が 1 の場合にのみ、値 1 を結果に代入します。そうでない場合は、結果のビットに 0 をセットします。
AND 演算子は、その他すべてのビット演算子と同様、オペランドとして数値のみを取ります。 And演算子に付いては下記のURLを参照してください。また、AndとOr演算子の結果については図3を参照してください。
→ And 演算子(msdn)
繰り返し変数indexに対応するRGBデータのピクセル座標を表す、ColorImagePointの配列変数colorPointのXとY座標を取得します。 RGBデータのY座標に 距離カメラのフレームデータの幅を乗算して、RGBデータのX座標を加算し、その値にメンバ変数BrgPixelの 4(ビット)を乗算して変数colorIndexに格納しておきます。
プレイヤーを検出した座標だけ、RGBカメラの画像を使用します。変数colorIndexに対応する、Byte型の配列変数であるRGBカメラの画像(opacityColor)に、青、緑、赤、Alphaを指定していきます。32ビット(4バイト)分確保された配列で、最後の余った8ビット(1バイト)をAlphaチャネルに使用しています。
opacityColor(colorIndex + 3) = &HFFと指定しAlphaを指定することで背景が透明化されます。
戻り値は、表示用のコンテンツを表すバイト配列を取得したopacityColorです。
Private Function BackgroundMask(kinect As KinectSensor, colorFrame As ColorImageFrame, depthFrame As DepthImageFrame) As Byte() Dim myColorStream As ColorImageStream = kinect.ColorStream Dim myDepthStream As DepthImageStream = kinect.DepthStream Dim colorPixel As Byte() = New Byte(colorFrame.PixelDataLength - 1) {} colorFrame.CopyPixelDataTo(colorPixel) Dim depthPixel As Short() = New Short(depthFrame.PixelDataLength - 1) {} depthFrame.CopyPixelDataTo(depthPixel) Dim colorPoint As ColorImagePoint() = New ColorImagePoint(depthFrame.PixelDataLength - 1) {} kinect.MapDepthFrameToColorFrame(myDepthStream.Format, depthPixel, myColorStream.Format, colorPoint) Dim opacityColor As Byte() = New Byte(colorPixel.Length - 1) {} For index As Integer = 0 To depthPixel.Length - 1 Dim player As Integer = depthPixel(index) And DepthImageFrame.PlayerIndexBitmask Dim x As Integer = colorPoint(index).X Dim y As Integer = colorPoint(index).Y Dim colorIndex As Integer = ((y * depthFrame.Width) + x) * BgrPixel If player <> 0 Then opacityColor(colorIndex) = colorPixel(colorIndex) 'Blue opacityColor(colorIndex + 1) = colorPixel(colorIndex + 1) 'Green opacityColor(colorIndex + 2) = colorPixel(colorIndex + 2) 'Red opacityColor(colorIndex + 3) = &HFF 'Alpha End If Next Return opacityColor End Function
人物だけを切り抜いて表示するKinectサンプル
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Kinectで結成したマイ・ダンスチームを、サンプルを見ながら実際の背景に合成してみよう
- これであなたもダンスグループの一員!?Kinectで自分を分身させるプログラムを作る
- Kinectの音声認識を使って、プレイヤーを分離、結合させるデモを試してみる
- Kinectを使って、自分の手のひらに小さな分身を出現させてみる
- Kinectを使って、画面上の赤い輪をくぐるサンプル
- プレイヤーの身体パーツを判別するKinectサンプル
- Kinectを使って、顔の動きを認識して画面に表示する
- 人体の連続した動作を音声でキャプチャするKinectのサンプルプログラム
- Kinectで距離カメラの値を取得して、指定した距離で人物が背景に溶け込むサンプル
- 人物特定に使える!?実際の映像で顔を認識するKinectプログラム