Kinectで得た人体情報を転送して、Windows Phoneの画面上に関節の位置を表示してみる

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

プロジェクトの作成(Windows Phoneアプリケーション)

VS 2010のメニューから[ファイル(F)/新規作成(N)/プロジェクト(P)]と選択します。次に、「インストールされたテンプレート」から「Silverlight for Windows Phone」を選択し、右に表示される項目から「Windows Phone アプリケーション」を選択して、「名前(N)」に任意のプロジェクト名を指定します。ここでは「KINECT_WindowsPhone_Skeleton」という名前を付けています(図6)。Windows Phone OSのバージョンは7.1を選択します。

図6:Windows Phoneアプリケーションを選択する(クリックで拡大)

参照の追加

VS2010のメニューから「プロジェクト(P)/参照の追加(R)」と選択して、各種コンポーネントを追加しておきます。今回追加するのは、Coding4Fun.Kinect.KinectService.CommonとCoding4Fun.Kinect.KinectService.PhoneClientの2つです。「.NET」タブ内に表示されていないDLLファイルは「参照」タブからDLLファイルを指定します。

Coding4Fun.Kinect.KinectService.Common.dlとCoding4Fun.Kinect.KinectService.PhoneClient.dllは、kinectservice.zipを解凍したフォルダの、\KinectService\Phone Client\フォルダ内に存在しますので、これを指定してください。

コントロールの配置

ツールボックスからデザイン画面上にCanvasコントロールを1個、その子要素として、Imageを1個、Ellipseを10個配置します。次にCanvasの外にTextBlockを2個とButtonコントロールを1個配置します。書き出されるXAMLコードはリスト1、レイアウトは図7のようになります。EllipseのNameに指定している名前はリスト1を参照してください。

リスト1 (MainPage.xaml)

(1)x:NameがSkeletonという要素を配置しています。
(2)要素の子要素としてNameがImage1という要素を配置しています。HeightとWidthの値は 要素のHeightとWidthの値と同じにしておきます。各人間の身体の部位をNameにした10個の要素を配置し、WidthとHeightに15を指定して直径15ピクセルの円を描いています。Fillには各円の色を指定しています。
(3)addressTextBoxには、図4で表示されるIPアドレスを入力します。

