これであなたもダンスグループの一員!?Kinectで自分を分身させるプログラムを作る

2012年9月3日(月)
薬師寺 国安

前ページからの続きです。

Kinectの画像をビットマップデータに書き出す処理

Byte型の配列変数bytePlayerをInt32Rect構造体の高さにscreenImageStrideで乗算した値で初期化します。640*1280=819200-1の値で初期化するのと同じです。

各分身は、バッファに保存してある0.5秒前、1秒前のデータをそれぞれが参照することで、時差表示を可能にしています。1秒で30フレームなので、0.5秒の場合はその半分の15フレームとなり、変数frameを15で初期化しておきます。

繰り返し変数iで0からメンバ定数変数myPlayer(7)-1分、反復処理を行います。
整数型変数iframeを宣言し、反復変数iが0の場合は、iframe変数にRingBuffer構造体のbuffer_indexプロパティの値(リングバッファ内のインデックス値)を指定します。このbuffer_indexは次のフレームに移動するたびに加算される値で、リングバッファ内のインデックス値になります。
iが0以外の場合は、RingBuffer構造体のbuffer_indexプロパティに、15フレームにi-1の値を乗算した値が加算され格納されます。i-1した値を乗算しないと、背景が白で切り抜かれたプレイヤーが1人分余分に表示されますので注意してください。

Byte型のcolorPixelData配列変数に、RingBuffer構造体のget_rgb_frame関数を使って、iframeに該当する、リングバッファ内のRGBカメラのデータ値を取得します。整数型の配列変数PlayerInexに、RingBuffer構造体のget_PlayerIndexData関数を使って、iframeに該当するリングバッファ内のプレイヤーインデックス値を取得します。

変数indexの値が、colorPixelData変数の値の長さより小さい場合は以下の処理を繰り返します。

4バイトずつ加算される変数indexに該当するPlayerIndexの値が0より大きい場合、つまりプレイヤーが存在する場合は、プレイヤーを描画します。indexに対応する、Byte型の配列変数である距離データを画像化していきます。

Kinectの画像をビットマップデータに書き出します。WriteableBitmap型の変数overrayBitmapにWritePixelsメソッドで、byteからビットマップへ書き出します。ビットマップの指定した領域内に更新したデータを格納します。書式は下記の通りです。このoverrayBitmapプロパティの値を、Nameがhuman_image1というImageコントロールのSourceプロパティに指定します。

WritePixels(更新するWriteableBitmapの四角形,ビットマップの更新に使用するピクセル配列,pixel内の更新領域のストライド,入力バッファのオフセット)

  Private Sub RenderScreen2()
    If screenImageStride = 0 Then
      Return
    End If
    Dim bytePlayer As Byte() = New Byte(CInt(myImageSize.Height) * screenImageStride - 1) {}
    Dim frame As Integer = 15
  
    For i As Integer = 0 To myPlayer - 1
      Dim iframe As Integer
 
      If i = 0 Then
        iframe = myRingBuffer.buffer_index
      Else
        iframe = myRingBuffer.buffer_index + (frame * i - 1)
      End If
      Dim colorPixelData As Byte() = myRingBuffer.get_rgb_frame(iframe)
      Dim PlayerIndex As Integer() = myRingBuffer.get_PlayerIndexData(iframe)
      If colorPixelData Is Nothing Then
        Continue For
      End If
 
      Dim index As Integer = 0
      While index < colorPixelData.Length
        If PlayerIndex(index) > 0 Then
          bytePlayer(index) = colorPixelData(index)
          bytePlayer(index + 1) = colorPixelData(index + 1)
          bytePlayer(index + 2) = colorPixelData(index + 2)
          bytePlayer(index + 3) = colorPixelData(index + 3)
        End If
        index = index + BytesPerPixel
      End While
    Next
    overrayBitmap.WritePixels(myScreenImageRect, bytePlayer, screenImageStride, 0)
    human_image1.Source = overrayBitmap
  End Sub

ウィンドウが閉じられる時の処理

