ロジックコードを記述する
リスト2 (MainPage.xaml.vb)
KinectServiceに接続するための機能を提供する、2つの名前空間をインポートしておきます。
1 | Imports Coding4Fun.Kinect.KinectService.Common |
2 | Imports Coding4Fun.Kinect.KinectService.PhoneClient |
仮想ファイルシステムを作成および使用するための型が含まれている、System.IO.IsolatedStorage名前空間をインポートします。分離ストレージによって、安全なクライアント側のストレージが提供されます。
1 | Imports System.IO.IsolatedStorage |
XML to LINQを使用するためSystem.Xml.Linq名前空間をインポートしておきます。
Imports System.Xml.Linq
タイマーの機能を提供するクラスであるSystem.Windows.Threading名前空間をインポートしておきます。
1 | Imports System.Windows.Threading |
3 | Partial Public Class MainPage |
4 | Inherits PhoneApplicationPage |
Kinectセンサーの視界内にプレイヤーが存在するかどうかを判別するブール型メンバ変数flagを宣言し、Falseで初期化しておきます。
1 | Dim flag As Boolean = False |
リスナーに接続するためのクライアントであるSkeletonClientクラス型のメンバ変数_skeletonClientを宣言します。同様にColorClientクラス型のメンバ変数_colorClientを宣言します。
1 | Dim _skeletonClient As SkeletonClient |
2 | Dim _colorClient As ColorClient |
Kinectセンサーの視界から外れた時刻を記録するメンバ変数nowTimeと、視界に入ってきた時刻を記録するメンバ変数inTimeを宣言します。
1 | Dim nowTime As String = String.Empty |
タイマーを表すクラスであるDispatcherTimerクラス型のメンバ変数myTimerを宣言します。
1 | Dim myTimer As DispatcherTimer |
ページがアクティブになった時の処理
新しいDispatcherTimerのインスタンスを作成します。Intervalプロパティに1000ミリセコンド(1秒)を指定し、タイマーをスタートします。AddHandlerステートメントで、指定したタイマーの間隔が経過し、タイマーが有効である場合に発生するTickイベントにイベントハンドラを追加します。イベントハンドラ内では、非表示となっているinTextBlockのTextプロパティに現在の年月日時間分秒の値を入れています。
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。分離ストレージ内にkinectというフォルダが存在しない場合は、CreateDirectoryメソッドでkinectというフォルダを作成します。
Path.Combineメソッドで、2つの文字列を1つのパスに結合します。ここでは、kinectフォルダとFileList.xmlを結合しています。kinect\FileList.xmlとなります。この値をxmlFilePath変数に格納します。FileExistsメソッドでxmlFilePathに格納したファイルが存在する場合は、dataButton(データ一覧)の使用を可能にします。そうでない場合は、不可のままです。
01 | Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs) |
02 | myTimer = New DispatcherTimer |
03 | myTimer.Interval = New TimeSpan(1000) |
05 | AddHandler myTimer.Tick, Sub(timerSender As Object, timerArgs As EventArgs) |
06 | inTextBlock.Text = DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒") |
09 | Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication |
10 | If storage.DirectoryExists("kinect") = False Then |
11 | storage.CreateDirectory("kinect") |
13 | Dim xmlFilePath As String = Path.Combine("kinect", "FileList.xml") |
14 | If storage.FileExists(xmlFilePath) = True Then |
15 | dataButton.IsEnabled = True |
17 | dataButton.IsEnabled = False |
19 | MyBase.OnNavigatedTo(e) |
[OK]ボタンがクリックされた時の処理
Address入力ボックスに入力されたIPアドレスを、変数myIPAddressに格納しておきます。IPアドレスが入力されていない場合は、警告メッセージを表示し、処理を抜けます。IPアドレスが入力されている場合は、新しいSkeletonClientのインスタンスとColorClientのインスタンスを作成します。ColorClientが接続されていなかった場合は、ConnectメソッドでIPアドレスとポートを指定して接続します。
SkeletonClientが接続されていなかった場合は、同じくConnectメソッドでIPアドレスとポートを指定して接続します。
AddHandlerステートメントでRGBカメラのフレームが更新された場合のイベントハンドラを追加します。同じく、スケルトンのフレームが更新された場合のイベントハンドラを追加します。
01 | Private Sub okButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles okButton.Click |
02 | Dim myIPAddress As String = addressTextBox.Text |
04 | If myIPAddress = String.Empty Then |
05 | MessageBox.Show("Addressを入力してください。") |
08 | _skeletonClient = New SkeletonClient() |
09 | _colorClient = New ColorClient() |
11 | If Not _colorClient.IsConnected Then |
12 | _colorClient.Connect(myIPAddress, 4530) |
14 | _colorClient.Disconnect() |
16 | If Not _skeletonClient.IsConnected Then |
17 | _skeletonClient.Connect(myIPAddress, 4532) |
19 | _skeletonClient.Disconnect() |
21 | AddHandler _colorClient.ColorFrameReady, AddressOf client_ColorFrameReady |
22 | AddHandler _skeletonClient.SkeletonFrameReady, AddressOf client_SkeletonFrameReady |
RGBカメラのフレームが更新された時に発生するイベント
Image1のSourceプロパティにRGBカメラのフレームをビットマップイメージに変換して表示します。
非表示となっているTextBlock1にメンバ変数flagの値を格納します。flagの値がTrueの場合、つまりKinectセンサーの視界内にプレイヤーが存在した場合は、タイマーを停止し、ListBoxコントロールをクリアして、現在の年月日時間分秒をListBoxに追加し、選択状態にします。
それ以外、つまりKinectセンサーの視界内にプレイヤーが存在していなかった場合は、DataSaveプロシージャーを実行し、タイマーをスタートします。
01 | Private Sub client_ColorFrameReady(ByVal sender As Object, ByVal e As ColorFrameReadyEventArgs) |
02 | If e.ColorFrame.BitmapImage Is Nothing = False Then |
03 | Image1.Source = e.ColorFrame.BitmapImage |
05 | TextBlock1.Text = flag.ToString |
08 | ListBox1.Items.Clear() |
09 | ListBox1.Items.Add(DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒")) |
10 | ListBox1.SelectedIndex = 0 |
スケルトンフレームが更新された時に発生するイベント
Skeletonクラス用のオブジェクト変数であるmySkeletonを使って、スケルトンデータを持つe.SkeletonFrame.Skeletons内で、全ての関節の位置がトラッキングされた状態にある、シーケンスの最初の要素を取得していきます。
プレイヤーの骨格が追跡されていない場合は、メンバ変数flagをFalseで初期化し、タイマーをスタートさせます。それ以外の場合、つまり、プレイヤーの骨格が追跡されていた場合は、flagをTrueで初期化しタイマーを停止します。
01 | Private Sub client_SkeletonFrameReady(ByVal sender As Object, ByVal e As SkeletonFrameReadyEventArgs) |
02 | Dim mySkeleton As Skeleton = (From s In e.SkeletonFrame.Skeletons Where s.TrackingState = SkeletonTrackingState.Tracked Select s).FirstOrDefault() |
04 | If mySkeleton Is Nothing Then |
プレイヤーがKinectセンサーの視界に入った時刻と、視界から外れた時刻をXMLファイルとして保存する処理
ListBoxコントロールに現在の年月日時間分秒が追加されている場合の処理です。メンバ変数nowTimeにListBoxコントロールの項目を格納します。メンバ変数inTimeに非表示となっているinTextBlockに格納されている時刻を格納します。
変数xmlStorageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.CombineでkinectというフォルダとFileList.xmlに格納されている画像名とを連結し、変数xmlFilePathに格納しておきます。
kinectというフォルダ内にFileList.xmlが存在していない場合は、Visual Basic の埋め込み式を用いて、XML宣言とルート要素、その子要素として、その子要素としてとを作成し、埋め込み式の構文である を用いてとの内容テキストに、変数inTimeとnowTimeを指定します。これは ASP.NET で使用される構文と同じです。
分離ストレージ内のファイルを表すIsolatedStorageFileStreamクラス用オブジェクト変数xmlStreamを用意し、IsolatedStorageFile.CreateFileメソッドで、分離ストレージ内にxmlFilePath変数の持っているフォルダ名付きXMLファイルを作成します。
kinectフォルダ内にFileList.xmlファイルが存在すれば、新しいStreamWriter を生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開き、初期化します。Writeメソッドで埋め込み式のXMLをストリームに書き込みます。[データ一覧]ボタンの使用を可能にします。
次は、既にkinectフォルダ内にFileList.xmlが存在する場合の処理です。
IsolatedStorageFileクラスのOpenFileメソッドでkinectフォルダ内のFileList.xmlファイルを、指定したファイルアクセスを使用して指定したモードで開きます。開いたファイルをStreamReaderで読み込みます。ReadToEndメソッドでファイルの最後まで読み取り変数readXmldoc変数に格納しておきます。読み込んだXMLテキストをParseメソッドでXElementとして読み込みます。
追加するとその子要素とを作成します。と要素の内容テキストに、埋め込み式を用いてinTimeとnowTime変数の値を指定します。新しく生成したXML要素を、読み込んだXMLにAddメソッドで追加します。IsolatedStorageFileStreamを閉じます。
FileList.xmlファイルが存在する場合は、新しいStreamWriter を生成し、IsolatedStorageFile.OpenFileメソッドで、指定したファイルアクセスを使用して指定したモードでファイルを開き、初期化します。Writeメソッドで新しいXML要素の追加されたXMLを、ストリームに書き込みます。[データ一覧]ボタンの使用を可能にし、ListBoxの内容をクリアします。
02 | If ListBox1.Items.Count > 0 Then |
03 | nowTime = ListBox1.SelectedItem.ToString |
04 | inTime = inTextBlock.Text |
06 | Dim xmlStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication |
07 | Dim xmlFilePath As String = Path.Combine("kinect", "FileList.xml") |
09 | If xmlStorage.FileExists(xmlFilePath) = False Then |
10 | Dim xmldoc As XDocument = <?xml version="1.0" encoding="utf-8"?> |
13 | <入時間><%= inTime %></入時間> |
14 | <出時間><%= nowTime %></出時間> |
17 | Using xmlStream As IsolatedStorageFileStream = xmlStorage.CreateFile(xmlFilePath) |
20 | If xmlStorage.FileExists(xmlFilePath) = True Then |
21 | Using xmlwriter As StreamWriter = New StreamWriter(xmlStorage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Write)) |
23 | xmlwriter.Write(xmldoc.ToString) |
26 | dataButton.IsEnabled = True |
30 | Dim xmlStream As IsolatedStorageFileStream = xmlStorage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Read) |
31 | Using xmlreader As StreamReader = New StreamReader(xmlStream) |
33 | Dim readXmldoc As String = xmlreader.ReadToEnd |
34 | Dim doc As XElement = XElement.Parse(readXmldoc) |
35 | Dim addXml As XElement = <情報> |
36 | <入時間><%= inTime %></入時間> |
37 | <出時間><%= nowTime %></出時間> |
42 | If xmlStorage.FileExists(xmlFilePath) = True Then |
43 | Using xmlwriter As StreamWriter = New StreamWriter(xmlStorage.OpenFile(xmlFilePath, FileMode.Open, FileAccess.Write)) |
45 | xmlwriter.Write(doc.ToString) |
46 | dataButton.IsEnabled = True |
51 | ListBox1.Items.Clear() |
[データ一覧]ボタンがクリックされた時の処理
KinectServiceを切断します。DataIchiranPageに遷移します。
1 | Private Sub dataButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles dataButton.Click |
2 | RemoveHandler _colorClient.ColorFrameReady, AddressOf client_ColorFrameReady |
3 | RemoveHandler _skeletonClient.SkeletonFrameReady, AddressOf client_SkeletonFrameReady |
4 | _colorClient.Disconnect() |
5 | _skeletonClient.Disconnect() |
6 | NavigationService.Navigate(New Uri("/DataIchiranPage.xaml", UriKind.Relative)) |
次に、ソリューションエクスプローラー内のDataIchiranPage.xamlを展開して表示される、DataIchiranPage.xaml.vbをダブルクリックしてリスト3のコードを記述します。
ロジックコードを記述する
リスト3 (DataIchiranPage.xaml.vb)
02 | Imports System.Xml.Linq |
04 | Imports System.IO.IsolatedStorage |
05 | Imports Microsoft.Phone |
06 | Imports System.Windows.Media.Imaging |
08 | Partial Public Class DataIchiranPage |
09 | Inherits PhoneApplicationPage |
ページがアクティブになった時の処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.Combineメソッドで、2つの文字列を1つのパスに結合します。ここでは、kinectフォルダとFileList.xmlを結合しています。kinect\FileList.xmlとなります。この値をfilePath変数に格納します。
IsolatedStorageFileクラスのOpenFileメソッドでkinectフォルダ内のFileList.xmlファイルを、指定したファイルアクセスを使用して指定したモードで開きます。開いたファイルをStreamReaderで読み込みます。ReadToEndメソッドでファイルの最後まで読み取り変数readXmldoc変数に格納しておきます。読み込んだXMLテキストをParseメソッドでXElementとして読み込みます。
文字列型の新しいリストである、myListInfoオブジェクトを作成します。読み込んだXMLファイルの要素コレクション内を変数resultで反復処理しながら、以下の処理を実行します。
文字列型のリストオブジェクトにAddメソッドで、と要素の内容テキストを、文字列と改行を付加して追加していきます。ListBoxのItemsSourceプロパティに時間の追加されたmyListInfoオブジェクトを指定します。ListBoxコントロール内に「入時間」と「出時間」の一覧が表示されます。
01 | Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs) |
02 | Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication |
03 | Dim filePath As String = Path.Combine("kinect", "FileList.xml") |
04 | Using myStream As IsolatedStorageFileStream = storage.OpenFile(filePath, FileMode.Open, FileAccess.Read) |
05 | Using reader As StreamReader = New StreamReader(myStream) |
06 | Dim readXmldoc As String = reader.ReadToEnd |
08 | Dim doc As XElement = XElement.Parse(readXmldoc) |
09 | Dim myListInfo As New List(Of String) |
11 | For Each result In From c In doc.Descendants("情報") Select c |
12 | myListInfo.Add("入時間=" & result.Element("入時間").Value & vbCrLf & "出時間=" & result.Element("出時間").Value & vbCrLf & "---------------------------") |
14 | ListBox1.ItemsSource = myListInfo |
17 | MyBase.OnNavigatedTo(e) |
[全データの削除]ボタンをクリックした時の処理
変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。Path.Combineメソッドで、2つの文字列を1つのパスに結合します。ここでは、kinectフォルダとFileList.xmlを結合しています。kinect\FileList.xmlとなります。この値をfilePath変数に格納します。kinect\FileList.xmlが存在していた場合は、DeleteFileメソッドで削除します。その後MainPageに遷移します。
1 | Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click |
2 | Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication |
3 | Dim filePath As String = Path.Combine("kinect", "FileList.xml") |
4 | If storage.FileExists(filePath) = True Then |
5 | storage.DeleteFile(filePath) |
7 | NavigationService.Navigate(New Uri("/MainPage.xaml", UriKind.Relative)) |
今回で全14回にわたってお届けしたKinect応用編は終了です。基本編と応用編で全28個のサンプルを紹介してきましたが、いかがだったでしょうか。自分なりのKinectアプリケーション作成のヒントにはなりましたか。もし、少しでもお役に立てたならうれしい限りです。どうも長い間ありがとうございました。
PROJECT KySS 薬師寺国安