<phone:PhoneApplicationPage 
  x:Class="KINECT_WindowsPhone_Skeleton.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
  xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
  FontFamily="{StaticResource PhoneFontFamilyNormal}"
  FontSize="{StaticResource PhoneFontSizeNormal}"
  Foreground="{StaticResource PhoneForegroundBrush}"
  SupportedOrientations="Portrait" Orientation="Portrait"
  shell:SystemTray.IsVisible="True">
 
  <!--LayoutRoot は、全てのページ コンテンツが配置されるルート グリッドです-->
  <Grid x:Name="LayoutRoot" Background="Transparent">
    <Canvas x:Name="Skeleton" Width="448" Height="336" Margin="12,108,20,324"> ■(1)
      <Image Height="336" Name="Image1" Stretch="Fill" Width="448" Canvas.Left="0" Canvas.Top="0" /> ■(1)
      <Ellipse Canvas.Left="0" Canvas.Top="0" Height="15" Name="headEllipse" Stroke="Black" Width="15" Fill="Orange" /> ■(2)
      <Ellipse Canvas.Left="15" Canvas.Top="0" Height="15" Name="rightEllipse" Stroke="Black" Width="15" Fill="SlateGray" /> ■(2)
      <Ellipse Canvas.Left="30" Canvas.Top="0" Height="15" Name="leftEllipse" Stroke="Black" Width="15" Fill="SpringGreen" /> ■(2)
      <Ellipse Canvas.Left="45" Canvas.Top="0" Height="15" Name="hipCenterEllipse" Stroke="Black" Width="15" Fill="Red" /> ■(2)
      <Ellipse Canvas.Left="60" Canvas.Top="0" Height="15" Name="ElbowRightEllipse" Stroke="Black" Width="15" Fill="Beige" /> ■(2)
      <Ellipse Canvas.Left="75" Canvas.Top="0" Height="15" Name="ElbowLeftEllipse" Stroke="Black" Width="15" Fill="Firebrick" /> ■(2)
      <Ellipse Canvas.Left="90" Canvas.Top="0" Height="15" Name="shoulderRightEllipse" Stroke="Black" Width="15" Fill="Bisque" /> ■(2)
      <Ellipse Canvas.Left="105" Canvas.Top="0" Height="15" Name="shoulderLeftEllipse" Stroke="Black" Width="15" Fill="Chartreuse" /> ■(2)
      <Ellipse Canvas.Left="120" Canvas.Top="0" Height="15" Name="HipRightEllipse" Stroke="Black" Width="15" Fill="Moccasin" /> ■(2)
      <Ellipse Canvas.Left="135" Canvas.Top="0" Height="15" Name="HipLeftEllipse" Stroke="Black" Width="15" Fill="MediumOrchid" /> ■(2)
    </Canvas> ■(1)
    <TextBlock Height="47" HorizontalAlignment="Left" Margin="15,487,0,0" Name="TextBlock1" Text="Address" VerticalAlignment="Top" Width="87" />
    <TextBox Height="74" HorizontalAlignment="Left" Margin="87,473,0,0" Name="addressTextBox"  VerticalAlignment="Top" Width="293" /> ■(3)
    <Button Content="OK" Height="74" HorizontalAlignment="Left" Margin="372,473,0,0" Name="okButton" VerticalAlignment="Top" Width="90" />
  </Grid>
~コード略~
</phone:PhoneApplicationPage>
図7:各種コントロールがレイアウトされた(クリックで拡大)

次に、ソリューションエクスプローラー内のMainPage.xamlを展開して表示される、MainPage.xaml.vbをダブルクリックしてリスト2のコードを記述します。

ロジックコードを記述する

リスト2 (MainPage.xaml.vb)

Option Strict On

KinectServiceに接続するための機能を提供する、2つの名前空間をインポートしておきます。

Imports Coding4Fun.Kinect.KinectService.Common
Imports Coding4Fun.Kinect.KinectService.PhoneClient

Partial Public Class MainPage
  Inherits PhoneApplicationPage
 
  ' コンストラクター
  Public Sub New()
    InitializeComponent()
  End Sub

リスナーに接続するためのクライアントであるSkeletonClientクラス型のメンバ変数_skeletonClientを宣言します。同様にColorClientクラス型のメンバ変数_colorClientを宣言します。

  Dim _skeletonClient As SkeletonClient
  Dim _colorClient As ColorClient

[OK]ボタンがクリックされた時の処理

Address入力ボックスに入力されたIPアドレスを、変数myIPAddressに格納しておきます。IPアドレスが入力されていない場合は、警告メッセージを表示し、処理を抜けます。IPアドレスが入力されていたなら、新しいSkeletonClientのインスタンスとColorClientのインスタンスを作成します。ColorClientが接続されていなかった場合は、ConnectメソッドでIPアドレスとポートを指定して接続します。

SkeletonClientが接続されていなかった場合は、同じくConnectメソッドでIPアドレスとポートを指定して接続します。

AddHandlerステートメントでRGBカメラのフレームが更新された場合のイベントハンドラを追加します。同じく、スケルトンのフレームが更新された場合のイベントハンドラを追加します。

  Private Sub okButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles okButton.Click
      Dim myIPAddress As String = addressTextBox.Text
 
    If myIPAddress = String.Empty Then
      MessageBox.Show("Addressを入力してください。")
      Exit Sub
    Else
      _skeletonClient = New SkeletonClient()
      _colorClient = New ColorClient()

