Kinectを使ったバーチャル試着室で着せ替えシミュレーション

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

コントロールの配置

ツールボックスからデザイン画面上にListBoxコントロール1個、KinectColorViwerコントロールを1個配置します。次に 要素を配置し、その子要素として、Imageコントロールを1個、KinectSensorChooserコントロールを1個配置します。

次に、KinectColorViewerを選択し、プロパティの[その他]パネルにあるKinectの「データバインドの適用」を選択します。ElementNameにKinectSensorChooser1を指定し、「パス」にKinectを指定します(図2)。

図2:KinectColorViewerのKinectプロパティから「データバインドの適用」を設定する(クリックで拡大)

書き出されるXAMLをリスト2のように編集します。レイアウトは図3になります。

リスト2 書き出され編集されたXAMLコード(MainWindow.xaml)

  • (1)要素のWindowStateにMaximizedを指定して全画面表示とします。
  • (2)プロパティ要素内に、KeyがListBoxTemplateという名前の要素を配置します。中に要素を配置しMarginプロパティに10を指定して余白を設けます。要素内に要素を配置し、Width、Height、Sourceプロパティにデータをバインドします。ここで指定する名称はVBコード内で定義されたプロパティ名です。
  • (3)KinectColorViewerのKinectプロパティにデータバインドが適用されています。
  • (4)ListBoxのItemTemplateプロパティで(1)で定義したListBoxTemplateを参照します。
<Window x:Class="MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" WindowState="Maximized"
  Title="MainWindow" Height="1026" Width="976" xmlns:my="clr-namespace:Microsoft.Samples.Kinect.WpfViewers;assembly=Microsoft.Samples.Kinect.WpfViewers"> ■(1)
  <Window.Resources> ■(2)
    <DataTemplate x:Key="ListBoxTemplate">  ■(2)
      <StackPanel Margin="10"> ■(1)
        <Image Source="{Binding 画像名}" Width="{Binding 幅}" Height="{Binding 高さ}" Stretch="Fill"/> ■(2)
      </StackPanel> ■(2)
    </DataTemplate> ■(2)
  </Window.Resources> ■(2)
  <Canvas>
    <ListBox Height="884" HorizontalAlignment="Left" Margin="685,0,0,0" Name="ListBox1" VerticalAlignment="Top" Width="226" ItemTemplate="{StaticResource ListBoxTemplate}"/> ■(4)
    <my:KinectColorViewer Height="480" HorizontalAlignment="Left" Name="KinectColorViewer1" VerticalAlignment="Top" Width="640" Kinect="{Binding ElementName=KinectSensorChooser1, Path=Kinect}" /> ■(3)
    <Canvas x:Name="ImageArea" Width="640" Height="480" Margin="0,0,310,60">
      <Image Height="95" HorizontalAlignment="Left"  Name="fashionImage" Stretch="Fill" VerticalAlignment="Top" Width="81" />
      <my:KinectSensorChooser Height="142" Name="KinectSensorChooser1" Width="175" Canvas.Left="444" Canvas.Top="320" />
    </Canvas>
  </Canvas>
</Window>
図3:各種コントロールがレイアウトされた(クリックで拡大)

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

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

リスト3 (MainWindow.xaml.vb)

Option Strict On

Imports Microsoft.Kinect

音声認識用のオーディオ形式を表すクラスが含まれる、Microsoft.Speech.AudioFormat名前空間をインポートします。

Imports Microsoft.Speech.AudioFormat

音声認識を実装するためのクラスが含まれる、Microsoft.Speech.Recognition名前空間をインポートします。

Imports Microsoft.Speech.Recognition

Imports Coding4Fun.Kinect.Wpf
Imports System.Windows.Media.Imaging
Imports System.IO

Fashionクラス内にString型の「画像名」「幅」「高さ」プロパティを定義しています。XAMLコード内でのバインディングに使用されています。

