Kinectの音声認識を使って、プレイヤーを分離、結合させるデモを試してみる
※前ページからの続きです。
ウィンドウが読み込まれた時の処理
最初は1人が表示されるようにspeechNoを1で初期化しています。
ピクセル深度の値を2000(2m)で初期化しておきます。よって、Kinectセンサーより2mの距離を取ってお試しください。Kinectセンサーを取得し、ビットマップデータを初期化し、プレイヤーの画層を初期化するinit_kinectプロシージャを実行します。
構成ツリーのオブジェクトがレンダリングされる直前に発生する、CompositionTarget.Renderingイベントにイベントハンドラを指定します。イベントハンドラ内では以下の処理を行います。
OpenNextFrame(100)メソッドで、KinectからRGBデータの次のフレームを開きます。次のフレームがなかった場合のタイムアウトを100ミリセコンドと指定しています。同様に、Kinectから深度データの次のフレームを開きます。次のフレームがなかった場合のタイムアウトを100ミリセコンドと指定しています。スケルトンフレームに関しても同じ処理を行います。
分身の数を保持する変数maxPlayerにspeechNoの値を代入します。
背景を描画し距離データを取得するRenderScreenプロシージャを実行します。引数として、Kinectのストリーミング用RGBデータのバッファと深度データのバッファを含んでいる、colorFrameとdepthFrameを渡しています。
RGBデータのバッファ、深度データのバッファ、スケルトンデータのバッファを引数に、SaveBufferプロシージャを実行します。
DataContextプロパティにMainWindow自身のインスタンスを指定します。この処理を行わないとプレイヤーが表示されませんので注意してください。
Choicesクラスは、要素を構成するための代替項目の一覧を表すクラスで、GrammarBuilder オブジェクトからのみ直接使用されます。認識させる言葉をAddメソッドで登録します。
GrammarBuilderクラスは、単純な入力から複雑な Grammar(構文情報を取得管理するクラス)を構築するためのメカニズムを提供するクラスで、登録された言葉の構文(文法)設定を行い、SpeechRecognitionEngineへと設定します。Appendメソッドで、登録した言葉を GrammarBuilder オブジェクトとして現在の GrammarBuilder に追加します。
文法のチェックされた言葉(builder)で初期化された、新しいGrammerクラスのインスタンス、myGrammerオブジェクトを作成します。Grammerクラスは、構文情報を取得および管理するためにランタイムをサポートするクラスです。
次に、SpeechRecognitionEngineクラスの新しいインスタンスengineオブジェクトを作成します。
SpeechRecognitionEngineクラスのLoadGrammerメソッドで、Grammar によって指定された通りに、特定の構文を同期的に読み込みます。
Kinectの音声インターフェースは、Kinect.AudioSourceで提供されます。Startメソッドで音声入力を開始します。入力ストリームを取得し、SpeechRecognitionEngine クラスのSetInputToDefaultAudioDeviceメソッドで、SpeechRecognitionEngine の現在のインスタンスに、システム既定のオーディオ入力を割り当てます。
認識操作の後に、RecognizeAsync によって開始された認識を終了しないよう、RecognizeMode.Multipleを指定して、RecognizeAsyncメソッドで非同期音声認識を開始します。
言葉が認識された際には、AddHandlerステートメントで言葉を認識した際に発生するSpeechRecognizedイベントに、イベントハンドラを指定します。認識された音声(speechArgs.Result.Text)を変数wordsに格納します。
Confidenceプロパティで音声認識の信頼度を設定します。-1が低、0が標準、1が高信頼度となります。-1を指定するとどんな言葉でも反応する恐れがあります。1を指定するとなかなか認識してくれません。今回は信頼度が0.3より大きい場合に設定しています。
wordsの内容で条件分岐を行いますwordsの値が「おわり」の場合はKinectセンサーの動作を停止し、音声認識も停止し、Environment.Exit(0)でプログラムを終了します。
「分身」の場合は、OpenNextFrame(100)メソッドで、KinectからRGBデータの次のフレームを開きます。次のフレームがなかった場合のタイムアウトを100ミリセコンドと指定しています。同様に、Kinectから深度データの次のフレームを開きます。次のフレームがなかった場合のタイムアウトを100ミリセコンドと指定しています。
スケルトンフレームに関しても同じ処理を行います。speechNoの値が6より大きい場合、分身が6名になった場合は警告メッセージを表示します。それ以外は、speechNoの値を1ずつ加算し、背景を描画し距離データを取得するRenderScreenプロシージャを実行します。
引数として、Kinectのストリーミング用RGBデータのバッファと深度データのバッファを含んでいる、colorFrameとdepthFrameを渡しています。RGBデータのバッファ、深度データのバッファ、スケルトンデータのバッファを引数に、SaveBufferプロシージャを実行します。
「統合」の場合は、OpenNextFrame(100)メソッドで、KinectからRGBデータの次のフレームを開きます。次のフレームがなかった場合のタイムアウトを100ミリセコンドと指定しています。同様に、Kinectから深度データの次のフレームを開きます。次のフレームがなかった場合のタイムアウトを100ミリセコンドと指定しています。スケルトンフレームに関しても同じ処理を行います。
speechNoを1で初期化し、プレイヤーの表示を1人だけとします。背景を描画し距離データを取得するRenderScreenプロシージャを実行します。引数として、Kinectのストリーミング用RGBデータのバッファと深度データのバッファを含んでいる、colorFrameとdepthFrameを渡しています。RGBデータのバッファ、深度データのバッファ、スケルトンデータのバッファを引数に、SaveBufferプロシージャを実行します。
Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded speechNo = 1 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) Using mySkeletonFrame As SkeletonFrame = Kinect.SkeletonStream.OpenNextFrame(100) maxPlayer = speechNo RenderScreen(colorFrame, depthFrame) SaveBuffer(colorFrame, depthFrame, mySkeletonFrame) End Using End Using End Using End Sub DataContext = Me Dim sentence As Choices = New Choices With sentence .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) Dim myConfidence As Single = speechArgs.Result.Confidence If myConfidence > 0.3 Then words = speechArgs.Result.Text Select Case words Case "おわり" If Kinect Is Nothing = False Then If Kinect.IsRunning = True Then Kinect.Stop() Kinect.AudioSource.Stop() engine.RecognizeAsyncStop() End If Environment.Exit(0) End If Case "分身" Using colorFrame As ColorImageFrame = Kinect.ColorStream.OpenNextFrame(100) Using depthFrame As DepthImageFrame = Kinect.DepthStream.OpenNextFrame(100) Using mySkeletonFrame As SkeletonFrame = Kinect.SkeletonStream.OpenNextFrame(100) If speechNo >= 6 Then TextBlock1.Text = "これ以上は分身できません。" Exit Sub Else speechNo = speechNo + 1 End If RenderScreen(colorFrame, depthFrame) SaveBuffer(colorFrame, depthFrame, mySkeletonFrame) End Using End Using End Using Case "統合" Using colorFrame As ColorImageFrame = Kinect.ColorStream.OpenNextFrame(100) Using depthFrame As DepthImageFrame = Kinect.DepthStream.OpenNextFrame(100) Using mySkeletonFrame As SkeletonFrame = Kinect.SkeletonStream.OpenNextFrame(100) TextBlock1.Text = String.Empty speechNo = 1 RenderScreen(colorFrame, depthFrame) SaveBuffer(colorFrame, depthFrame, mySkeletonFrame) End Using End Using End Using Case Else Exit Select 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 Sub
Kinectセンサーを取得し、ビットマップデータを初期化し、プレイヤーの画層を初期化する処理
Kinectセンサーを取得します。DepthStream.Enableメソッドで距離カメラの表示サイズを320×240、1秒あたり30フレームで、動作を開始します。表示サイズを640×480に設定すると描画が重くて動かなくなるので注意してください。同様にColorStream.Enableメソッドで、RGBカメラを、RGBフォーマット、表示サイズ640×480、1秒あたり30フレームで、動作を開始します。プレイヤーおよびスケルトンの認識も開始します。
骨格生成を行う場合は、深度センサー(距離カメラ)を有効にする必要があります。Dim depthStream As DepthImageStream = Kinect.DepthStreamで、距離カメラの処理を行うクラスのインスタンスを取得します。Short配列変数myDepthPixelDataに距離カメラのピクセルデータのバイト長分の配列を作成します。320×240の表示サイズの場合は、myDepthPixelData=New Short(76800-1){}分の配列を確保することになります。-1しているのは配列のインデックスは0から始まるためです。バイト配列変数myColorPixelDataにRGBカメラのピクセルデータのバイト長分の配列を作成します。
WriteableBitmapクラス型のmyOverrayBitmapを下記の書式で初期化します。
New WriteableBitmap(距離カメラのフレーム幅,距離カメラのフレーム高さ, ビットマップの水平値, ビットマップの垂直値,ビットマップのピクセルフォーマット,ビットマップのビットマップパレット)
「ビットマップのピクセルフォーマット」に指定する、PixelFormats.Bgra32は、Bgra32 ピクセル形式を取得します。ビットマップのピクセル形式には、PixelFormats.Bgra32を指定します。Bgra32 は、bits per pixel (BPP) が 32 の sRGB 形式です。各カラー チャネル(青、緑、赤、およびアルファ)に割り当てられるbits per pixel (BPP) は 8 です。ここに、Brg32を指定すると背景が透明化されず真っ黒になってしまいますので注意してください。
ここは、myOverrayBitmap = New WriteableBitmap(320 240, 96, 96, PixelFormats.Bgra32, Nothing)
と指定しても同じです。
IntRect32構造体のmyScreenImageRectを下記の書式で初期化し、領域を指定します。
New IntRect32(新しいInt32Rect のインスタンスの X座標,新しいInt32Rect のインスタンスの Y座標, 四角形の幅を指定した新しい Int32Rect のインスタンスの幅, 四角形の高さを指定した新しい Int32Rect のインスタンスの高さ)
「新しいInt32Rect のインスタンスの X座標」と「新しいInt32Rect のインスタンスの Y座標」には0を指定します。「四角形の幅を指定した新しい Int32Rect のインスタンスの幅」には、overrayBitmapオブジェクトの幅を指定し、「Int32Rect のインスタンスの高さ」には、myOverrayBitmapオブジェクトの高さを指定します。
結局は、
myScreenImageRect = New Int32Rect(0, 0, 320, 240)
と指定しても同じことです。
リングバッファを初期化します。RingBuffer構造体のinit_ringbuffer関数を使用します。書式は下記の通りです。
init_ringbuffer(秒数分のバッファ領域(この場合は3), RGBストリームの画素数, 深度ストリームの画素数) As Boolean
WriteableBitmapクラス型のRoom_BitmapをmyOverrayBitmapと同じ値で初期化し、myScreenImageRect2をRoom_Bitmap.WidthとRoom_Bitmap.Heightで初期化します。
myScreenImageRect2 = New Int32Rect(0, 0, 320, 240)と同じです。
Kinectセンサーを動作します。
Int32Rect構造体のmyImageSizeのWidthプロパティにRGBストリームのフレームの幅(640)を指定します。同じくHeightプロパティにはRGBストリームのフレームの高さ(480)を指定します。画像のサイズを取得します。
Private Sub init_kinect() Try If KinectSensor.KinectSensors.Count = 0 Then MessageBox.Show("KINECTが接続されておりません。") Exit Sub Else Kinect = KinectSensor.KinectSensors(0) End If Kinect.DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30) '640×480では重くて動かない。 Kinect.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30) Kinect.SkeletonStream.Enable() Dim depthStream = Kinect.DepthStream myDepthPixelData = New Short(Kinect.DepthStream.FramePixelDataLength - 1) {} myColorPixelData = New Byte(Kinect.ColorStream.FramePixelDataLength - 1) {} myOverrayBitmap = New WriteableBitmap(depthStream.FrameWidth, depthStream.FrameHeight, 96, 96, PixelFormats.Bgra32, Nothing) myScreenImageRect = New Int32Rect(0, 0, CInt(Math.Ceiling(myOverrayBitmap.Width)), CInt(Math.Ceiling(myOverrayBitmap.Height))) myRingBuffer.init_ringbuffer(bufferSecond, Kinect.ColorStream.FramePixelDataLength, Kinect.DepthStream.FramePixelDataLength) Room_Bitmap = New WriteableBitmap(depthStream.FrameWidth, depthStream.FrameHeight, 96, 96, PixelFormats.Bgra32, Nothing) myScreenImageRect2 = New Int32Rect(0, 0, CInt(Math.Ceiling(Room_Bitmap.Width)), CInt(Math.Ceiling(Room_Bitmap.Height))) Kinect.Start() myImageSize.Width = Kinect.ColorStream.FrameWidth myImageSize.Height = Kinect.ColorStream.FrameHeight Catch ex As Exception MessageBox.Show(ex.Message) Exit Sub End Try End Sub
Kinectの音声認識でプレイヤーを分離、結合するサンプル
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Kinectで結成したマイ・ダンスチームを、サンプルを見ながら実際の背景に合成してみよう
- Kinectを使って、画面上の赤い輪をくぐるサンプル
- これであなたもダンスグループの一員!?Kinectで自分を分身させるプログラムを作る
- Kinectを使って、自分の手のひらに小さな分身を出現させてみる
- 人体の連続した動作を音声でキャプチャするKinectのサンプルプログラム
- プレイヤーの身体パーツを判別するKinectサンプル
- Kinectで距離カメラの値を取得して、指定した距離で人物が背景に溶け込むサンプル
- 人物を切り抜いて画面に表示するKinectサンプル
- Kinectを使って、顔の動きを認識して画面に表示する
- Kinect v2のカメラから画像を取り込んで表示する基本プログラム