音声認識処理

リスト6:MainWindow.xaml.vbの一部、リスト5の続き

Private Sub speechEngine_SpeechRecognized(sender As Object, e As SpeechRecognizedEventArgs)
    Const myConfidence As Double = 0.6
    If e.Result.Confidence >= myConfidence Then   (1)
        Select Case e.Result.Text
            Case "うえ"   (2)
                If Index <= 0 Then
                    Index = 0
                    Exit Sub
                Else
                    Index -= 1
                End If
                ListBox1.SelectedIndex = Index   (4)
                ListBox1.ScrollIntoView(ListBox1.SelectedItem)    (5)
            Case "した"   (3)
                If Index >= ListBox1.Items.Count - 1 Then
                    Index = ListBox1.Items.Count - 1
                    Exit Sub
                Else
                    Index += 1
                End If
                ListBox1.SelectedIndex = Index   (4)
                ListBox1.ScrollIntoView(ListBox1.SelectedItem)    (5)
        End Select
    End If
End Sub
  1. Confidenceプロパティで音声認識の信頼度を設定します。低信頼度の「-1」から高信頼度の「1」の間の値をとり、標準は「0」となります。-1を指定すると、どんな言葉でも反応する恐れがあります。一方1を指定すると、なかなか認識されません。今回は、信頼度が0.6より大きい場合に言葉を認識するよう指定しています。
  2. プレイヤーが「うえ」とセンサーに向かって発生した場合の処理です。メンバー変数Indexの値が、1ずつ減少します。
  3. プレイヤーが「した」とセンサーに向かって発生した場合の処理です。メンバー変数Indexの値が、1ずつ加算されます。
  4. ListBox1の選択されたインデックスにIndex変数の値を指定することで、Indexに該当するマスクが選択されます。
  5. ScrollIntoViewメソッドで選択された画像位置まで移動しますが、今回はListBoxに3個全ての画像が表示されているので、移動は確認できません。例えばListBoxに2個のマスクだけが表示されていて、残り1個が下に隠れて見えない場合は、3個目のマスクが指定された場合は、その位置までスクロールします。

新しいボディフレームの準備ができているときに発生するイベント処理

リスト7:MainWindow.xaml.vbの一部、リスト6の続き

Private Sub myBodyFrameReader_FrameArrived(sender As Object, e As BodyFrameArrivedEventArgs)
    CanvasBody.Children.Clear()   (1)
    CanvasBody2.Children.Clear()
    Using myBodyFrame = e.FrameReference.AcquireFrame   (2)
        If myBodyFrame Is Nothing = True Then
            Return
        End If
        myBodyFrame.GetAndRefreshBodyData(myBodies)    (3)
    End Using
    For Each body In myBodies
        For Each joint In body.Joints   (4)
            If joint.Value.TrackingState = TrackingState.Tracked Then
                DrawEllipse(joint.Value, 10, Brushes.Transparent)   (5)
            End If
        Next
    Next
End Sub
  1. CanvasBodyとCanvasBody2内を一度クリアしておきます。
  2. e.FrameReference.AcquireFrameメソッドで、ボディフレームを取得し、myBodyFrameで参照します。myBodyFrameに何も設定されていなかった場合は、何も行いません。
  3. GetAndRefreshBodyDataメソッドで、更新されたボディデータを取得します。
  4. ボディデータの中を、反復処理を行いながら、繰り返し変数jointで、関節の位置を反復処理しながら、関節位置を取得していきます。
  5. 関節が追跡されている場合は、関節の位置、関節の位置を描く円の大きさ(10を指定)と円の色(透明)を指定して、DrawEllipseを実行します。

ウインドウが閉じられる場合の処理

リスト8:MainWindow.xaml.vbの一部、リスト7の続き

Private Sub MainWindow_Closing(sender As Object, e As ComponentModel.CancelEventArgs) Handles Me.Closing
    If myKinect Is Nothing = False Then
        myKinect.Close()
        myKinect = Nothing
    End If
End Sub

Kinectが動作している場合は、Kinectを閉じ、全ての関連付けから解放します。

ListBoxからマスクが選択された時の処理

リスト9:MainWindow.xaml.vbの一部、リスト8の続き

Private Sub ListBox1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ListBox1.SelectionChanged
    myMask = DirectCast(ListBox1.SelectedItem, Mask).画像名
End Sub

メンバー変数myMaskに、ListBox1から選択された項目を、Maskクラスにキャストして、その「画像名」プロパティの値を取得して格納します。

関節の位置に透明の円を表示する処理

リスト10:MainWindow.xaml.vbの一部、リスト9の続き

Private Sub DrawEllipse(myJoint As Joint, R As Integer, myBrush As Brush)
    Dim myEllipse = New Ellipse
    With myEllipse
        .Width = R
        .Height = R
        .Fill = myBrush
    End With

    point = myKinect.CoordinateMapper.MapCameraPointToDepthSpace(myJoint.Position) (1)
    If point.X < 0 OrElse point.Y < 0 Then
        Return
    End If
    Canvas.SetLeft(myEllipse, point.X - (R / 2))   (2)
    Canvas.SetTop(myEllipse, point.Y - (R / 2))
    If myJoint.JointType = JointType.Head AndAlso myMask <> String.Empty Then
        myImage = New Image   (3)
        With myImage   (4)
            .Width = 60
            .Height = 75
            .Source = New BitmapImage(New Uri(myMask, UriKind.Relative))
        End With
        Canvas.SetLeft(myImage, point.X - 30)   (5)
        Canvas.SetTop(myImage, point.Y - 30)
        CanvasBody2.Children.Add(myImage)
    End If
    CanvasBody.Children.Add(myEllipse)   (6)