Public Class FashionInfo
  Property 画像名 As String
  Property 幅 As String
  Property 高さ As String
End Class
Class MainWindow

深度情報が認識可能なプレイヤーの数(6)で初期化された定数メンバ変数、skeletonCountを宣言します。

  Const skeletonCount As Integer = 6

認識可能なプレイヤーの数分の配列を持った、Skeletonクラス型のメンバ配列変数allSkeletonsを宣言しておきます。-1しているのは、添え字は0から始まるためです。

  Dim allSkeletons(skeletonCount - 1) As Skeleton

1個のKinectセンサーを表すメンバ変数kinectを宣言します。

  Dim kinect As KinectSensor

XML要素を表すメンバ変数xmldocを宣言します。

  Dim xmldoc As XElement

音声認識サービスを実行するためのアクセス権を提供するSpeechRecognitionEngineクラス用メンバ変数engineを宣言します。

  Dim engine As SpeechRecognitionEngine

  Dim words As String

ListBox内の項目のインデックスに該当するメンバ変数で、0で初期化しておきます。

  Dim index As Integer = 0

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

XElement.Loadメソッドでbin\Debugフォルダ内のXML文書ファイル(fashion.xml)を読み込みます。FashionInfoクラス型の新しいリストであるmyFashionInfoオブジェクトを作成します。
要素のコレクション内を変数resultで反復処理しながら、以下の処理を繰り返します。

新しいFashionInfoクラスの「画像名」プロパティに、Image/という文字列を連結した要素の値を指定します。「幅」プロパティにはダブル型に変換した、要素の属性”幅”の値を指定します。「高さ」プロパティには、ダブル型に変換した、要素の属性”高さ”の値を指定します。
これらの値の指定されたFashionInfoクラスを、AddメソッドでmyFashionInfoオブジェクトに追加していきます。ListBoxのItemsSourceプロパティにmyFashionInfoオブジェクトを指定します。ListBox内に服の一覧が表示されます。

fashionImageのSourceプロパティに0番目の要素の値を、画像を配置しているImage/というフォルダ名と連結して指定します。Widthプロパティには0番目の要素の属性”幅”を指定し、Heightプロパティには、0番目の要素の属性”高さ”を指定します。fashionImage内にListBoxのインデックスが0番目の画像(先頭の画像)が表示されます。

Kinectセンサーを取得し、Kinectを動作させます。

要素を構成するための代替項目の一覧を表すChoicesクラスのインスタンス、sentenceオブジェクトを作成します。Choiceクラスは、GrammarBuilder オブジェクトからのみ直接使用されます。Addメソッドで、認識させる言葉として「上」「下」「消去」「おわり」という語句をChoicesオブジェクトであるsentenceに追加します。
GrammarBuilderクラスは、単純な入力から複雑な Grammar(構文情報を取得管理するクラス)を構築するためのメカニズムを提供するクラスで、登録された言葉の構文(文法)設定を行い、SpeechRecognitionEngineへと設定します。
Appendメソッドで、登録した言葉を GrammarBuilder オブジェクトとして現在の GrammarBuilder に追加します。音声認識サービスを実行するためのアクセス権を提供する、SpeechRecognitionEngineクラスの新しいインスタンスを作成します。

文法のチェックされた言葉(builder)で初期化された、新しいGrammerクラスのインスタンス、myGrammerオブジェクトを作成します。Grammerクラスは、構文情報を取得および管理するためにランタイムをサポートするクラスです。

SpeechRecognitionEngineクラスのLoadGrammerメソッドで、Grammar によって指定されたとおりに、特定の構文を同期的に読み込みます。

言葉が認識された際には、AddHandlerステートメントで言葉を認識した際に発生するSpeechRecognizedイベントに、イベントハンドラを指定します。