If Not _colorClient.IsConnected Then _colorClient.Connect(myIPAddress, 4530) Else _colorClient.Disconnect() End If If Not _skeletonClient.IsConnected Then _skeletonClient.Connect(myIPAddress, 4532) Else _skeletonClient.Disconnect() End If AddHandler _colorClient.ColorFrameReady, AddressOf client_ColorFrameReady AddHandler _skeletonClient.SkeletonFrameReady, AddressOf client_SkeletonFrameReady End If End Sub

RGBカメラのフレームが更新された時に発生するイベント

Image1のSourceプロパティにRGBカメラのフレームをビットマップイメージに変換して表示します。

  Private Sub client_ColorFrameReady(ByVal sender As Object, ByVal e As ColorFrameReadyEventArgs)
    If e.ColorFrame.BitmapImage IsNot Nothing Then
      Image1.Source = e.ColorFrame.BitmapImage
    End If
  End Sub

スケルトンフレームが更新された時に発生するイベント

Skeletonクラス用オブジェクト変数mySkeletonで、スケルトンデータを持つe.SkeletonFrame.Skeletons内で、全ての関節の位置がトラッキングされた状態にある、シーケンスの最初の要素を取得していきます。

頭、左手、右手、腰の中央、左肘、右肘、左肩、右肩、左腰、右腰の関節を取得し、各部位に該当する円を、身体の部位に収まるよう描画するSetEllpisePositionプロシージャを実行します。

  Private Sub client_SkeletonFrameReady(ByVal sender As Object, ByVal e As SkeletonFrameReadyEventArgs)
      Dim mySkeleton As Skeleton = (From s In e.SkeletonFrame.Skeletons Where s.TrackingState = SkeletonTrackingState.Tracked Select s).FirstOrDefault()
    If mySkeleton Is Nothing Then
      Return
    End If
    SetEllipsePosition(headEllipse, mySkeleton.Joints(CInt(JointType.Head)))
    SetEllipsePosition(leftEllipse, mySkeleton.Joints(CInt(JointType.HandLeft)))
    SetEllipsePosition(rightEllipse, mySkeleton.Joints(CInt(JointType.HandRight)))
 
    SetEllipsePosition(hipCenterEllipse, mySkeleton.Joints(CInt(JointType.HipCenter)))
    
    SetEllipsePosition(ElbowLeftEllipse, mySkeleton.Joints(CInt(JointType.ElbowLeft)))
    SetEllipsePosition(ElbowRightEllipse, mySkeleton.Joints(CInt(JointType.ElbowRight)))
 
    SetEllipsePosition(shoulderLeftEllipse, mySkeleton.Joints(CInt(JointType.ShoulderLeft)))
    SetEllipsePosition(shoulderRightEllipse, mySkeleton.Joints(CInt(JointType.ShoulderRight)))
 
    SetEllipsePosition(HipLeftEllipse, mySkeleton.Joints(CInt(JointType.HipLeft)))
    SetEllipsePosition(HipRightEllipse, mySkeleton.Joints(CInt(JointType.HipRight)))    End Sub

各円が身体の関節の位置に納まる処理

Canvas.SetLeftとSetTopプロパティの書式は下記です。

  • Canvas.SetLeft(プロパティ値の書き込み対象の要素,指定した要素のCanvas.Left属性を設定)
  • Canvas.SetTop(プロパティ値の書き込み対象の要素,指定した要素のCanvas.Top属性を設定)

448はImage1のWidth、336はImage1のHeightの値です。円の位置合わせのためにWidthとHeighの半分の値を加算しています。

  Private Sub SetEllipsePosition(myEllipse As Ellipse, myJoint As Joint)
    Canvas.SetLeft(myEllipse, (448 * myJoint.Position.X) + 224)
    Canvas.SetTop(myEllipse, (336 * -myJoint.Position.Y) + 168)
  End Sub
End Class

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

  • 人間の関節情報をWindows Phoneに表示するサンプルプログラム

薬師寺国安事務所

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

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