Partilhar via


Reconhecer traços do Windows Ink como texto e formas

Converta traços de tinta digital em texto e formas usando os recursos de reconhecimento internos do Windows Ink.

APIs importantes: InkCanvas, Windows.UI.Input.Inking

Reconhecimento de forma livre com análise de tinta

Aqui, demonstramos como usar o mecanismo de análise do Windows Ink (Windows.UI.Input.Inking.Analysis) para classificar, analisar e reconhecer um conjunto de traçados de forma livre em um InkCanvas como texto ou formas. (Além de reconhecimento de texto e forma, a análise de tinta também pode ser usada para reconhecer a estrutura do documento, listas de marcadores e desenhos genéricos.)

Observação

Para cenários básicos de texto sem formatação de linha única, como entrada de formulário, consulte Reconhecimento restrito de manuscrito mais adiante neste tópico.

Neste exemplo, o reconhecimento é iniciado quando o usuário clica em um botão para indicar que concluiu o desenho.

Baixe este exemplo da Amostra de análise de tinta (básica)

  1. Primeiro, configuramos a interface do usuário (MainPage.xaml).

    Ela inclui um botão "Reconhecer", um InkCanvas e um Canvas padrão. Quando o botão "Reconhecer" é pressionado, todos os traços de tinta na tela de tinta são analisados e, se reconhecidos, as formas e o texto correspondentes são desenhados na tela padrão. Os traços de tinta originais são excluídos da tela de tinta.

    <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. No arquivo code-behind da interface do usuário (MainPage.xaml.cs), adicione as referências de tipo de namespace necessárias para nossa funcionalidade de análise de tinta e tinta:

  3. Em seguida, especificamos nossas variáveis globais:

     InkAnalyzer inkAnalyzer = new InkAnalyzer();
     IReadOnlyList<InkStroke> inkStrokes = null;
     InkAnalysisResult inkAnalysisResults = null;
    
  4. Em seguida, definimos alguns comportamentos básicos de entrada de tinta:

    • InkPresenter está configurado para interpretar dados de entrada de caneta, mouse e toque como traços de tinta (InputDeviceTypes).
    • Traços de tinta são renderizados em InkCanvas usando os InkDrawingAttributes especificados.
    • Um ouvinte para o evento de clique no botão "Reconhecer" também é declarado.
    /// <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. Para este exemplo, executamos a análise de tinta no manipulador de eventos de clique do botão "Reconhecer".

    • Primeiro, chame GetStrokes em StrokeContainer de InkCanvas.InkPresenter para obter a coleção de todos os traços de tinta atuais.
    • Se houver traços de tinta, transmita-os em uma chamada para AddDataForStrokes de InkAnalyzer.
    • Estamos tentando reconhecer desenhos e texto, mas você pode usar o método SetStrokeDataKind para especificar se está interessado apenas em texto (incluindo estrutura do documento e listas de marcadores) ou apenas em desenhos (incluindo reconhecimento de forma).
    • Chame AnalyzeAsync para iniciar a análise de tinta e obter o InkAnalysisResult.
    • Se Status retornar um estado de Atualizado, chame FindNodes para InkAnalysisNodeKind.InkWord e InkAnalysisNodeKind.InkDrawing.
    • Itere por ambos os conjuntos de tipos de nó e desenhe o respectivo texto ou forma na tela de reconhecimento (abaixo da tela de tinta).
    • Por último, exclua os nós reconhecidos de InkAnalyzer e os traços de tinta correspondentes da tela de tinta.
    /// <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. Aqui está a função para desenhar um TextBlock na tela de reconhecimento. Usamos o retângulo delimitador do traço de tinta associado na tela de tinta para definir a posição e o tamanho da fonte de 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. Estas são as funções para desenhar elipses e polígonos em nossa tela de reconhecimento. Usamos o retângulo delimitador do traço de tinta associado na tela de tinta para definir a posição e o tamanho da fonte das formas.

     // 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);
     }
    

Veja este exemplo em ação:

Antes da análise Após a análise
Antes da análise Após a análise

Reconhecimento de manuscrito restrito

Na seção anterior (Reconhecimento de forma livre com análise de tinta), demonstramos como usar as APIs de análise de tinta para analisar e reconhecer traços de tinta arbitrários em uma área de InkCanvas.