Confidenceプロパティで音声認識の信頼度を設定します。-1が低、0が標準、1が高信頼度となります。-1を指定するとどんな言葉にでも反応する恐れがあります。1を指定するとなかなか認識してくれません。今回は信頼度が0.5より大きい場合に、指定した言葉を認識するよう指定しています。

認識された音声(speechArgs.Result.Text)を変数wordsに格納します。wordsの内容で条件分岐を行います。

「上」と発声された場合は、メンバ変数indexを1ずつ減算します。ListBoxの選択されたインデックスにindexの値を指定します。fashionImageのSourceプロパティにindex番目の要素の値を、画像を配置しているImage/というフォルダ名と連結して指定します。
Widthプロパティにはindex番目の要素の属性”幅”を指定し、Heightプロパティには、index番目の要素の属性”高さ”を指定します。fashionImage内にListBoxのインデックスがindex番目の画像が表示されます。

「下」と発声された場合は、メンバ変数indexを1ずつ増加します。ListBoxの選択されたインデックスにindexの値を指定します。fashionImageのSourceプロパティにindex番目の要素の値を、画像を配置しているImage/というフォルダ名と連結して指定します。
Widthプロパティにはindex番目の要素の属性”幅”を指定し、Heightプロパティには、index番目の要素の属性”高さ”を指定します。fashionImage内にListBoxのインデックスがindex番目の画像が表示されます。

「消去」と発声された場合は、fashionImageを非表示にします。

「おわり」と発声された場合は、Kinectセンサーを停止し、音声認識も停止して、Environment.Exit(0)でプログラムを終了します。

Kinectの音声インターフェースは、Kinect.AudioSourceで提供されます。Startメソッドで音声入力を開始します。入力ストリームを取得し、SpeechRecognitionEngine クラスのSetInputToDefaultAudioDeviceメソッドで、SpeechRecognitionEngine の現在のインスタンスに、システム既定のオーディオ入力を割り当てます。

認識操作の後に、RecognizeAsync によって開始された認識を終了しないよう、RecognizeMode.Multipleを指定して、RecognizeAsyncメソッドで非同期音声認識を開始します。

スケルトン・トラッキングの移動パラメータを指定するTransformSmoothParametersクラスの各プロパティを設定します。

Smoothingで平滑化の程度を指定します。値は0~1.0の間で、デフォルトは0.5です。値が大きいほど平滑化されます。

Correctionで、平滑化の緩急を指定します。値は0~1.0の間で、デフォルトは0.5です。値が小さいほど、平滑化に緩急を付けるため動作が低下します。

Predictionでスムーズに動作させるために予測されたフレームの数を取得します。
JitterRadiusでジッタ処理の対象にする半径(メートル)を指定します。デフォルトは0.05(5cm)です。

MaxDeviationRadiusで、フィルタされた値と生データとの誤差の許容最大値(メートル)を設定します。デフォルトは0.04(4cm)です。

EnableメソッドにTransformSmoothParametersを指定して、スケルトンを有効にします。

AddHandlerステートメントで、RGBカメラ、距離カメラ、スケルトンのフレームが更新された時に発生する、AllFramesReadyイベントにkinect_AllFramesReadyイベントハンドラを追加します。

Enableメソッドで、RGBカメラ、距離カメラを有効にします。

  Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    xmldoc = XElement.Load("fashion.xml")
 
    Dim myFashionInfo As New List(Of FashionInfo)
  
    For Each result In From c In xmldoc.Descendants("画像名") Select c
      With myFashionInfo
        .Add(New FashionInfo With {.画像名 = "Image/" & result.Value,
                                   .幅 = CStr(CInt(result.Attribute("幅").Value) / 2),
                                   .高さ = CStr(CInt(result.Attribute("高さ").Value) / 2)})
      End With
    Next
    ListBox1.ItemsSource = myFashionInfo
    ListBox1.SelectedIndex = 0
    fashionImage.Visibility = Windows.Visibility.Visible
    fashionImage.Source = New BitmapImage(New Uri("Image/" & xmldoc.Descendants("画像名")(index).Value, UriKind.Relative))
    fashionImage.Width = CDbl(xmldoc.Descendants("画像名")(index).Attribute("幅").Value)
    fashionImage.Height = CDbl(xmldoc.Descendants("画像名")(index).Attribute("高さ").Value)
 
    If KinectSensor.KinectSensors.Count = 0 Then
      MessageBox.Show("Kinectを接続してください")
      Exit Sub
    Else
      kinect = KinectSensor.KinectSensors(0)
      kinect.Start()
      Dim sentence As Choices = New Choices
      With sentence
        .Add("上")
        .Add("下")
        .Add("消去")
        .Add("おわり")
      End With
 
      Dim builder As GrammarBuilder = New GrammarBuilder
      builder.Append(sentence)
      Dim myGrammer As Grammar = New Grammar(builder)
      engine = New SpeechRecognitionEngine()
      engine.LoadGrammar(myGrammer)
 
      AddHandler engine.SpeechRecognized, Sub(speechSender As Object, speechArgs As SpeechRecognizedEventArgs)
 
            words = speechArgs.Result.Text
            Dim myConfidence = speechArgs.Result.Confidence
            If myConfidence > 0.5 Then
              Select Case words
                Case "上"
                  If index <= 0 Then
                    index = 0
                  Else
                    index = index - 1
                  End If
                  ListBox1.SelectedIndex = index
                fashionImage.Visibility = Windows.Visibility.Visible
                fashionImage.Source = New BitmapImage(New Uri("Image/" & xmldoc.Descendants("画像名")(index).Value, UriKind.Relative))
                fashionImage.Width = CDbl(xmldoc.Descendants("画像名")(index).Attribute("幅").Value)
                fashionImage.Height = CDbl(xmldoc.Descendants("画像名")(index).Attribute("高さ").Value)
                  Case "下"
                    If index >= ListBox1.Items.Count - 1 Then
                       index = ListBox1.Items.Count - 1
                    Else
                       index = index + 1
                    End If
                    ListBox1.SelectedIndex = index
                    fashionImage.Visibility = Windows.Visibility.Visible
                fashionImage.Source = New BitmapImage(New Uri("Image/" & xmldoc.Descendants("画像名")(index).Value, UriKind.Relative))
                fashionImage.Width = CDbl(xmldoc.Descendants("画像名")(index).Attribute("幅").Value)
                fashionImage.Height = CDbl(xmldoc.Descendants("画像名")(index).Attribute("高さ").Value)
                  Case "消去"
                    fashionImage.Visibility = Windows.Visibility.Collapsed
                  Case "おわり"
                    If kinect Is Nothing = False Then
                      If kinect.IsRunning = True Then
                         kinect.Stop()
                         kinect.Dispose()
                         engine.RecognizeAsyncStop()
                      End If
                    End If
                      Environment.Exit(0)
                      End Select
            End If
              End Sub

            Dim audio As KinectAudioSource = kinect.AudioSource
            Using s As Stream = audio.Start()
            engine.SetInputToDefaultAudioDevice()
            engine.RecognizeAsync(RecognizeMode.Multiple)
      End Using
    End If
 
    Dim myParam As New TransformSmoothParameters
    With myParam
        .Smoothing = 0.75F 'スムージングの量を取得
        .Correction = 0.0F '補正の量を取得
        .Prediction = 0.0F '予測されたフレームの数を取得。
        .JitterRadius = 0.05F '取得またはジッタ低減 (メートル) に半径を設定。
        .MaxDeviationRadius = 0.4F 'フィルタの位置がrawデータから逸脱できる最大半径を取得
    End With
    kinect.SkeletonStream.Enable(myParam)

 
    AddHandler kinect.AllFramesReady, AddressOf kinect_AllFramesReady
    kinect.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30)
    kinect.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30)
  End Sub
  • 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メルマガ会員のサービス内容を見る

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