音声認識と手書き認識を使ってみよう

2014年6月28日(土)
薬師寺 国安

リスト8 要素内で、ポインターが移動した時に発生するイベントの処理

イベントデータを提供するPointerIdが、入力ポインターの一意の識別子と同じである場合の処理です。

GetCurrentPointメソッドで、Canvasの現在のポインターを取得する。PointerPointクラスのPositionプロパティで、ポインター入力位置のクライアント座標を取得し、メンバー変数currentContactPointに格納する。

メンバー変数x1に、ポインターデバイスが開始された時のX座標を指定する。メンバー変数y1にポインターデバイスが開始された時のY座標を指定する。メンバー変数x2に、要素内で、ポインターが移動した時のX座標を指定する。メンバー変数y2に、要素内で、ポインターが移動した時のY座標を指定する。

新しいLineのインスタンスmyLineオブジェクトを作成する。Line の始点の x 座標に変数x1の値を指定する。Line の始点の y 座標に変数y1の値を指定する。Line の終点の x 座標に変数x2の値を指定する。Line の終点の y 座標に変数y2の値を指定する。Lineの太さを5に指定し、ラインの色を赤に指定する。

要素内で、ポインターが移動した時の、Canvasの現在のポインターをメンバー変数prevContactPointに代入する。CanvasにAddメソッドでmyLineオブジェクトを追加する。InkManagerクラスのProcessPointerUpdateメソッドで、最後のイベントポインターから現在のポインターイベントまでの、圧力と傾きなどの、位置と状態プロパティを更新する。

    Private Sub myCanvas_PointerMoved(sender As Object, e As PointerRoutedEventArgs)
        If e.Pointer.PointerId = myPenID Then
            recognizeButton.IsEnabled = True
            clearButton.IsEnabled = True
            Dim myPointer As PointerPoint = e.GetCurrentPoint(myCanvas)
            currentContactPoint = myPointer.Position
            x1 = prevContactPoint.X
            y1 = prevContactPoint.Y
            x2 = currentContactPoint.X
            y2 = currentContactPoint.Y

            Dim myLine As New Line
            With myLine
                .X1 = x1
                .Y1 = y1
                .X2 = x2
                .Y2 = y2
                .StrokeThickness = 5
                .Stroke = New SolidColorBrush(Colors.Red)
            End With
            prevContactPoint = currentContactPoint
            myCanvas.Children.Add(myLine)
            myInkManager.ProcessPointerUpdate(myPointer)

        End If
        e.Handled = True
    End Sub

次は、要素内で、プレス アクションを開始したポインター デバイスが離された時に発生するイベントの処理です(リスト9)。

リスト9 要素内で、プレス アクションを開始したポインター デバイスが離された時に発生するイベントの処理

イベントデータを提供するPointerIdが、入力ポインターの一意の識別子と同じである場合の処理です。GetCurrentPointメソッドで、Canvas内の現在のポインターを取得する。InkManagerクラスのProcessPointerUpメソッドで、ポインターの位置と圧力の傾きを処理する。このメソッドは ProcessPointerUpdateメソッドを呼び出した後で呼び出す必要がある。入力ポインターの一意の識別子を取得していたメンバー変数myPenIDを0で初期化する。

    Private Sub myCanvas_PointerReleased(sender As Object, e As PointerRoutedEventArgs)
        If e.Pointer.PointerId = myPenID Then
            Dim myPointer As PointerPoint = e.GetCurrentPoint(myCanvas)
            myInkManager.ProcessPointerUp(myPointer)
        End If
        myPenID = 0
        e.Handled = True
    End Sub

次は[消去]ボタンがタップされた時の処理です(リスト10)

リスト10 [消去]ボタンがタップされた時の処理認識された文字が格納されているメンバー変数dummyTextBoxの値を空にする。

InkManagerクラスのModeプロパティで、インク入力モードをInkManipulationMode.Erasingに指定する。ストロークが削除モードになる。InkManagerのGetStrokesメソッドで、InkManager の管理するコレクション内のすべての InkStroke オブジェクトを取得し、変数myStrokeに格納する。

InkStrokeのコレクションの数だけ繰り返し処理を行う。すべてのInkStrokeを選択状態にする。DeleteSelectedメソッドで、選択されたInkStroke オブジェクトを InkManager の管理する InkStroke のコレクションから削除する。Canvasの背景色をWhiteに指定し、Canvas内をクリアする。その後、InkManagerのModeをInkManipulationMode.Inkingに戻しておく。再びインクストロークが可能な状態になる。[読み上げる]と[消去]ボタンの使用を不可とする。

    Private Sub clearButton_Click(sender As Object, e As RoutedEventArgs) Handles clearButton.Click
        dummyTextBox = String.Empty
        myInkManager.Mode = InkManipulationMode.Erasing
        Dim myStroke = myInkManager.GetStrokes
        For i As Integer = 0 To myStroke.Count - 1
            myStroke(i).Selected = True
        Next
        myInkManager.DeleteSelected()
        myCanvas.Background = New SolidColorBrush(Colors.White)
        myCanvas.Children.Clear()
        myInkManager.Mode = InkManipulationMode.Inking
        recognizeButton.IsEnabled = False
        clearButton.IsEnabled = False
    End Sub