Kinectセンサーの動作を停止してリソースを解放する、StopKinectプロシージャを実行します。

 Private Sub MainWindow_Closing(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles Me.Closing
  StopKinect()
  End Sub
  End Class

RingBufferLib.dllのソースコード(Class1.vb)

リングバッファ内にRGB配列のコピー、Depth配列のコピー、PlayerIndex配列のコピー等を行っています。

Option Strict On

Public Class Kinect_RingBuffer
  Public Structure RingBufferData
 
    Public Property ColorPixelData() As Byte()
    Public Property DepthPixelData() As Short()
    Public Property PlayerIndexData() As Integer()
 
    Public Sub InitArray(ColorStream_FramePixelDataLength As Integer, DepthStream_FramePixelDataLength As Integer)
      ColorPixelData = New Byte(ColorStream_FramePixelDataLength - 1) {}
      DepthPixelData = New Short(DepthStream_FramePixelDataLength - 1) {}
      PlayerIndexData = New Integer(ColorStream_FramePixelDataLength - 1) {}
    End Sub
 
    Public Sub copy_framedata(ByRef value As Byte())
      System.Array.Copy(value, ColorPixelData, value.Length)
    End Sub
 
    Public Sub copymyDepthPixelData(value As Short())
      System.Array.Copy(value, DepthPixelData, value.Length)
    End Sub
 
    Public Sub copy_PlayerIndexData(value As Integer())
      System.Array.Copy(value, PlayerIndexData, value.Length)
    End Sub
  End Structure
 
  Public Structure RingBuffer
    Dim myRingBufferData As RingBufferData()
   
    Property max_buffer_frame() As Integer
    Property buffer_index() As Integer
    Private Function get_index(frameNo As Integer) As Integer
      Dim no As Integer
      If frameNo < max_buffer_frame Then
        no = frameNo
      Else
        no = frameNo - max_buffer_frame
      End If
      If frameNo < 0 Then
        no = max_buffer_frame + frameNo
      End If
      Return no
    End Function
 
    Public Function get_rgb_frame(frameNo As Integer) As Byte()
      If myRingBufferData Is Nothing Then
        Return Nothing
      End If
      Return myRingBufferData(get_index(frameNo)).ColorPixelData
    End Function
 
    Public Function get_depth_frame(frameNo As Integer) As Short()
      If myRingBufferData Is Nothing Then
        Return Nothing
      End If
      Return myRingBufferData(get_index(frameNo)).DepthPixelData
    End Function
 
    Public Function get_PlayerIndexData(frameNo As Integer) As Integer()
      If myRingBufferData Is Nothing Then
        Return Nothing
      End If
      Return myRingBufferData(get_index(frameNo)).PlayerIndexData
    End Function
 
    Public Function init_ringbuffer(buffer_sec As Integer, ColorStream_FramePixelDataLength As Integer, DepthStream_FramePixelDataLength As Integer) As Boolean
      Try
        buffer_index = 0
        max_buffer_frame = 30 * buffer_sec
        myRingBufferData = New RingBufferData(max_buffer_frame - 1) {}
 
        For i As Integer = 0 To max_buffer_frame - 1
          myRingBufferData(i) = New RingBufferData()
          myRingBufferData(i).InitArray(ColorStream_FramePixelDataLength, DepthStream_FramePixelDataLength)
        Next
        Return True
      Catch ex As Exception
        Return False
      End Try
    End Function
 
    Public Sub save_framedata(value As Byte())
      myRingBufferData(buffer_index).copy_framedata(value)
    End Sub
   
    Public Sub save_depthdata(value As Short())
      myRingBufferData(buffer_index).copymyDepthPixelData(value)
    End Sub
    Public Sub save_playerIndexdata(value As Integer())
      myRingBufferData(buffer_index).copy_PlayerIndexData(value)
    End Sub
    Public Sub set_nextframe()
      If buffer_index >= max_buffer_frame - 1 Then
        buffer_index = 0
      Else
        buffer_index = buffer_index + 1
      End If
    End Sub
  End Structure
End Class

以上で今回のサンプルは終了です。

  • ダンスグループの一員になれるKinectサンプル

薬師寺国安事務所

薬師寺国安事務所代表。Visual Basic プログラミングと、マイクロソフト系の技術をテーマとした、書籍や記事の執筆を行う。
1950年生まれ。事務系のサラリーマンだった40歳から趣味でプログラミングを始め、1996年より独学でActiveXに取り組む。1997年に薬師寺聖とコラボレーション・ユニット PROJECT KySS を結成。2003年よりフリーになり、PROJECT KySS の活動に本格的に参加、.NETやRIAに関する書籍や記事を多数執筆する傍ら、受託案件のプログラミングも手掛ける。Windows Phoneアプリ開発を経て、現在はWindows ストア アプリを多数公開中

Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。Microsoft MVP for Development Platforms - Windows Phone Development(Oct 2012-Sep 2013)。Microsoft MVP for Development Platforms - Client Development(Oct 2013-Sep 2014)。Microsoft MVP for Development Platforms-Windows Platform Development (Oct 2014-Sep 2015)。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています