人体の連続した動作を音声でキャプチャするKinectのサンプルプログラム

2012年9月21日(金)
薬師寺 国安

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

リスト2 (MainWindow.xaml.vb)

Option Strict On
Imports Microsoft.Kinect
Imports Microsoft.Speech.AudioFormat
Imports Microsoft.Speech.Recognition
Imports System.IO
Imports System.Windows.Media.Imaging
Class MainWindow

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

  Dim Kinect As KinectSensor

整数四角形の幅、高さ、および位置を表すInt32Rect構造体のメンバ変数myScreenImageRectを宣言します。領域の指定に使用されます。

  Dim myScreenImageRect As Int32Rect

Short型の配列メンバ変数myDepthPixelDataとByte型の配列メンバ変数myColorPixelDataを宣言しておきます。

※深度情報は、1ピクセルあたり2バイトのshort型。画像情報はフルカラーなので1ピクセルあたり4バイトのbyte型です。

  Dim myDepthPixelData As Short()
  Dim myColorPixelData As Byte()

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

  Dim engine As SpeechRecognitionEngine

作成される画像の個数を表すメンバ変数noを宣言します。

  Dim no As Integer = 0

深度を指定する定数メンバ変数targetDepthを宣言し、2000(2m)で初期化しておきます。Kinectセンサーからの距離を2mとって動作確認をする必要があります。

  Const targetDepth As Double=2000

Imageクラス型のmyImageメンバ変数を宣言します。

  Dim myImage As Image

認識された言葉を格納するメンバ変数wordsを宣言します。

  Dim words As String

WriteableBitmapクラス型のHuman1_bitmap、Human2_bitmap、Room_Bitmapプロパティを定義しておきます。

  Property Human1_bitmap As WriteableBitmap
  Property Human2_bitmap As WriteableBitmap
  Property Room_Bitmap As WriteableBitmap

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

システムの特別なフォルダである「マイピクチャ」へのディレクトリパスを変数dirに格納します。「マイピクチャ」内に「KINECT_Separate」というサブフォルダがなかった場合は、CreateDirectoryメソッドで「KINECT_Separate」というサブフォルダを作成します。

Choicesクラスは、要素を構成するための代替項目の一覧を表すクラスで、GrammarBuilder オブジェクトからのみ直接使用されます。認識させる言葉をAddメソッドで登録します。

GrammarBuilderクラスは、単純な入力から複雑な Grammar(構文情報を取得管理するクラス)を構築するためのメカニズムを提供するクラスで、登録された言葉の構文(文法)設定を行い、SpeechRecognitionEngineへと設定します。Appendメソッドで、登録した言葉を GrammarBuilder オブジェクトとして現在の GrammarBuilder に追加します。

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

次に、SpeechRecognitionEngineクラスの新しいインスタンスengineオブジェクトを作成します。
SpeechRecognitionEngineクラスのLoadGrammerメソッドで、Grammar によって指定された通りに、特定の構文を同期的に読み込みます。

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

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

言葉が認識されたら、AddHandlerステートメントで言葉を認識した際に発生するSpeechRecognizedイベントに、イベントハンドラを指定します。認識された音声(speechArgs.Result.Text)を変数wordsに格納します。Confidenceプロパティで音声認識の信頼度を設定します。-1が低、0が標準、1が高信頼度となります。-1を指定するとどんな言葉にでも反応する恐れがあります。1を指定するとなかなか認識してくれません。今回は信頼度が0.5より大きい場合に設定しています。

ここでは、「わかれる」と発声された場合の処理は不要です。「わかれる」と発声した場合の処理は、CompositionTarget.Renderingイベント内で行います。

「くっつく」と発声された場合は、personImageというCanvas内をクリアします。メンバ変数noを0で初期化します

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

RGBカメラ、距離カメラ、スケルトンを有効にし、各画像データを初期化するinit_kinectプロシージャを実行します。

構成ツリーのオブジェクトがレンダリングされる直前に発生する、CompositionTarget.Renderingイベントにイベントハンドラを指定します。イベントハンドラ内では以下の処理を行います。

OpenNextFrame(100)メソッドで、KinectからRGBデータの次のフレームを開きます。次のフレームがなかった場合のタイムアウトを100ミリセコンドと指定しています。同様に、Kinectから深度データの次のフレームを開きます。次のフレームがなかった場合のタイムアウトを100ミリセコンドと指定しています。

背景を描画し距離データを取得するRenderScreenプロシージャを実行します。引数として、Kinectのストリーミング用RGBデータのバッファと深度データのバッファを含んでいる、colorFrameとdepthFrameを渡しています。

「わかれる」と発声された場合は、プレイヤーの動作を保存するSeparateHumanプロシージャを実行します。「中止」と発声された場合は、depthFrameオブジェクトのリソースを解放します。DataContextプロパティにMainWindow自身のインスタンスを指定します。この処理を行わないとプレイヤーが表示されませんので注意してください。

  Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
  Dim dir As String= Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)
 
  If Directory.Exists(Path.Combine(dir, "KINECT_Separate")) = False Then
    Directory.CreateDirectory(Path.Combine(dir, "KINECT_Separate"))
  End If
  If KinectSensor.KinectSensors.Count = 0 Then
    MessageBox.Show("Kinectが接続されておりません。")
    Exit Sub
  End If
 
  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)
    Try
      words = speechArgs.Result.Text
      Dim confidence = speechArgs.Result.Confidence
      If confidence > 0.5 Then
        Select Case words
          Case "くっつく"
              personImage.Children.Clear()
              no = 0
          Case "おわり"
              personImage.Children.Clear()
              If Kinect Is Nothing = False Then
                If Kinect.IsRunning = True Then
                   Kinect.Stop()
                   Kinect.AudioSource.Stop()
                   engine.RecognizeAsyncStop()
                   Kinect.Dispose()
                   End If
                End If
                   Environment.Exit(0)
        End Select
      End If
    Catch
        Exit Sub
    End Try
  End Sub
 
  Kinect = KinectSensor.KinectSensors(0)
  Kinect.Start()
  Dim audio As KinectAudioSource = Kinect.AudioSource
 
  Using s As Stream = audio.Start()
    engine.SetInputToDefaultAudioDevice()
    engine.RecognizeAsync(RecognizeMode.Multiple)
  End Using
 
  targetDepth = 2000 
  init_kinect()
 
  AddHandler CompositionTarget.Rendering, Sub(renderSender As Object, renderArgs As EventArgs)
    Using colorFrame As ColorImageFrame = Kinect.ColorStream.OpenNextFrame(100)
      Using depthFrame As DepthImageFrame = Kinect.DepthStream.OpenNextFrame(100)
        RenderScreen(colorFrame, depthFrame)
          If words = "わかれる" Then
            SeparateHuman()
          ElseIf words = "中止" Then
            depthFrame.Dispose()
          End If
        End Using
      End Using
    End Sub
    DataContext = Me
  End Sub

プレイヤーの動作を保存する処理

「わかれる」と発声された場合は、システムの特別なフォルダである「マイピクチャ」へのディレクトリパスを変数dirに格納しておきます。「マイピクチャ」内の「KINECT_Separate」サブフォルダへのディレクトリパスを変数saveDirに格納します。
BitmapFrameクラスのbmpFrame変数を宣言します。BitmapFrameクラスは、デコーダによって返されてエンコーダによって受け入れられるイメージ データを表すクラスです。

BitmapFrame.CreateメソッドでHuman2_bitmapプロパティからBitmapFrameを作成します。1ずつ加算されるメンバ変数noと、文字列dummyと.pngを連結したファイル名と、新規作成モードで指定した形式で初期化された、FileStreamのインスタンスstreamオブジェクトを作成します。
新しいPngBitmapEncoderクラスのインスタンスmyPngオブジェクトを作成します。PngBitmapEncodeクラスは、PNG形式のイメージのエンコードに使用されるエンコーダを定義するクラスです。
myPngオブジェクトのイメージ内の個別のフレームに、Addメソッドで作成した画像ファイルを追加していきます。SaveメソッドでHuman2_bitmapを「マイピクチャ」の「KINECT_Separate」サブフォルダ内に保存します。ファイル名はdummy0.png、dummy1.png・・・・・dummy50.pngといった形式になります。保存される画像はPNG形式で背景が透明化された画像になります。

新しいImageクラスのインスタンスmyImageオブジェクトを作成します。WidthとHeightプロパティを設定し、Sourceプロパティに、先ほど保存した「KINECT_Separate」サブフォルダ内の画像を指定します。この場合は必ずUriKind.Absoluteと指定し、絶対URIで指定します。personImageというCanvasに、AddメソッドでmyImageオブジェクトを追加します。メンバ変数noの値を1ずつ増加させると、保存されていた画像が順次表示されます。

  Private Sub SeparateHuman()
    Dim dir As String = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)
    Dim saveDir As String = Path.Combine(Dir, "KINECT_Separate")
 
    Dim bmpFrame As BitmapFrame = BitmapFrame.Create(Human2_bitmap)
    Using stream As FileStream = New FileStream(Path.Combine(saveDir, "dummy" & no & ".png"), FileMode.Create)
      Dim myPng As PngBitmapEncoder = New PngBitmapEncoder
      myPng.Frames.Add(bmpFrame)
      myPng.Save(stream)
    End Using
 
    myImage = New Image
    With myImage
      .Width = 640
      .Height = 480
      .Source = New BitmapImage(New Uri(Path.Combine(saveDir, "dummy" & no & ".png"), UriKind.Absolute))
  End With
  personImage.Children.Add(myImage)
  no = no + 1    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メルマガ会員のサービス内容を見る

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