Nesta seção, demonstramos como usar o mecanismo de reconhecimento de manuscrito do Windows Ink (não a análise de tinta) para converter um conjunto de traçados em um InkCanvas em texto (com base no pacote de idiomas padrão instalado).

Observação

O reconhecimento de manuscrito básico mostrado nesta seção é mais adequado para cenários de entrada de texto de linha única, como entrada de formulário. Para cenários de reconhecimento mais avançados que incluem análises e interpretação da estruturas de documentos, itens de lista, formas e desenhos (além do reconhecimento de texto), consulte a seção anterior: Reconhecimento de forma livre com análise de tinta.

Neste exemplo, o reconhecimento é iniciado quando o usuário clica em um botão para indicar que concluiu a escrita.

Baixe este exemplo do Exemplo de reconhecimento de manuscrito à tinta

  1. Primeiro, configuramos a interface do usuário.

    A interface do usuário inclui um botão "Reconhecer", InkCanvas e uma área para exibir os resultados do reconhecimento.

    <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. Para este exemplo, primeiro você precisa adicionar as referências de tipo de namespace necessárias para nossa funcionalidade de tinta:

  3. Em seguida, definimos alguns comportamentos básicos de entrada de tinta.

    InkPresenter está configurado para interpretar dados de entrada de caneta e mouse como traços de tinta (InputDeviceTypes). Traços de tinta são renderizados em InkCanvas usando os InkDrawingAttributes especificados. Um ouvinte para o evento de clique no botão "Reconhecer" também é declarado.

    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. Por último, realizamos o reconhecimento básico de manuscrito. Para este exemplo, usamos o manipulador de eventos de clique do botão "Reconhecer" para executar o reconhecimento de manuscrito.

    // 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);
    
    • Cada objeto InkRecognitionResult contém um conjunto de candidatos de texto. O item mais importante dessa lista é considerado pelo mecanismo de reconhecimento como o melhor correspondente, seguido pelos demais candidatos em ordem decrescente de confiança.

      Iteramos através de cada InkRecognitionResult e compilamos a lista de candidatos. Os candidatos são exibidos, e InkStrokeContainer é limpo (o que também limpa 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();
    
    • Aqui está o exemplo do manipulador de cliques na íntegra.
    // 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.";
            }
        }
    

Reconhecimento internacional

O reconhecimento de manuscrito incorporado à plataforma de tinta digital do Windows inclui um amplo subconjunto de localidades e idiomas aceitos pelo Windows.

Consulte o tópico sobre a propriedade InkRecognizer.Name para obter uma lista de idiomas com suporte por InkRecognizer.

Seu aplicativo pode consultar o conjunto de mecanismos de reconhecimento de manuscrito instalados e usar um deles ou permitir que um usuário selecione o idioma preferido.

Observação Os usuários podem ver uma lista de idiomas instalados acessando Configurações -> Hora e Idioma. Os idiomas instalados estão listados em Idiomas.

Para instalar novos pacotes de idiomas e habilitar o reconhecimento de manuscrito para eles:

  1. Acesse Configurações > Hora e idioma > Região e idioma.
  2. Selecione Adicionar um idioma.
  3. Selecione um idioma na lista e escolha a versão da região. O idioma agora é listado na página Região e idioma.
  4. Clique no idioma e selecione Opções.
  5. Na página Opções de idioma, baixe o Mecanismo de reconhecimento de manuscrito (também é possível baixar o pacote de idiomas completo, o mecanismo de reconhecimento de fala e o layout do teclado aqui).

Aqui, demonstramos como usar o mecanismo de reconhecimento de manuscrito para interpretar um conjunto de traços em InkCanvas com base no reconhecedor escolhido.

