共用方式為


將 Windows 筆跡筆劃辨識為文字和圖形

使用 Windows Ink 內建的辨識功能,將筆墨筆劃轉換成文字和圖形。

重要 APIInkCanvasWindows.UI.Input.Inking

使用筆跡分析進行自由格式辨識

在這裡,我們示範如何使用 Windows Ink 分析引擎(Windows.UI.Input.Inking.Analysis),將 InkCanvas 上的一組自由格式筆劃分類、分析並辨識為文字或圖形。 (除了文字和圖形辨識之外,筆跡分析還可以用來辨識檔結構、專案符號清單和一般繪圖。

備註

如需表單輸入等基本、單行、純文字案例,請參閱本主題稍後的 限制手寫辨識

在此範例中,當使用者按兩下按鈕以指出完成繪圖時,就會起始辨識。

筆跡分析範例下載此範例 (基本)

  1. 首先,我們會設定 UI (MainPage.xaml)。

    使用者介面包含 «辨識» 按鈕、InkCanvas,以及標準 Canvas。 按下 [辨識] 按鈕時,會分析墨跡畫布上的所有筆劃,並在標準畫布上繪製對應的圖形和文字。 然後,原始筆跡筆劃會從筆跡畫布中刪除。

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
         <Grid.RowDefinitions>
             <RowDefinition Height="Auto"/>
             <RowDefinition Height="*"/>
         </Grid.RowDefinitions>
         <StackPanel x:Name="HeaderPanel" 
                     Orientation="Horizontal" 
                     Grid.Row="0">
             <TextBlock x:Name="Header" 
                         Text="Basic ink analysis sample" 
                         Style="{ThemeResource HeaderTextBlockStyle}" 
                         Margin="10,0,0,0" />
             <Button x:Name="recognize" 
                     Content="Recognize" 
                     Margin="50,0,10,0"/>
         </StackPanel>
         <Grid x:Name="drawingCanvas" Grid.Row="1">
    
             <!-- The canvas where we render the replacement text and shapes. -->
             <Canvas x:Name="recognitionCanvas" />
             <!-- The canvas for ink input. -->
             <InkCanvas x:Name="inkCanvas" />
    
         </Grid>
    </Grid>
    
  2. 在 UI 程式碼後置檔案 (MainPage.xaml.cs) 中,新增我們所需的筆跡和筆跡分析功能的命名空間類別引用:

  3. 然後,我們會指定全域變數:

     InkAnalyzer inkAnalyzer = new InkAnalyzer();
     IReadOnlyList<InkStroke> inkStrokes = null;
     InkAnalysisResult inkAnalysisResults = null;
    
  4. 接下來,我們會設定一些基本的筆跡輸入行為:

    • InkPresenter 設定為將手寫筆、滑鼠和觸控的輸入數據解譯為筆墨筆劃(InputDeviceTypes)。
    • 使用指定的 inkDrawingAttributes InkDrawingAttributes InkCanvas 轉譯筆墨筆劃。
    • 也會宣告 「辨識」按鈕上 Click 事件的接聽程式。
    /// <summary>
    /// Initialize the UI page.
    /// </summary>
    public MainPage()
    {
        this.InitializeComponent();
    
        // Set supported inking device types.
        inkCanvas.InkPresenter.InputDeviceTypes =
            Windows.UI.Core.CoreInputDeviceTypes.Mouse |
            Windows.UI.Core.CoreInputDeviceTypes.Pen | 
            Windows.UI.Core.CoreInputDeviceTypes.Touch;
    
        // Set initial ink stroke attributes.
        InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
        drawingAttributes.Color = Windows.UI.Colors.Black;
        drawingAttributes.IgnorePressure = false;
        drawingAttributes.FitToCurve = true;
        inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
    
        // Listen for button click to initiate recognition.
        recognize.Click += RecognizeStrokes_Click;
    }
    
  5. 在此範例中,我們會在 [辨識] 按鈕的 Click 事件處理程式中執行筆跡分析。

    /// <summary>
    /// The "Analyze" button click handler.
    /// Ink recognition is performed here.
    /// </summary>
    /// <param name="sender">Source of the click event</param>
    /// <param name="e">Event args for the button click routed event</param>
    private async void RecognizeStrokes_Click(object sender, RoutedEventArgs e)
    {
        inkStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
        // Ensure an ink stroke is present.
        if (inkStrokes.Count > 0)
        {
            inkAnalyzer.AddDataForStrokes(inkStrokes);
    
            // In this example, we try to recognizing both 
            // writing and drawing, so the platform default 
            // of "InkAnalysisStrokeKind.Auto" is used.
            // If you're only interested in a specific type of recognition,
            // such as writing or drawing, you can constrain recognition 
            // using the SetStrokDataKind method as follows:
            // foreach (var stroke in strokesText)
            // {
            //     analyzerText.SetStrokeDataKind(
            //      stroke.Id, InkAnalysisStrokeKind.Writing);
            // }
            // This can improve both efficiency and recognition results.
            inkAnalysisResults = await inkAnalyzer.AnalyzeAsync();
    
            // Have ink strokes on the canvas changed?
            if (inkAnalysisResults.Status == InkAnalysisStatus.Updated)
            {
                // Find all strokes that are recognized as handwriting and 
                // create a corresponding ink analysis InkWord node.
                var inkwordNodes = 
                    inkAnalyzer.AnalysisRoot.FindNodes(
                        InkAnalysisNodeKind.InkWord);
    
                // Iterate through each InkWord node.
                // Draw primary recognized text on recognitionCanvas 
                // (for this example, we ignore alternatives), and delete 
                // ink analysis data and recognized strokes.
                foreach (InkAnalysisInkWord node in inkwordNodes)
                {
                    // Draw a TextBlock object on the recognitionCanvas.
                    DrawText(node.RecognizedText, node.BoundingRect);
    
                    foreach (var strokeId in node.GetStrokeIds())
                    {
                        var stroke = 
                            inkCanvas.InkPresenter.StrokeContainer.GetStrokeById(strokeId);
                        stroke.Selected = true;
                    }
                    inkAnalyzer.RemoveDataForStrokes(node.GetStrokeIds());
                }
                inkCanvas.InkPresenter.StrokeContainer.DeleteSelected();
    
                // Find all strokes that are recognized as a drawing and 
                // create a corresponding ink analysis InkDrawing node.
                var inkdrawingNodes =
                    inkAnalyzer.AnalysisRoot.FindNodes(
                        InkAnalysisNodeKind.InkDrawing);
                // Iterate through each InkDrawing node.
                // Draw recognized shapes on recognitionCanvas and
                // delete ink analysis data and recognized strokes.
                foreach (InkAnalysisInkDrawing node in inkdrawingNodes)
                {
                    if (node.DrawingKind == InkAnalysisDrawingKind.Drawing)
                    {
                        // Catch and process unsupported shapes (lines and so on) here.
                    }
                    // Process generalized shapes here (ellipses and polygons).
                    else
                    {
                        // Draw an Ellipse object on the recognitionCanvas (circle is a specialized ellipse).
                        if (node.DrawingKind == InkAnalysisDrawingKind.Circle || node.DrawingKind == InkAnalysisDrawingKind.Ellipse)
                        {
                            DrawEllipse(node);
                        }
                        // Draw a Polygon object on the recognitionCanvas.
                        else
                        {
                            DrawPolygon(node);
                        }
                        foreach (var strokeId in node.GetStrokeIds())
                        {
                            var stroke = inkCanvas.InkPresenter.StrokeContainer.GetStrokeById(strokeId);
                            stroke.Selected = true;
                        }
                    }
                    inkAnalyzer.RemoveDataForStrokes(node.GetStrokeIds());
                }
                inkCanvas.InkPresenter.StrokeContainer.DeleteSelected();
            }
        }
    }
    
  6. 以下是在辨識畫布上繪製 TextBlock 的函數。 我們使用墨跡畫布上相關聯筆劃的邊界矩形,來設定 TextBlock 的位置和字型大小。

     /// <summary>
     /// Draw ink recognition text string on the recognitionCanvas.
     /// </summary>
     /// <param name="recognizedText">The string returned by text recognition.</param>
     /// <param name="boundingRect">The bounding rect of the original ink writing.</param>
     private void DrawText(string recognizedText, Rect boundingRect)
     {
         TextBlock text = new TextBlock();
         Canvas.SetTop(text, boundingRect.Top);
         Canvas.SetLeft(text, boundingRect.Left);
    
         text.Text = recognizedText;
         text.FontSize = boundingRect.Height;
    
         recognitionCanvas.Children.Add(text);
     }
    
  7. 以下是在我們的辨識畫板上用來繪製橢圓和多邊形的功能。 我們使用墨跡畫布上相關聯墨跡筆劃的邊界矩形,來設定圖形的位置和字型大小。

     // Draw an ellipse on the recognitionCanvas.
     private void DrawEllipse(InkAnalysisInkDrawing shape)
     {
         var points = shape.Points;
         Ellipse ellipse = new Ellipse();
    
         ellipse.Width = shape.BoundingRect.Width;
         ellipse.Height = shape.BoundingRect.Height;
    
         Canvas.SetTop(ellipse, shape.BoundingRect.Top);
         Canvas.SetLeft(ellipse, shape.BoundingRect.Left);
    
         var brush = new SolidColorBrush(Windows.UI.ColorHelper.FromArgb(255, 0, 0, 255));
         ellipse.Stroke = brush;
         ellipse.StrokeThickness = 2;
         recognitionCanvas.Children.Add(ellipse);
     }
    
     // Draw a polygon on the recognitionCanvas.
     private void DrawPolygon(InkAnalysisInkDrawing shape)
     {
         List<Point> points = new List<Point>(shape.Points);
         Polygon polygon = new Polygon();
    
         foreach (Point point in points)
         {
             polygon.Points.Add(point);
         }
    
         var brush = new SolidColorBrush(Windows.UI.ColorHelper.FromArgb(255, 0, 0, 255));
         polygon.Stroke = brush;
         polygon.StrokeThickness = 2;
         recognitionCanvas.Children.Add(polygon);
     }
    

以下是此範例的實際應用:

分析之前 分析之後
在分析 之前 分析之後

受限的手寫識別

在上一節中(使用筆跡分析進行自由格式辨識),我們示範如何使用 筆跡分析 API 來分析和辨識 InkCanvas 區域內的任意筆跡。

在本節中,我們將示範如何使用 Windows Ink 手寫辨識引擎(非筆跡分析)將 InkCanvas 上的一組筆劃轉換成文字(根據已安裝的默認語言套件)。

備註

本節顯示的基本手寫辨識功能最適合用於單行文字輸入的情境,例如表單輸入。 如需更全面的辨識應用,包括文件結構、清單項目、圖形和繪圖的分析與解釋(不僅限於文字辨識),請參閱上一節:使用筆跡分析進行自由形式識別

在此範例中,當使用者按兩下按鈕以指出完成寫入時,就會起始辨識。

筆跡手寫辨識範例中下載此範例

  1. 首先,我們會設定UI。

    UI 包含 [辨識] 按鈕、 InkCanvas 和顯示辨識結果的區域。

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
         <Grid.RowDefinitions>
             <RowDefinition Height="Auto"/>
             <RowDefinition Height="*"/>
         </Grid.RowDefinitions>
         <StackPanel x:Name="HeaderPanel"
                     Orientation="Horizontal"
                     Grid.Row="0">
                 <TextBlock x:Name="Header"
                     Text="Basic ink recognition sample"
                     Style="{ThemeResource HeaderTextBlockStyle}"
                     Margin="10,0,0,0" />
                 <Button x:Name="recognize"
                     Content="Recognize"
                     Margin="50,0,10,0"/>
         </StackPanel>
         <Grid Grid.Row="1">
             <Grid.RowDefinitions>
                 <RowDefinition Height="*"/>
                 <RowDefinition Height="Auto"/>
             </Grid.RowDefinitions>
             <InkCanvas x:Name="inkCanvas"
                 Grid.Row="0"/>
             <TextBlock x:Name="recognitionResult"
                 Grid.Row="1"
                 Margin="50,0,10,0"/>
         </Grid>
     </Grid>
    
  2. 在此範例中,您必須先新增筆跡功能所需的命名空間類型參考:

  3. 然後,我們會設定一些基本的筆跡輸入行為。

    InkPresenter 設定為將手寫筆和滑鼠的輸入數據解譯為筆墨筆劃(InputDeviceTypes)。 使用指定的 inkDrawingAttributes InkDrawingAttributes InkCanvas 轉譯筆墨筆劃。 也會宣告 「辨識」按鈕上 Click 事件的接聽程式。

    public MainPage()
        {
            this.InitializeComponent();
    
            // Set supported inking device types.
            inkCanvas.InkPresenter.InputDeviceTypes =
                Windows.UI.Core.CoreInputDeviceTypes.Mouse |
                Windows.UI.Core.CoreInputDeviceTypes.Pen;
    
            // Set initial ink stroke attributes.
            InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
            drawingAttributes.Color = Windows.UI.Colors.Black;
            drawingAttributes.IgnorePressure = false;
            drawingAttributes.FitToCurve = true;
            inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
    
            // Listen for button click to initiate recognition.
            recognize.Click += Recognize_Click;
        }
    
  4. 最後,我們會執行基本的手寫識別。 在此範例中,我們使用 [辨識] 按鈕的 click 事件處理程式來執行手寫辨識。

    // Get all strokes on the InkCanvas.
        IReadOnlyList<InkStroke> currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
    
    // Create a manager for the InkRecognizer object
        // used in handwriting recognition.
        InkRecognizerContainer inkRecognizerContainer =
            new InkRecognizerContainer();
    
    // Recognize all ink strokes on the ink canvas.
        IReadOnlyList<InkRecognitionResult> recognitionResults =
            await inkRecognizerContainer.RecognizeAsync(
                inkCanvas.InkPresenter.StrokeContainer,
                InkRecognitionTarget.All);
    
    • 每個 InkRecognitionResult 物件都包含一組文字候選專案。 此清單中最上方的項目被辨識引擎視為最佳匹配,其餘項目則依信賴度遞減排序。

      我們會逐一查看每個 InkRecognitionResult 並編譯候選項目清單。 然後會顯示候選專案,並清除 InkStrokeContainer(這也清除了 InkCanvas)。

    string str = "Recognition result\n";
        // Iterate through the recognition results.
        foreach (var result in recognitionResults)
        {
            // Get all recognition candidates from each recognition result.
            IReadOnlyList<string> candidates = result.GetTextCandidates();
            str += "Candidates: " + candidates.Count.ToString() + "\n";
            foreach (string candidate in candidates)
            {
                str += candidate + " ";
            }
        }
        // Display the recognition candidates.
        recognitionResult.Text = str;
        // Clear the ink canvas once recognition is complete.
        inkCanvas.InkPresenter.StrokeContainer.Clear();
    
    • 以下是完整的點擊事件處理程序範例。
    // Handle button click to initiate recognition.
        private async void Recognize_Click(object sender, RoutedEventArgs e)
        {
            // Get all strokes on the InkCanvas.
            IReadOnlyList<InkStroke> currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
    
            // Ensure an ink stroke is present.
            if (currentStrokes.Count > 0)
            {
                // Create a manager for the InkRecognizer object
                // used in handwriting recognition.
                InkRecognizerContainer inkRecognizerContainer =
                    new InkRecognizerContainer();
    
                // inkRecognizerContainer is null if a recognition engine is not available.
                if (!(inkRecognizerContainer == null))
                {
                    // Recognize all ink strokes on the ink canvas.
                    IReadOnlyList<InkRecognitionResult> recognitionResults =
                        await inkRecognizerContainer.RecognizeAsync(
                            inkCanvas.InkPresenter.StrokeContainer,
                            InkRecognitionTarget.All);
                    // Process and display the recognition results.
                    if (recognitionResults.Count > 0)
                    {
                        string str = "Recognition result\n";
                        // Iterate through the recognition results.
                        foreach (var result in recognitionResults)
                        {
                            // Get all recognition candidates from each recognition result.
                            IReadOnlyList<string> candidates = result.GetTextCandidates();
                            str += "Candidates: " + candidates.Count.ToString() + "\n";
                            foreach (string candidate in candidates)
                            {
                                str += candidate + " ";
                            }
                        }
                        // Display the recognition candidates.
                        recognitionResult.Text = str;
                        // Clear the ink canvas once recognition is complete.
                        inkCanvas.InkPresenter.StrokeContainer.Clear();
                    }
                    else
                    {
                        recognitionResult.Text = "No recognition results.";
                    }
                }
                else
                {
                    Windows.UI.Popups.MessageDialog messageDialog = new Windows.UI.Popups.MessageDialog("You must install handwriting recognition engine.");
                    await messageDialog.ShowAsync();
                }
            }
            else
            {
                recognitionResult.Text = "No ink strokes to recognize.";
            }
        }
    

國際辨識

Windows 筆跡平臺內建的手寫辨識包含 Windows 所支援地區設定和語言的廣泛子集。

如需 InkRecognizer 支援的語言清單,請參閱 InkRecognizer.Name 屬性主題。

您的應用程式可以查詢已安裝的手寫辨識引擎集,並使用其中一個引擎,或讓用戶選取他們慣用的語言。

注意 用戶可以移至 [ 設定 -> 時間與語言] 來查看已安裝的語言清單。 已安裝的語言列在 [語言] 底下。

若要安裝新的語言套件,並啟用該語言的手寫辨識:

  1. 移至 [設定 > 時間與語言 > 區域與語言]。
  2. 選擇 新增語言
  3. 從清單中選取語言,然後選擇區域版本。 語言現在會列在 [區域和語言 ] 頁面上。
  4. 點選語言,然後選擇 選項
  5. 在 [ 語言選項 ] 頁面上,下載 手寫辨識引擎 (您也可以在這裡下載完整的語言套件、語音辨識引擎和鍵盤配置)。

在這裡,我們將示範如何使用手寫辨識引擎,根據選取的辨識器來解譯 InkCanvas 上的一組筆劃。

當使用者完成寫入時,按兩下按鈕就會起始辨識。

  1. 首先,我們會設定UI。

    UI 包含 [辨識] 按鈕、列出所有已安裝手寫辨識器的組合框、InkCanvas,以及顯示辨識結果的區域。

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <StackPanel x:Name="HeaderPanel"
                        Orientation="Horizontal"
                        Grid.Row="0">
                <TextBlock x:Name="Header"
                           Text="Advanced international ink recognition sample"
                           Style="{ThemeResource HeaderTextBlockStyle}"
                           Margin="10,0,0,0" />
                <ComboBox x:Name="comboInstalledRecognizers"
                         Margin="50,0,10,0">
                    <ComboBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Name}" />
                            </StackPanel>
                        </DataTemplate>
                    </ComboBox.ItemTemplate>
                </ComboBox>
                <Button x:Name="buttonRecognize"
                        Content="Recognize"
                        IsEnabled="False"
                        Margin="50,0,10,0"/>
            </StackPanel>
            <Grid Grid.Row="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <InkCanvas x:Name="inkCanvas"
                           Grid.Row="0"/>
                <TextBlock x:Name="recognitionResult"
                           Grid.Row="1"
                           Margin="50,0,10,0"/>
            </Grid>
        </Grid>
    
  2. 然後,我們會設定一些基本的筆跡輸入行為。

    InkPresenter 設定為將手寫筆和滑鼠的輸入數據解譯為筆墨筆劃(InputDeviceTypes)。 使用指定的 inkDrawingAttributes InkDrawingAttributes InkCanvas 轉譯筆墨筆劃。

    我們調用 InitializeRecognizerList 函式,將已安裝的手寫辨識器清單填入辨識器下拉式方塊。

    我們也會在 [辨識] 按鈕上宣告 Click 事件的接聽程式,並在辨識器下拉式方塊上宣告選取範圍變更事件。

     public MainPage()
     {
        this.InitializeComponent();
    
        // Set supported inking device types.
        inkCanvas.InkPresenter.InputDeviceTypes =
            Windows.UI.Core.CoreInputDeviceTypes.Mouse |
            Windows.UI.Core.CoreInputDeviceTypes.Pen;
    
        // Set initial ink stroke attributes.
        InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
        drawingAttributes.Color = Windows.UI.Colors.Black;
        drawingAttributes.IgnorePressure = false;
        drawingAttributes.FitToCurve = true;
        inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
    
        // Populate the recognizer combo box with installed recognizers.
        InitializeRecognizerList();
    
        // Listen for combo box selection.
        comboInstalledRecognizers.SelectionChanged +=
            comboInstalledRecognizers_SelectionChanged;
    
        // Listen for button click to initiate recognition.
        buttonRecognize.Click += Recognize_Click;
    }
    
  3. 我們會在辨識器下拉式方塊中填入已安裝手寫辨識器的清單。

    一個 InkRecognizerContainer 被建立,用來管理手寫辨識過程。 使用此物件來呼叫 GetRecognizers,並擷取已安裝的辨識器清單,以填充辨識器下拉式方塊。

    // Populate the recognizer combo box with installed recognizers.
    private void InitializeRecognizerList()
    {
        // Create a manager for the handwriting recognition process.
        inkRecognizerContainer = new InkRecognizerContainer();
        // Retrieve the collection of installed handwriting recognizers.
        IReadOnlyList<InkRecognizer> installedRecognizers =
            inkRecognizerContainer.GetRecognizers();
        // inkRecognizerContainer is null if a recognition engine is not available.
        if (!(inkRecognizerContainer == null))
        {
            comboInstalledRecognizers.ItemsSource = installedRecognizers;
            buttonRecognize.IsEnabled = true;
        }
    }
    
  4. 如果辨識器組合框的選擇發生變更,請更新手寫辨識器。

    使用 InkRecognizerContainer,根據從辨識器下拉式方塊選取的辨識器,呼叫 SetDefaultRecognizer

    // Handle recognizer change.
        private void comboInstalledRecognizers_SelectionChanged(
            object sender, SelectionChangedEventArgs e)
        {
            inkRecognizerContainer.SetDefaultRecognizer(
                (InkRecognizer)comboInstalledRecognizers.SelectedItem);
        }
    
  5. 最後,我們會根據選取的手寫辨識器來執行手寫辨識。 在此範例中,我們使用 [辨識] 按鈕的 click 事件處理程式來執行手寫辨識。

    // Get all strokes on the InkCanvas.
        IReadOnlyList<InkStroke> currentStrokes =
            inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
    
    // Recognize all ink strokes on the ink canvas.
    IReadOnlyList<InkRecognitionResult> recognitionResults =
        await inkRecognizerContainer.RecognizeAsync(
            inkCanvas.InkPresenter.StrokeContainer,
            InkRecognitionTarget.All);
    
    • 每個 InkRecognitionResult 物件都包含一組文字候選專案。 此清單中最上方的項目被辨識引擎視為最佳匹配,其餘項目則依信賴度遞減排序。

      我們會逐一查看每個 InkRecognitionResult 並編譯候選項目清單。 然後會顯示候選專案,並清除 InkStrokeContainer(這也清除了 InkCanvas)。

    string str = "Recognition result\n";
    // Iterate through the recognition results.
    foreach (InkRecognitionResult result in recognitionResults)
    {
            // Get all recognition candidates from each recognition result.
            IReadOnlyList<string> candidates =
                result.GetTextCandidates();
            str += "Candidates: " + candidates.Count.ToString() + "\n";
            foreach (string candidate in candidates)
            {
                str += candidate + " ";
            }
    }
    // Display the recognition candidates.
    recognitionResult.Text = str;
    // Clear the ink canvas once recognition is complete.
    inkCanvas.InkPresenter.StrokeContainer.Clear();
    
    • 以下是完整的點擊事件處理程序範例。
    // Handle button click to initiate recognition.
    private async void Recognize_Click(object sender, RoutedEventArgs e)
    {
        // Get all strokes on the InkCanvas.
        IReadOnlyList<InkStroke> currentStrokes =
            inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
    
        // Ensure an ink stroke is present.
        if (currentStrokes.Count > 0)
        {
            // inkRecognizerContainer is null if a recognition engine is not available.
            if (!(inkRecognizerContainer == null))
            {
                // Recognize all ink strokes on the ink canvas.
                IReadOnlyList<InkRecognitionResult> recognitionResults =
                    await inkRecognizerContainer.RecognizeAsync(
                        inkCanvas.InkPresenter.StrokeContainer,
                        InkRecognitionTarget.All);
                // Process and display the recognition results.
                if (recognitionResults.Count > 0)
                {
                    string str = "Recognition result\n";
                    // Iterate through the recognition results.
                    foreach (InkRecognitionResult result in recognitionResults)
                    {
                        // Get all recognition candidates from each recognition result.
                        IReadOnlyList<string> candidates =
                            result.GetTextCandidates();
                        str += "Candidates: " + candidates.Count.ToString() + "\n";
                        foreach (string candidate in candidates)
                        {
                            str += candidate + " ";
                        }
                    }
                    // Display the recognition candidates.
                    recognitionResult.Text = str;
                    // Clear the ink canvas once recognition is complete.
                    inkCanvas.InkPresenter.StrokeContainer.Clear();
                }
                else
                {
                    recognitionResult.Text = "No recognition results.";
                }
            }
            else
            {
                Windows.UI.Popups.MessageDialog messageDialog =
                    new Windows.UI.Popups.MessageDialog(
                        "You must install handwriting recognition engine.");
                await messageDialog.ShowAsync();
            }
        }
        else
        {
            recognitionResult.Text = "No ink strokes to recognize.";
        }
    }
    

