センサーの範囲内にいる人間を見つけて撮影・保存するKinectサンプル

2012年8月27日(月)
薬師寺 国安

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

画像データとXMLファイル(リスト4)を保存する処理

ListBox内に項目(年月日時分秒)が表示されている場合の処理です。

RenderTargetBitmapクラスの新しいインスタンスを作成します。その際下記の書式で初期化します。RenderTargetBitmapクラスはVisual オブジェクトをビットマップに変換します。

RenderTargetBitmap(ビットマップの幅,ビットマップの高さ,ビットマップの水平DPI,ビットマップの垂直DPI,ビットマップの形式)

「ビットマップの形式」には、PixelFormats.Pbgra32を指定します。PixelFormats.Pbgra32は、Pbgra32 ピクセル形式を取得します。Pbgra32 は、bits per pixel(BPP)が 32 の sRGB 形式です。各カラー チャネル(青、緑、赤、およびアルファ)に割り当てられるbits per pixel(BPP)は 8 です。各カラー チャネルは、アルファ値によって左側から乗算されます。

RenderメソッドでImage1をレンダリングします。新しいJpegBitmapEncoderクラスのインスタンスmyJpgオブジェクトを作成します。JpegBitmapEncoderクラスは、JPEG形式のイメージのエンコードに使用されるエンコーダーを定義するクラスです。

myJpg.Frames.Add(BitmapFrame.Create(bmp))で、エンコーダーにフレームを追加していきます。

システムの特別なフォルダである「マイピクチャ」フォルダへのディレクトリパスを取得し、変数dirに格納しておきます。「マイピクチャ」フォルダ内の「KINECT」サブフォルダへのパスを変数saveDirに格納しておきます。

KINECTフォルダと、ListBoxより選択された項目に文字列.jpgを連結したファイルを、新規作成モードで開き、JpegBitmapEncoderクラスのSaveメソッドで、ビットマップ イメージを指定した Stream にエンコードします。これで、年月日時分秒.jpgファイルが作成されます。

変数outTimeにListBoxより選択された項目を格納し、inTime変数にKinectセンサーの視界に人の現れた時刻を格納しておきます。

KINECTフォルダ内にImageFileList.xmlファイルが存在していなかった場合、そして、Kinectセンサーの視界に人が入った時間と、ListBoxに表示されている時間が異なる場合は、XML宣言とルート要素、要素とその子要素とを作成し、変数inTimeの値と変数imageFileNameの値を指定して、XMLを作成し、保存します。[データ表示]ボタンの使用を可能にします。

すでにimageFileList.xmlファイルが存在する場合は、そのファイルを読み込み、要素とその子要素とに値を指定して、読み込んだXMLに追加保存します。[データ表示]ボタンの使用を可能にします。TextBlockに「記録中。」と表示します。

  Private Sub DataSave()
    If ListBox1.Items.Count > 0 Then
 
      Dim bmp = New RenderTargetBitmap(CInt(Image1.Width), CInt(Image1.Height), 96, 96, PixelFormats.Pbgra32)
      bmp.Render(Image1)
      Dim myJpg = New JpegBitmapEncoder
      myJpg.Frames.Add(BitmapFrame.Create(bmp))
      
      Dim dir As String = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)
      Dim saveDir As String = Path.Combine(dir, "KINECT")
 
      Using fs As FileStream = File.Open(Path.Combine(saveDir, imageFileName), FileMode.Create)
        myJpg.Save(fs)
      End Using
 
      outTime = ListBox1.SelectedItem.ToString
      inTime = inTextBlock.Text
 
      If File.Exists(Path.Combine(saveDir, "ImageFileList.xml")) = False Then
        Dim xmldoc As XDocument = <?xml version="1.0" encoding="utf-8"?>
                                  <人物記録>
                                    <情報>
                                      <入時間><%= inTime %></入時間>
                                      <画像名><%= imageFileName %></画像名>
                                    </情報>
                                  </人物記録> 
        xmldoc.Save(Path.Combine(saveDir, "ImageFileList.xml"))
          Button1.IsEnabled = True
          TextBlock1.Text = "記録中。"
        Else
          Dim readXmldoc As XElement = XElement.Load(Path.Combine(saveDir, "ImageFileList.xml"))
          Dim addXml As XElement = 
            <情報>
              <入時間><%= inTime %></入時間>
              <画像名><%= imageFileName %></画像名>
            </情報>
          readXmldoc.Add(addXml)
          readXmldoc.Save(Path.Combine(saveDir, "ImageFileList.xml"))
          Button1.IsEnabled = True
          TextBlock1.Text = "記録中。"
        End If
      End If
  End Sub

