LINEで送る

前回は、画面上に線を描画する処理を実装しました。
今回は、InkManagerというクラスを利用して、文字認識を行う処理を実装します。

1.コントロールの追加
文字認識に関連したコントロールを、XAMLに記述します。

<Page
    x:Class="WindowsStoreAppInkSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WindowsStoreAppInkSample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Canvas x:Name="InkCanvas" Background="White" />
        <StackPanel Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="20,20,0,0">
            <TextBlock x:Name="RecognizeText" Foreground="Red" FontSize="18" Text=""/>
            <TextBlock x:Name="LogText" Foreground="Black" FontSize="18" Text=""/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Bottom" Background="Black">
            <Button x:Name="ClearButton" Content="Clear" FontSize="22" Width="200" Height="50" Click="ClearButton_Click" Margin="20,10,20,10"/>
            <Button x:Name="RecognizeButton" Content="Recognize" FontSize="22" Width="200" Height="50" Margin="20,10,20,10" Click="RecognizeButton_Click"/>
            <ComboBox x:Name="RecognizerBox" Width="600" FontSize="22" Margin="20,10,20,10"/>
        </StackPanel>
    </Grid>
</Page>

以下のようなコントロールを追加しました。

RecognizeText :認識した文字を表示するテキストブロック
ClearButton :描画した線を削除するボタン
RecognizeButton :文字認識を行うボタン
RecognizeBox :文字認識に利用するエンジンを選択するコンボボックス

2.InkManagerの定義
InkManagerというクラスを定義します。
このクラスを利用することで、文字認識エンジンの列挙や、文字認識を行うことができます。

InkManager _InkManager = new InkManager();

3.文字認識エンジンの列挙
2で定義したInkManagerを利用し、文字認識エンジンの列挙を行います。
列挙した文字認識エンジンは、RecognizerBoxに設定します。

public MainPage()
{
    this.InitializeComponent();

    InkCanvas.PointerPressed += InkCanvas_PointerPressed;
    InkCanvas.PointerMoved += InkCanvas_PointerMoved;
    InkCanvas.PointerReleased += InkCanvas_PointerReleased;
    InkCanvas.PointerExited += InkCanvas_PointerExited;

    // 文字認識エンジンの列挙
    RecognizerBox.DisplayMemberPath = "Name";
    IReadOnlyList<InkRecognizer> recognizers = _InkManager.GetRecognizers();
    foreach (InkRecognizer recognizer in recognizers)
    {
        RecognizerBox.Items.Add(recognizer);
    }
    RecognizerBox.SelectedIndex = 0;
}

コンボボックスのDisplayMemberPathには「Name」を指定し、InkRecognizerの「Name」プロパティを利用するよう設定します。

4.ポインターイベントの修正
InkManagerはPressed・Move・Releasedのイベント時に、PointerPointを登録することで、内部に線の情報を保存します。
また、InkManagerにも消しゴムのモードが実装されており、線を設定する際にモードを設定すると、消しゴムとして動作させることも可能です。
今回は、デバイスの種類が消しゴムだった場合のみ、消しゴムとして動作させているので、Pressedイベント時にInkManagerのModeを設定しています。

void InkCanvas_PointerPressed(object sender, PointerRoutedEventArgs e)
{
    LogText.Text = "InkCanvas_PointerPressed ----------------------------------------- ";
    OutputPointerData(e);

    PointerPoint po = e.GetCurrentPoint(InkCanvas);
    if (po.PointerDevice.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Pen)
    {
        _PenID = po.PointerId;
        _PrevPoint = po.Position;

        if (po.Properties.IsEraser)
        {
            // 消しゴムモード
            _InkManager.Mode = InkManipulationMode.Erasing;
        }
        else
        {
            // ペンモード
            _InkManager.Mode = InkManipulationMode.Inking;
        }

        _InkManager.ProcessPointerDown(po);
    }

    e.Handled = true;
}

void InkCanvas_PointerMoved(object sender, PointerRoutedEventArgs e)
{
    LogText.Text = "InkCanvas_PointerMoved ------------------------------------------- ";
    OutputPointerData(e);

    if (e.Pointer.PointerId == _PenID)
    {
        PointerPoint po = e.GetCurrentPoint(InkCanvas);
        Point currentPoint = po.Position;

        if (GetPointDistance(_PrevPoint, currentPoint) > 0)
        {
	・
	・
	・

            _InkManager.ProcessPointerUpdate(po);
        }
    }

    e.Handled = true;
}

void InkCanvas_PointerReleased(object sender, PointerRoutedEventArgs e)
{
    LogText.Text = "InkCanvas_PointerReleased ---------------------------------------- ";
    OutputPointerData(e);

    if (e.Pointer.PointerId == _PenID)
    {
        _PenID = 0;
        _InkManager.ProcessPointerUp(e.GetCurrentPoint(InkCanvas));
    }

    e.Handled = true;
}

5.文字認識の処理実装
文字認識は、InkManagerのSetDefaultRecognizerを利用します。
SetDefaultRecognizerを行う前に、SetDefaultRecognizerで文字認識エンジンの設定を行います。
RecognizeAsyncは、名前の通り非同期で実行する必要があるため、async・awaitを利用しています。
文字認識した結果は、複数取得できますので、各結果をカンマ区切りで表示しています。
結果の順番は、最初のものが第一候補となっており、それ以降が第二、第三の候補という順番になっています。

// 文字認識
private async void RecognizeButton_Click(object sender, RoutedEventArgs e)
{
    if (_InkManager.GetStrokes().Count() != 0)
    {
        // 文字認識エンジンの設定
        _InkManager.SetDefaultRecognizer((InkRecognizer)RecognizerBox.SelectedItem);

        // 文字認識
        string text = "";
        IReadOnlyList<InkRecognitionResult> results = await _InkManager.RecognizeAsync(InkRecognitionTarget.All);
        foreach (InkRecognitionResult result in results)
        {
            IReadOnlyList<string> strings = result.GetTextCandidates();
            foreach (string s in strings)
            {
                if (text.Length != 0)
                {
                    text += ", ";
                }
                text += s;
            }
        }
        // 結果の設定
        RecognizeText.Text = text;
    }
}

6.クリアボタンの実装
クリアボタンの実装を行います。
今回は、InkCanvas.Childrenのクリアと、InkManagerを作成し直して対応しました。

// 画面のクリア
private void ClearButton_Click(object sender, RoutedEventArgs e)
{
    _InkManager = new InkManager();
    InkCanvas.Children.Clear();
}

それでは、実際にアプリケーションを実行し、動作を確認しましょう。
今回は「Microsoft 日本語手書き認識エンジン」という文字認識エンジンを利用して、文字の認識を行いました。

wsa_03_01

Recognizeボタンを押したあとのスクリーンショットです。
左上の赤字のテキストが、文字認識の結果です。
正常に、「坂本」という文字が認識されていることが分かります。
また、「坂本」以降のテキストは、その他の候補を表示しています。

サンプルソースはこちらにアップしています。
興味のある方は、実際に試して頂ければと思います。

Windows ストアアプリの記事につきましては、今回で終了となります。
次回以降は、また別の内容となりますので、お楽しみに!

Top