動態辨識

雖然上述兩個範例會要求使用者按下按鈕來開始辨識,您也可以使用與基本計時函式配對的筆劃輸入來執行動態辨識。

在此範例中,我們將使用與先前的國際辨識範例相同的UI和筆劃設定。

  1. 這些全域物件(InkAnalyzerInkStrokeInkAnalysisResultDispatcherTimer)會在整個應用程式中使用。

    // Stroke recognition globals.
    InkAnalyzer inkAnalyzer;
    DispatcherTimer recoTimer;
    
  2. 我們不是使用按鈕來啟動辨識,而是新增接聽兩個 InkPresenter 筆劃事件(StrokesCollectedStrokeStarted),然後設定一個基本的定時器(DispatcherTimer),間隔為一秒 Tick

    public MainPage()
    {
        this.InitializeComponent();
    
        // Set supported inking device types.
        inkCanvas.InkPresenter.InputDeviceTypes =
            Windows.UI.Core.CoreInputDeviceTypes.Mouse |
            Windows.UI.Core.CoreInputDeviceTypes.Pen;
    
        // Listen for stroke events on the InkPresenter to 
        // enable dynamic recognition.
        // StrokesCollected is fired when the user stops inking by 
        // lifting their pen or finger, or releasing the mouse button.
        inkCanvas.InkPresenter.StrokesCollected += inkCanvas_StrokesCollected;
        // StrokeStarted is fired when ink input is first detected.
        inkCanvas.InkPresenter.StrokeInput.StrokeStarted +=
            inkCanvas_StrokeStarted;
    
        inkAnalyzer = new InkAnalyzer();
    
        // Timer to manage dynamic recognition.
        recoTimer = new DispatcherTimer();
        recoTimer.Interval = TimeSpan.FromSeconds(1);
        recoTimer.Tick += recoTimer_TickAsync;
    }
    
  3. 接著,我們會定義在第一個步驟中宣告的 InkPresenter 事件處理程序(我們也會覆寫 OnNavigatingFrom 頁面事件來管理我們的定時器)。

    • 收集筆劃
      將筆跡筆劃 (AddDataForStrokes) 新增至 InkAnalyzer,並在使用者停止筆跡時啟動辨識定時器(舉起手寫筆或手指或放開滑鼠按鈕)。 在第一秒沒有筆跡輸入之後,就會起始辨識。

      使用 SetStrokeDataKind 方法來指定您只對文字感興趣(包括文件結構和項目符號清單)或只對繪圖感興趣(包括圖形辨識)。

    • 筆畫已開始
      如果新的筆劃在下一個定時器刻度事件之前開始,請停止定時器,因為新的筆劃可能是單一手寫專案的接續。

    // Handler for the InkPresenter StrokeStarted event.
    // Don't perform analysis while a stroke is in progress.
    // If a new stroke starts before the next timer tick event,
    // stop the timer as the new stroke is likely the continuation
    // of a single handwriting entry.
    private void inkCanvas_StrokeStarted(InkStrokeInput sender, PointerEventArgs args)
    {
        recoTimer.Stop();
    }
    // Handler for the InkPresenter StrokesCollected event.
    // Stop the timer and add the collected strokes to the InkAnalyzer.
    // Start the recognition timer when the user stops inking (by 
    // lifting their pen or finger, or releasing the mouse button).
    // If ink input is not detected after one second, initiate recognition.
    private void inkCanvas_StrokesCollected(InkPresenter sender, InkStrokesCollectedEventArgs args)
    {
        recoTimer.Stop();
        // If you're only interested in a specific type of recognition,
        // such as writing or drawing, you can constrain recognition 
        // using the SetStrokDataKind method, which can improve both 
        // efficiency and recognition results.
        // In this example, "InkAnalysisStrokeKind.Writing" is used.
        foreach (var stroke in args.Strokes)
        {
            inkAnalyzer.AddDataForStroke(stroke);
            inkAnalyzer.SetStrokeDataKind(stroke.Id, InkAnalysisStrokeKind.Writing);
        }
        recoTimer.Start();
    }
    // Override the Page OnNavigatingFrom event handler to 
    // stop our timer if user leaves page.
    protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
    {
        recoTimer.Stop();
    }
    
  4. 最後,我們會執行手寫識別。 在此範例中,我們使用 DispatcherTimerTick 事件處理程式來起始手寫辨識。

    private async void recoTimer_TickAsync(object sender, object e)
    {
        recoTimer.Stop();
        if (!inkAnalyzer.IsAnalyzing)
        {
            InkAnalysisResult result = await inkAnalyzer.AnalyzeAsync();
    
            // Have ink strokes on the canvas changed?
            if (result.Status == InkAnalysisStatus.Updated)
            {
                // Find all strokes that are recognized as handwriting and 
                // create a corresponding ink analysis InkWord node.
                var inkwordNodes =
                    inkAnalyzer.AnalysisRoot.FindNodes(
                        InkAnalysisNodeKind.InkWord);
    
                // Iterate through each InkWord node.
                // Display the primary recognized text (for this example, 
                // we ignore alternatives), and then delete the 
                // ink analysis data and recognized strokes.
                foreach (InkAnalysisInkWord node in inkwordNodes)
                {
                    string recognizedText = node.RecognizedText;
                    // Display the recognition candidates.
                    recognitionResult.Text = recognizedText;
    
                    foreach (var strokeId in node.GetStrokeIds())
                    {
                        var stroke =
                            inkCanvas.InkPresenter.StrokeContainer.GetStrokeById(strokeId);
                        stroke.Selected = true;
                    }
                    inkAnalyzer.RemoveDataForStrokes(node.GetStrokeIds());
                }
                inkCanvas.InkPresenter.StrokeContainer.DeleteSelected();
            }
        }
        else
        {
            // Ink analyzer is busy. Wait a while and try again.
            recoTimer.Start();
        }
    }
    

主題範例

其他範例