次は、[読み上げる]ボタンがタップされた時の処理です(リスト11)

リスト11 [読み上げる]ボタンがタップされた時の処理

[消去]ボタンの使用を不可とする。InkManagerのGetStrokesメソッドで、InkManager のを管理するコレクション内のすべての InkStroke オブジェクトを取得し、変数myStrokeに格納する。InkStrokeのコレクションの数だけ繰り返し処理を行う。すべてのInkStrokeを選択状態にする。

文字列変数myRecNameを「Microsoft 日本語手書き認識エンジン」で初期化しておく。余談だが、英語の認識エンジンを使用する場合は「Microsoft English (US) Handwriting Recognizer」と指定する。

InkManagerのGetRecognizersメソッドで、手書き認識エンジンのコレクションを取得する。

取得したコレクションの数だけ反復処理を行う。変数myRecNameの値が、手書き認識エンジンのコレクションのNameと一致する場合は、その手書き認識エンジンを、SetDefaultRecognizerメソッドで規定値として設定する。「Microsoft 日本語手書き認識エンジン」が規定値に設定される。RecognizeAsyncメソッドで、1つまたは複数のInkStrokeオブジェクトに対して手書き認識を実行する。InkRecognitionTarget.Allで、ストロークコレクション内のすべてのストロークを認識エンジンに渡す。UpdateRecognitionResultsメソッドで、潜在的なテキストのコレクションが、手書きの認識からマッチするよう更新する。

認識された手書きの結果テキストのコレクション内を反復処理しながら、以下の処理を行う。InkRecognitionResult.GetTextCandidatesメソッドで、手書き認識と一致する可能性のある候補として識別された、文字列のコレクションを取得して、変数myTextに格納する。認識された文字列をdummyTextBoxメンバー変数に格納する。

メンバー変数readingTextに、認識された文字格納されているdummyTextBoxの値を指定する。[消去]ボタンの使用を可能にし、音声で喋らすRecognizeタスクを実行する。非同期処理で行われますので、メソッドの先頭にAsyncを追加する。

    Private Async Sub recognizeButton_Click(sender As Object, e As RoutedEventArgs) Handles recognizeButton.Click
        clearButton.IsEnabled = False
        Dim myStroke = myInkManager.GetStrokes
        For i As Integer = 0 To myStroke.Count - 1
            myStroke(i).Selected = True
        Next

        Dim myRecName = "Microsoft 日本語手書き認識エンジン" '"Microsoft English (US) Handwriting Recognizer"
        Dim myRecognizer = myInkManager.GetRecognizers

        For i As Integer = 0 To myRecognizer.Count - 1
            If myRecName = myRecognizer(i).Name Then
                myInkManager.SetDefaultRecognizer(myRecognizer(i))
            End If

        Next

        Dim result As IReadOnlyList(Of InkRecognitionResult) = Await myInkManager.RecognizeAsync(InkRecognitionTarget.All)
        myInkManager.UpdateRecognitionResults(result)

        Dim myAlternate = String.Empty
        For Each myResult In result
            Dim myText = myResult.GetTextCandidates
            myAlternate = myAlternate & " " & myText(0)
            dummyTextBox = myAlternate
        Next
        readingText = dummyTextBox
        clearButton.IsEnabled = True
        Await Recognize()
    End Sub

最後は、認識された手書き文字を音声で読み上げる処理です(リスト12)。

リスト12 認識された手書き文字を音声で読み上げる処理MediaElement型のmyMedia変数を宣言し、MediaElement1で初期化しておく。

音声機能へのアクセスを提供する、新しいSpeechSynthesizerのインスタンス、synthオブジェクトを作成する。SynthesizeTextToStreamAsyncメソッドで、指定した文字列から、音声出力を非同期に生成する。SetSourceメソッドで、指定されたストリームおよびMIME型を使用してSourceプロパティを設定します。Playメソッドで音声を再生する。非同期処理で行われるため、メソッドの先頭にAsyncを追加する。

    Private Async Function Recognize() As Task
        Dim myMedia As MediaElement = Me.MediaElement1
        Dim synth = New Windows.Media.SpeechSynthesis.SpeechSynthesizer
        Dim stream = Await synth.SynthesizeTextToStreamAsync(readingText)
        myMedia.SetSource(stream, stream.ContentType)
        myMedia.Play()
    End Function
End Class

実行すると、図4のように表示されます。

手書き文字を認識して読み上げている
図4 手書き文字を認識して読み上げている

今回はこれで終わりです。TextBoxに入力した文字を読み上げる処理と、手書き文字を一度文字に変換して認識し、読み上げる処理を解説しました。2つ目の処理はコードが136行あって、多少長くなってしまいました。また、コード自体も結構複雑なので、何度も読んで理解してほしいと思います。というより、自分でサンプルを見ながらVS2013上で直接コードを打ち込むほうが、何倍も早く身につくので、まずは手を動かして頑張ってください。

最終回の次回は、ローカル画像の読み込み方を解説します。お楽しみに。

  • 音声認識と手書き認識を行うプログラム

    『Windows ストア アプリ 100行プログラミング』 第9回のサンプルプログラムです。
薬師寺国安事務所

薬師寺国安事務所代表。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メルマガ会員のサービス内容を見る

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