[データ表示]ボタンがクリックされた時の処理

DataShowWindowを表示します。

  Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click
    Dim dataPage As New DataShowWindow
    dataPage.Show()
  End Sub

ListBox内の項目が選択された時のイベント

ListBox内に項目名が表示されている場合は、その項目名に文字列.jpgを連結して、変数imageFileNameに格納します。

  Private Sub ListBox1_SelectionChanged(sender As Object, e As System.Windows.Controls.SelectionChangedEventArgs) Handles ListBox1.SelectionChanged
    If ListBox1.Items.Count > 0 Then
      imageFileName = ListBox1.SelectedItem.ToString & ".jpg"
    End If
  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

リスト4 「マイマイピクチャ」のKINECTフォルダに保存されたImageFileList.xmlの構造

<?xml version="1.0" encoding="utf-8"?>
<人物記録>
<情報>
  <入時間>2012年06月21日17時50分53秒</入時間>
  <画像名>2012年06月21日17時50分54秒.jpg</画像名>
</情報>
~  <情報></情報>繰り返し~
</人物記録>

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

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

リスト4 (DataShowWindow.xaml.vb)

Option Strict On
Imports System.IO

ImageInfoクラスにOutRoomとImageNameプロパティを定義しておきます。このプロパティをTextBlockのTextやImageのSourceプロパティにバインドしています。

Public Class ImageInfo
  Property OutRoomTime As String
  Property ImageName As String
End Class

ウィンドウが読み込まれた時の処理

ListBox内をクリアします。システムの特別なフォルダである「マイピクチャ」フォルダへのディレクトリパスを変数dirに格納しておきます。「マイピクチャ」フォルダ内のサブフォルダKINECTへのパスを変数fileDirに格納しておきます。
KINECTフォルダ内のImageFileList.xmlを読み込みます。ImageInfoクラス型の新しいリストであるmyImageInfoオブジェクトを作成します。読み込んだXMLの要素コレクション内の、重複しない(Distinct)要素コレクション内を、変数resultで反復処理しながら、以下の処理を繰り返します。

要素の内容テキストである値(年月日時分秒.jpg)のファイルが存在する場合は、新しいImageInfoクラスのOutRoomプロパティに、Path.GetFileNameWithoutExtensionメソッドで、.jpgという拡張子を除いた要素の値を指定します。
ImageNameプロパティにKINECTフォルダを連結した要素の値を指定して、AddメソッドでmyImageInfoオブジェクトに追加していきます。ListBoxのItemsSourceプロパティにmyImageInfoオブジェクトを指定すると、ListBox内に日時と画像が表示されます。

Public Class DataShowWindow
  Private Sub DataShowWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    ListBox1.Items.Clear()
    Dim dir As String = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)
    Dim fileDir As String = Path.Combine(dir, "KINECT")
    Dim xmldoc As XElement = XElement.Load(Path.Combine(fileDir, "ImageFileList.xml"))
    Dim myImageInfo As New List(Of ImageInfo)
    For Each result In From c In xmldoc.Descendants("情報") Select c.<画像名>.Value Distinct
      If File.Exists(Path.Combine(fileDir, result.Value)) = True Then
        With myImageInfo
          .Add(New ImageInfo With {.OutRoomTime = Path.GetFileNameWithoutExtension(result),
              .ImageName = Path.Combine(fileDir, result)})
        End With
      End If
    Next
    ListBox1.ItemsSource = myImageInfo
  End Sub
End Class

「マイピクチャ」のKINECTフォルダ内のImageFileList.xmlを見るとわかりますが、同じ時刻のデータが重複して記録されています。それでLINQクエリーでDistinct句を使って、データを取り出す際、重複しないデータを取り出して表示しています。

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

  • センサーの範囲内にいる人間を見つけて撮影・保存する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メルマガ会員のサービス内容を見る

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