O reconhecimento é iniciado pelo usuário ao clicar em um botão quando ele termina de escrever.

  1. Primeiro, configuramos a interface do usuário.

    A interface do usuário inclui um botão "Reconhecer", uma caixa de combinação que lista todos os reconhecedores de manuscrito instalados, InkCanvas e uma área para exibir os resultados do reconhecimento.

    <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. Em seguida, definimos alguns comportamentos básicos de entrada de tinta.

    InkPresenter está configurado para interpretar dados de entrada de caneta e mouse como traços de tinta (InputDeviceTypes). Traços de tinta são renderizados em InkCanvas usando os InkDrawingAttributes especificados.

    Chamamos uma função InitializeRecognizerList para preencher a caixa de combinação do reconhecedor com uma lista de reconhecedores de manuscrito instalados.

    Também declaramos ouvintes para o evento de clique no botão "Reconhecer" e o evento de seleção alterada na caixa de combinação do reconhecedor.

     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. Preenchemos a caixa de combinação do reconhecedor com uma lista de reconhecedores de manuscrito que estão instalados.

    Um InkRecognizerContainer é criado para gerenciar o processo de reconhecimento de manuscrito. Use esse objeto para chamar GetRecognizers e recuperar a lista de reconhecedores instalados para preencher a caixa de combinação do reconhecedor.

    // 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. Atualize o reconhecedor de manuscrito se a seleção da caixa de combinação do reconhecedor for alterada.

    Use InkRecognizerContainer para chamar SetDefaultRecognizer com base no reconhecedor selecionado da caixa de combinação do reconhecedor.

    // Handle recognizer change.
        private void comboInstalledRecognizers_SelectionChanged(
            object sender, SelectionChangedEventArgs e)
        {
            inkRecognizerContainer.SetDefaultRecognizer(
                (InkRecognizer)comboInstalledRecognizers.SelectedItem);
        }
    
  5. Por último, fazemos o reconhecimento de manuscrito com base no reconhecedor de manuscrito selecionado. Para este exemplo, usamos o manipulador de eventos de clique do botão "Reconhecer" para executar o reconhecimento de manuscrito.

    // 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);
    
    • Cada objeto InkRecognitionResult contém um conjunto de candidatos de texto. O item mais importante dessa lista é considerado pelo mecanismo de reconhecimento como o melhor correspondente, seguido pelos demais candidatos em ordem decrescente de confiança.

      Iteramos através de cada InkRecognitionResult e compilamos a lista de candidatos. Os candidatos são exibidos, e InkStrokeContainer é limpo (o que também limpa 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();
    
    • Aqui está o exemplo do manipulador de cliques na íntegra.
    // 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.";
        }
    }
    

Reconhecimento dinâmico

Embora ambos os exemplos anteriores exijam que o usuário pressione um botão para iniciar o reconhecimento, você também pode executar o reconhecimento dinâmico usando a entrada de traçado emparelhada com uma função de temporização básica.

Para este exemplo, usaremos as mesmas configurações de interface do usuário e traços do exemplo de reconhecimento internacional anterior.

  1. Esses objetos globais (InkAnalyzer, InkStroke, InkAnalysisResult, DispatcherTimer) são usados em todo o nosso aplicativo.

    // Stroke recognition globals.
    InkAnalyzer inkAnalyzer;
    DispatcherTimer recoTimer;
    
  2. Em vez de um botão para iniciar o reconhecimento, adicionamos ouvintes para dois eventos de traço de InkPresenter (StrokesCollected e StrokeStarted) e configuramos um temporizador básico (DispatcherTimer) com um intervalo de Tick de um segundo.

    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. Em seguida, definimos os manipuladores para os eventos InkPresenter que declaramos na primeira etapa (também substituímos o evento de página OnNavigatingFrom para gerenciar nosso temporizador).

    • StrokesCollected
      Adicione traços de tinta (AddDataForStrokes) a InkAnalyzer e inicie o temporizador de reconhecimento quando o usuário parar de usar tinta digital (levantando a caneta ou o dedo ou soltando o botão do mouse). O reconhecimento é iniciado após um segundo sem entrada de tinta.

      Use o método SetStrokeDataKind para especificar se está interessado apenas em texto (incluindo estrutura do documento e listas de marcadores) ou apenas em desenhos (incluindo reconhecimento de forma).

    • StrokeStarted
      Se um novo traço começar antes do próximo evento de tique do temporizador, pare o temporizador, pois o novo traço provavelmente será a continuação de uma única entrada de manuscrito.

    // 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. Por último, realizamos o reconhecimento de manuscrito. Para este exemplo, usamos o manipulador de eventos Tick de um DispatcherTimer para iniciar o reconhecimento de manuscrito.

    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();
        }
    }
    

Amostras de tópico

Outras amostras