End Sub

透明な直径10ピクセルの円を作成します。直径はDrawEllipseを呼ぶ際に指定しています(リスト7)。

  1. CoordinateMapper.MapCameraPointToDepthSpaceメソッドで、関節の位置を、カメラ空間から距離空間へのポイントにマップし、メンバー変数pointで参照します。
  2. SetLeftとSetTopメソッドで透明な円を、指定したXとY軸に描きます。
    関節が頭部であり、かつメンバー変数myMaskの値が空ではなかった場合には、以下の処理を行います。
  3. 新しいImageクラスのインスタンスmyImageオブジェクトを生成します。
  4. myImageオブジェクトのWidthとHeightを指定し、Sourceプロパティに、メンバー変数myMaskの値を指定します。
  5. SetLeftとSetTopメソッドで、選択されたマスクを頭部に配置します。「-30」は位置を調整している値です。
  6. CanvasBodyには、透明化された円がプレイヤーの関節部分に描画されます。目には見えません。

カラーフレーム到着時のイベント

リスト11:MainWindow.xaml.vbの一部、リスト10の続き

Private Sub myColorFrameReader_FrameArrived(sender As Object, e As ColorFrameArrivedEventArgs)
    Dim colorFrameProcessed As Boolean = False
    Using myColorFrame As ColorFrame = e.FrameReference.AcquireFrame  (1)
        If myColorFrame Is Nothing = False Then
            Dim myColorFrameDescription As FrameDescription = myColorFrame.FrameDescription   (2)
            If myColorFrameDescription.Width = colorBitmap.PixelWidth AndAlso myColorFrameDescription.Height = colorBitmap.PixelHeight Then   (3)
                If myColorFrame.RawColorImageFormat = ColorImageFormat.Bgra Then   (4)
                    myColorFrame.CopyRawFrameDataToArray(ColorImagePixelData)
                Else
                    myColorFrame.CopyConvertedFrameDataToArray(ColorImagePixelData, ColorImageFormat.Bgra)
                End If
                colorFrameProcessed = True   (5)
            End If
        End If
    End Using
    If colorFrameProcessed = True Then
        colorBitmap.WritePixels(New Int32Rect(0, 0, colorBitmap.PixelWidth, colorBitmap.PixelHeight), ColorImagePixelData, colorBitmap.PixelWidth * BytesPerPixel, 0)   (6)
        roomImage.Source = colorBitmap   (7)
    End If
End Sub
End Class
  1. e.FrameReference.AcquireFrameメソッドでカラーフレームを取得し、myColorFrameで参照します。
  2. myColorFrameにデータがあった場合、KinectSensor からイメージフレームのプロパティを取得し、変数myColorFrameDescriptionで参照します。
  3. 取得したプロパティの幅と高さがcolorBitmapのPixelWidth、PixelHeightと同じであった場合、以下の処理を行います。
  4. カラーフレームデータがBgra形式であった場合は、CopyRawFrameDataToArrayメソッドで、Raw フレームのデータ指定された配列(ColorImagePixelData)にコピーします。そうでない場合は、CopyConvertedFrameDataToArrayメソッドでBgra形式に変換してから、バイト配列に格納します。
  5. Boolean型で宣言していた変数colorFrameProcessedをTrueで初期化します。
  6. 変数colorFrameProcessed変数がTrueであった場合は、colorBitmap(WriteableBitmapクラス)のWritePixelsメソッドで、ビットマップの指定した領域内のピクセルを更新します。

CopyConvertedFrameDataToArrayメソッドやWritePixelsメソッドの書式については、連載の第3回目を参照してください。7.roomImageのSourceプロパティにcolorBitmapオブジェクトを指定します。これでRGBカメラからの画像が表示されます。

実際に動作させると動画1のようになります。実際には「うえ」、「した」と喋ってマスクを選択していますが、音声は録音されておりません。ご了承ください。

この記事のキーワード :

人気記事トップ10

人気記事ランキングをもっと見る

企画広告も役立つ情報バッチリ! Sponsored

ClickHouse Cloudが実現した広告運用SaaS「ATOM」のデータ基盤革新、パフォーマンスを向上しながら、運用負荷とコストを削減 11月11日 6:20 AIで開発が加速する時代、品質保証はどう変わる? Tricentisが示すAIエージェント活用の実像 11月9日 6:45 悪用までの猶予が「1分」に迫るフロンティアAI時代に開発と本番を貫く脆弱性管理が必要 10月29日 8:30 欧州CRA対応とSBOM運用。AI生成コードのOSS混入問題の対策とは ――スニペット単位で検出する「FossID」「Insignary Clarity」によるSBOM運用 9月16日 6:40 PostgreSQLからTiDBへ、稼働中SaaSで進める段階的DB移行の実践と再設計への展望 9月7日 6:30 機能安全開発におけるGoogleTestの活用を支援!「Parasoft C/C++test CT」で実現する効率化&AI連携 4月14日 6:30 【CNDW2025】Grafanaが明かす「オブザーバビリティの哲学」ー最小限の労力で実用的なインサイトを得るには 1月23日 6:30 【CNDW2025】プロダクト急増に備える基盤刷新 ーウェルスナビがECSからEKSへの移行で得た知見とは 1月15日 6:30 【CNDW2025】250環境を5人で運用、構築時間は30分に ーKINTOテクノロジーズが語るインフラ基盤組織の作り方 2025年12月18日 6:30 新たな自動化で熱視線! AIエージェントの「推論能力」を支える2つのコンポーネントとは? 2025年11月28日 6:30