Kinectで人体を認識して棒人間を動かすサンプル

2012年7月5日(木)
薬師寺 国安

今回は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)要素を要素より前面に持ってくる。 要素のWidthとHeightの値は要素の値と同じになっている。

<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画像データに重ねて表示するサンプル

  • 人間の動きに合わせて棒人間を動かすサンプル

薬師寺国安事務所

薬師寺国安事務所代表。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メルマガ会員のサービス内容を見る

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