Kinect v2の音声認識で「仮面」を選んで変身してみる
2015年1月6日(火)

音声認識処理
リスト6:MainWindow.xaml.vbの一部、リスト5の続き
01 | Private Sub speechEngine_SpeechRecognized(sender As Object, e As SpeechRecognizedEventArgs) |
02 | Const myConfidence As Double = 0.6 |
03 | If e.Result.Confidence >= myConfidence Then (1) |
04 | Select Case e.Result.Text |
05 | Case "うえ" (2) |
06 | If Index <= 0 Then |
07 | Index = 0 |
08 | Exit Sub |
09 | Else |
10 | Index -= 1 |
11 | End If |
12 | ListBox1.SelectedIndex = Index (4) |
13 | ListBox1.ScrollIntoView(ListBox1.SelectedItem) (5) |
14 | Case "した" (3) |
15 | If Index >= ListBox1.Items.Count - 1 Then |
16 | Index = ListBox1.Items.Count - 1 |
17 | Exit Sub |
18 | Else |
19 | Index += 1 |
20 | End If |
21 | ListBox1.SelectedIndex = Index (4) |
22 | ListBox1.ScrollIntoView(ListBox1.SelectedItem) (5) |
23 | End Select |
24 | End If |
25 | End Sub |
- Confidenceプロパティで音声認識の信頼度を設定します。低信頼度の「-1」から高信頼度の「1」の間の値をとり、標準は「0」となります。-1を指定すると、どんな言葉でも反応する恐れがあります。一方1を指定すると、なかなか認識されません。今回は、信頼度が0.6より大きい場合に言葉を認識するよう指定しています。
- プレイヤーが「うえ」とセンサーに向かって発生した場合の処理です。メンバー変数Indexの値が、1ずつ減少します。
- プレイヤーが「した」とセンサーに向かって発生した場合の処理です。メンバー変数Indexの値が、1ずつ加算されます。
- ListBox1の選択されたインデックスにIndex変数の値を指定することで、Indexに該当するマスクが選択されます。
- ScrollIntoViewメソッドで選択された画像位置まで移動しますが、今回はListBoxに3個全ての画像が表示されているので、移動は確認できません。例えばListBoxに2個のマスクだけが表示されていて、残り1個が下に隠れて見えない場合は、3個目のマスクが指定された場合は、その位置までスクロールします。
新しいボディフレームの準備ができているときに発生するイベント処理
リスト7:MainWindow.xaml.vbの一部、リスト6の続き
01 | Private Sub myBodyFrameReader_FrameArrived(sender As Object, e As BodyFrameArrivedEventArgs) |
02 | CanvasBody.Children.Clear() (1) |
03 | CanvasBody2.Children.Clear() |
04 | Using myBodyFrame = e.FrameReference.AcquireFrame (2) |
05 | If myBodyFrame Is Nothing = True Then |
06 | Return |
07 | End If |
08 | myBodyFrame.GetAndRefreshBodyData(myBodies) (3) |
09 | End Using |
10 | For Each body In myBodies |
11 | For Each joint In body.Joints (4) |
12 | If joint.Value.TrackingState = TrackingState.Tracked Then |
13 | DrawEllipse(joint.Value, 10, Brushes.Transparent) (5) |
14 | End If |
15 | Next |
16 | Next |
17 | End Sub |
- CanvasBodyとCanvasBody2内を一度クリアしておきます。
- e.FrameReference.AcquireFrameメソッドで、ボディフレームを取得し、myBodyFrameで参照します。myBodyFrameに何も設定されていなかった場合は、何も行いません。
- GetAndRefreshBodyDataメソッドで、更新されたボディデータを取得します。
- ボディデータの中を、反復処理を行いながら、繰り返し変数jointで、関節の位置を反復処理しながら、関節位置を取得していきます。
- 関節が追跡されている場合は、関節の位置、関節の位置を描く円の大きさ(10を指定)と円の色(透明)を指定して、DrawEllipseを実行します。
ウインドウが閉じられる場合の処理
リスト8:MainWindow.xaml.vbの一部、リスト7の続き
1 | Private Sub MainWindow_Closing(sender As Object, e As ComponentModel.CancelEventArgs) Handles Me.Closing |
2 | If myKinect Is Nothing = False Then |
3 | myKinect.Close() |
4 | myKinect = Nothing |
5 | End If |
6 | End Sub |
Kinectが動作している場合は、Kinectを閉じ、全ての関連付けから解放します。
ListBoxからマスクが選択された時の処理
リスト9:MainWindow.xaml.vbの一部、リスト8の続き
1 | Private Sub ListBox1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ListBox1.SelectionChanged |
2 | myMask = DirectCast(ListBox1.SelectedItem, Mask).画像名 |
3 | End Sub |
メンバー変数myMaskに、ListBox1から選択された項目を、Maskクラスにキャストして、その「画像名」プロパティの値を取得して格納します。
関節の位置に透明の円を表示する処理
リスト10:MainWindow.xaml.vbの一部、リスト9の続き
01 | Private Sub DrawEllipse(myJoint As Joint, R As Integer, myBrush As Brush) |
02 | Dim myEllipse = New Ellipse |
03 | With myEllipse |
04 | .Width = R |
05 | .Height = R |
06 | .Fill = myBrush |
07 | End With |
08 |
09 | point = myKinect.CoordinateMapper.MapCameraPointToDepthSpace(myJoint.Position) (1) |
10 | If point.X < 0 OrElse point.Y < 0 Then |
11 | Return |
12 | End If |
13 | Canvas.SetLeft(myEllipse, point.X - (R / 2)) (2) |
14 | Canvas.SetTop(myEllipse, point.Y - (R / 2)) |
15 | If myJoint.JointType = JointType.Head AndAlso myMask <> String.Empty Then |
16 | myImage = New Image (3) |
17 | With myImage (4) |
18 | .Width = 60 |
19 | .Height = 75 |
20 | .Source = New BitmapImage(New Uri(myMask, UriKind.Relative)) |
21 | End With |
22 | Canvas.SetLeft(myImage, point.X - 30) (5) |
23 | Canvas.SetTop(myImage, point.Y - 30) |
24 | CanvasBody2.Children.Add(myImage) |
25 | End If |
26 | CanvasBody.Children.Add(myEllipse) (6) |
27 | End Sub |
透明な直径10ピクセルの円を作成します。直径はDrawEllipseを呼ぶ際に指定しています(リスト7)。
- CoordinateMapper.MapCameraPointToDepthSpaceメソッドで、関節の位置を、カメラ空間から距離空間へのポイントにマップし、メンバー変数pointで参照します。
- SetLeftとSetTopメソッドで透明な円を、指定したXとY軸に描きます。
関節が頭部であり、かつメンバー変数myMaskの値が空ではなかった場合には、以下の処理を行います。 - 新しいImageクラスのインスタンスmyImageオブジェクトを生成します。
- myImageオブジェクトのWidthとHeightを指定し、Sourceプロパティに、メンバー変数myMaskの値を指定します。
- SetLeftとSetTopメソッドで、選択されたマスクを頭部に配置します。「-30」は位置を調整している値です。
- CanvasBodyには、透明化された円がプレイヤーの関節部分に描画されます。目には見えません。
カラーフレーム到着時のイベント
リスト11:MainWindow.xaml.vbの一部、リスト10の続き
01 | Private Sub myColorFrameReader_FrameArrived(sender As Object, e As ColorFrameArrivedEventArgs) |
02 | Dim colorFrameProcessed As Boolean = False |
03 | Using myColorFrame As ColorFrame = e.FrameReference.AcquireFrame (1) |
04 | If myColorFrame Is Nothing = False Then |
05 | Dim myColorFrameDescription As FrameDescription = myColorFrame.FrameDescription (2) |
06 | If myColorFrameDescription.Width = colorBitmap.PixelWidth AndAlso myColorFrameDescription.Height = colorBitmap.PixelHeight Then (3) |
07 | If myColorFrame.RawColorImageFormat = ColorImageFormat.Bgra Then (4) |
08 | myColorFrame.CopyRawFrameDataToArray(ColorImagePixelData) |
09 | Else |
10 | myColorFrame.CopyConvertedFrameDataToArray(ColorImagePixelData, ColorImageFormat.Bgra) |
11 | End If |
12 | colorFrameProcessed = True (5) |
13 | End If |
14 | End If |
15 | End Using |
16 | If colorFrameProcessed = True Then |
17 | colorBitmap.WritePixels(New Int32Rect(0, 0, colorBitmap.PixelWidth, colorBitmap.PixelHeight), ColorImagePixelData, colorBitmap.PixelWidth * BytesPerPixel, 0) (6) |
18 | roomImage.Source = colorBitmap (7) |
19 | End If |
20 | End Sub |
21 | End Class |
- e.FrameReference.AcquireFrameメソッドでカラーフレームを取得し、myColorFrameで参照します。
- myColorFrameにデータがあった場合、KinectSensor からイメージフレームのプロパティを取得し、変数myColorFrameDescriptionで参照します。
- 取得したプロパティの幅と高さがcolorBitmapのPixelWidth、PixelHeightと同じであった場合、以下の処理を行います。
- カラーフレームデータがBgra形式であった場合は、CopyRawFrameDataToArrayメソッドで、Raw フレームのデータ指定された配列(ColorImagePixelData)にコピーします。そうでない場合は、CopyConvertedFrameDataToArrayメソッドでBgra形式に変換してから、バイト配列に格納します。
- Boolean型で宣言していた変数colorFrameProcessedをTrueで初期化します。
- 変数colorFrameProcessed変数がTrueであった場合は、colorBitmap(WriteableBitmapクラス)のWritePixelsメソッドで、ビットマップの指定した領域内のピクセルを更新します。
CopyConvertedFrameDataToArrayメソッドやWritePixelsメソッドの書式については、連載の第3回目を参照してください。7.roomImageのSourceプロパティにcolorBitmapオブジェクトを指定します。これでRGBカメラからの画像が表示されます。
実際に動作させると動画1のようになります。実際には「うえ」、「した」と喋ってマスクを選択していますが、音声は録音されておりません。ご了承ください。
連載バックナンバー
Think ITメルマガ会員登録受付中
Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。
全文検索エンジンによるおすすめ記事
- Kinect v2を使って、机の上のリンゴをつかんで移動してみる
- Kinect v2で実現する打楽器のバーチャル演奏
- Kinect v2のカメラから画像を取り込んで表示する基本プログラム
- Kinect v2を使った「じゃんけんゲーム」を作る
- Kinect v2のジェスチャーでBing Mapsを未来的に直感操作する
- Kinect v2のIRセンサーから赤外線画像を読み込む
- Kinect v2の深度センサーから取り込んだ画像を表示する
- Kinectの音声認識を使ってWebブラウザを操作するサンプル
- 声で選んだアイテムをプレイヤーの身体に装着・連動させるKinectサンプル
- KinectButtonを動的に作成して、ジェスチャーで文字を表示させるサンプル