Compartir a través de


Reconocer trazos de Windows Ink como texto y formas

Convierta trazos de lápiz en texto y formas mediante las funcionalidades de reconocimiento integradas en Windows Ink.

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

Reconocimiento de forma libre con análisis de tinta digital

Aquí se muestra cómo usar el motor de análisis de Windows Ink (Windows.UI.Input.Inking.Analysis) para clasificar, analizar y reconocer un conjunto de trazos de forma libre en un InkCanvas como texto o formas. (Además del reconocimiento de texto y formas, también se puede usar el análisis de lápiz para reconocer la estructura del documento, las listas de viñetas y los dibujos genéricos).

Nota:

Para escenarios básicos de texto sin formato y de una sola línea, como la entrada de formularios, consulte Reconocimiento de Escritura a Mano Restringida más adelante en este tema.

En este ejemplo, el reconocimiento se inicia cuando el usuario hace clic en un botón para indicar que ha terminado de dibujar.

Descargar este ejemplo de ejemplo de análisis de tinta (básico)

  1. En primer lugar, configuramos la interfaz de usuario (MainPage.xaml).

    La interfaz de usuario incluye un botón "Reconocer", un InkCanvasy un Canvas estándar. Cuando se presiona el botón "Reconocer", se analizan todos los trazos de lápiz en el lienzo de lápiz y se dibujan en el lienzo estándar (si se reconoce) las formas y el texto correspondientes. Los trazos de lápiz originales se eliminan del lienzo de lápiz.

    <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. En el archivo de código subyacente de la interfaz de usuario (MainPage.xaml.cs), agregue las referencias de tipo de namespace necesarias para nuestra funcionalidad de tinta y análisis de tinta.

  3. A continuación, especificamos nuestras variables globales:

     InkAnalyzer inkAnalyzer = new InkAnalyzer();
     IReadOnlyList<InkStroke> inkStrokes = null;
     InkAnalysisResult inkAnalysisResults = null;
    
  4. A continuación, establecemos algunos comportamientos básicos de entrada de lápiz:

    • El InkPresenter está configurado para interpretar los datos de entrada del lápiz, el mouse y la entrada táctil como trazos de tinta (InputDeviceTypes).
    • Los trazos de tinta se representan en el InkCanvas mediante el InkDrawingAttributes.
    • También se declara un agente de escucha para el evento de clic en el botón "Reconocer".
    /// <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. En este ejemplo, se realiza el análisis de tinta en el controlador de eventos clic del botón "Recognize".

    • En primer lugar, llame a GetStrokes en el StrokeContainer del InkCanvas.InkPresenter para obtener la colección de todos los trazos de lápiz actuales.
    • Si hay trazos de lápiz presentes, páselos en una llamada a AddDataForStrokes de InkAnalyzer.
    • Estamos intentando reconocer tanto dibujos como texto, pero puede usar el método SetStrokeDataKind para especificar si solo está interesado en texto (incluida la estructura del documento y las listas de viñetas) o solo en dibujos (incluido el reconocimiento de formas).
    • Llame a AnalyzeAsync para iniciar el análisis de tinta y obtener el InkAnalysisResult.
    • Si Estado devuelve un estado de Actualizado, llame a FindNodes para ambos InkAnalysisNodeKind.InkWord y InkAnalysisNodeKind.InkDrawing.
    • Recorrer en iteración ambos conjuntos de tipos de nodo y dibujar el texto o la forma correspondientes en el lienzo de reconocimiento (debajo del lienzo de entrada de lápiz).
    • Por último, elimine los nodos reconocidos de InkAnalyzer y los trazos de tinta correspondientes del lienzo 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. Esta es la función para dibujar un TextBlock en nuestro lienzo de reconocimiento. Usamos el rectángulo delimitador del trazo asociado en el lienzo de lápiz para establecer la posición y el tamaño de fuente del 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 son las funciones para dibujar elipses y polígonos en nuestro lienzo de reconocimiento. Usamos el rectángulo delimitador del trazo de tinta asociado en el lienzo de tinta para establecer la posición y el tamaño de fuente de las 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);
     }
    

Este es este ejemplo en acción:

Antes del análisis Después del análisis
antes del análisis después del análisis

Reconocimiento restringido de la escritura a mano

En la sección anterior (Reconocimiento de forma libre con análisis de tinta), demostramos cómo usar las API de análisis de tinta de para analizar y reconocer trazos de tinta arbitrarios dentro de un área de InkCanvas.

En esta sección, se muestra cómo usar el motor de reconocimiento de escritura a mano de Windows Ink (no el análisis de entrada de lápiz) para convertir un conjunto de trazos en un InkCanvas al texto (basado en el paquete de idioma predeterminado instalado).

Nota:

El reconocimiento de escritura a mano básico que se muestra en esta sección es más adecuado para escenarios de entrada de texto de una sola línea, como la entrada de formulario. Para escenarios de reconocimiento más enriquecidos que incluyen análisis e interpretación de la estructura de documentos, elementos de lista, formas y dibujos (además del reconocimiento de texto), consulte la sección anterior: reconocimiento libre con análisis de tinta.

En este ejemplo, el reconocimiento se inicia cuando el usuario hace clic en un botón para indicar que ha terminado de escribir.

Descarga esta muestra del ejemplo de reconocimiento de escritura a mano con Ink

  1. En primer lugar, configuramos la interfaz de usuario.

    La interfaz de usuario incluye un botón "Reconocer", el InkCanvasy un área para mostrar los resultados del reconocimiento.

    <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 ejemplo, primero debe agregar las referencias de tipo de espacio de nombres necesarias para nuestra funcionalidad de tinta.

  3. A continuación, establecemos algunos comportamientos básicos de entrada de lápiz.

    El InkPresenter está configurado para interpretar los datos de entrada del lápiz y el mouse como trazos de tinta (InputDeviceTypes). Los trazos de tinta se representan en el InkCanvas mediante el InkDrawingAttributes. También se declara un agente de escucha para el evento de clic en el botón "Reconocer".

    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 el reconocimiento básico de escritura a mano. En este ejemplo, usamos el controlador de eventos click del botón "Recognize" para realizar el reconocimiento de escritura a mano.

    // 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 incluye un conjunto de candidatos de texto. El elemento más alto de esta lista es considerado por el motor de reconocimiento como la mejor coincidencia, seguido de los candidatos restantes en orden de confianza decreciente.

      Recorremos en iteración cada inkRecognitionResult y compilamos la lista de candidatos. A continuación, se muestran los candidatos y se limpia el InkStrokeContainer (lo cual también limpia el 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();
    
    • Este es el ejemplo del controlador de clics, en su totalidad.
    // 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.";
            }
        }
    

Reconocimiento internacional

El reconocimiento de escritura a mano integrado en la plataforma de entrada de lápiz de Windows incluye un amplio subconjunto de configuraciones regionales y lenguajes compatibles con Windows.

Consulte el tema de la propiedad InkRecognizer.Name para obtener una lista de los idiomas admitidos por el InkRecognizer de .

La aplicación puede consultar el conjunto de motores de reconocimiento de escritura a mano instalados y usar uno de ellos o permitir que un usuario seleccione su idioma preferido.

Nota Los usuarios pueden ver una lista de idiomas instalados; para ello, vaya a Configuración -> Hora e Idioma. Los idiomas instalados aparecen en Idiomas.

Para instalar nuevos paquetes de idioma y habilitar el reconocimiento de escritura a mano para ese idioma:

  1. Vaya a Configuración > hora e idioma > región e idioma.
  2. Seleccione Agregar un idioma.
  3. Seleccione un idioma de la lista y elija la versión de la región. El idioma aparece ahora en la página Región e idioma .
  4. Haga clic en el idioma y seleccione Opciones.
  5. En la página Opciones de idioma, descargue el motor de reconocimiento de escritura a mano (aquí también puede descargar el paquete de idioma completo, el motor de reconocimiento de voz y el diseño del teclado).

Aquí se muestra cómo usar el motor de reconocimiento de escritura a mano para interpretar un conjunto de trazos en un InkCanvas basado en el reconocedor seleccionado.

El usuario inicia el reconocimiento al hacer clic en un botón cuando termine de escribir.

  1. En primer lugar, configuramos la interfaz de usuario.

    La interfaz de usuario incluye un botón "Reconocer", un cuadro combinado que enumera todos los reconocedores de escritura a mano instalados, el InkCanvasy un área para mostrar los resultados del reconocimiento.

    <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. A continuación, establecemos algunos comportamientos básicos de entrada de lápiz.

    El InkPresenter está configurado para interpretar los datos de entrada del lápiz y el mouse como trazos de tinta (InputDeviceTypes). Los trazos de tinta se representan en el InkCanvas mediante el InkDrawingAttributes.

    Llamamos a una función InitializeRecognizerList para rellenar el cuadro combinado del reconocedor con una lista de reconocedores de escritura a mano instalados.

    También declaramos escuchadores para el evento de clic en el botón "Reconocer" y el evento de cambio de selección en el cuadro combinado reconocedor.

     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. Llenamos la lista desplegable con una lista de reconocedores de escritura a mano instalados.

    Se crea un InkRecognizerContainer para administrar el proceso de reconocimiento de escritura a mano. Use este objeto para llamar a GetRecognizers y recuperar la lista de reconocedores instalados para rellenar el cuadro combinado del reconocedor.

    // 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. Actualice el reconocedor de escritura a mano si cambia la selección del cuadro combinado del reconocedor.

    Use el InkRecognizerContainer para llamar a SetDefaultRecognizer en función del reconocedor seleccionado del cuadro combinado de reconocedores.

    // Handle recognizer change.
        private void comboInstalledRecognizers_SelectionChanged(
            object sender, SelectionChangedEventArgs e)
        {
            inkRecognizerContainer.SetDefaultRecognizer(
                (InkRecognizer)comboInstalledRecognizers.SelectedItem);
        }
    
  5. Por último, realizamos el reconocimiento de escritura a mano en función del reconocedor de escritura a mano seleccionado. En este ejemplo, usamos el controlador de eventos click del botón "Recognize" para realizar el reconocimiento de escritura a mano.

    // 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 incluye un conjunto de candidatos de texto. El elemento más alto de esta lista es considerado por el motor de reconocimiento como la mejor coincidencia, seguido de los candidatos restantes en orden de confianza decreciente.

      Recorremos en iteración cada inkRecognitionResult y compilamos la lista de candidatos. A continuación, se muestran los candidatos y se limpia el InkStrokeContainer (lo cual también limpia el 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();
    
    • Este es el ejemplo del controlador de clics, en su totalidad.
    // 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.";
        }
    }
    

Reconocimiento dinámico

Aunque los dos ejemplos anteriores requieren que el usuario presione un botón para iniciar el reconocimiento, también puede realizar el reconocimiento dinámico mediante la entrada de trazo emparejada con una función de control de tiempo básica.

En este ejemplo, usaremos la misma configuración de interfaz de usuario y trazo que en el ejemplo anterior de reconocimiento internacional.

  1. Estos objetos globales (InkAnalyzer, InkStroke, InkAnalysisResult, DispatcherTimer) se usan en toda la aplicación.

    // Stroke recognition globals.
    InkAnalyzer inkAnalyzer;
    DispatcherTimer recoTimer;
    
  2. En lugar de un botón para iniciar el reconocimiento, agregamos agentes de escucha para dos eventos de trazo InkPresenter (StrokesCollected y StrokeStarted) y configuramos un temporizador básico (DispatcherTimer) con un segundo intervalo de 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. A continuación, definimos los controladores para los eventos InkPresenter que declaramos en el primer paso (también invalidamos el evento de página OnNavigatingFrom para administrar nuestro temporizador).

    • TrazosRecogidos
      Agregue trazos de lápiz (AddDataForStrokes) a InkAnalyzer e inicie el temporizador de reconocimiento cuando el usuario deje de iniciar la entrada manuscrita (levantando su lápiz o dedo, o liberando el botón del mouse). Después de un segundo sin entrada de lápiz, se inicia el reconocimiento.

      Use el método SetStrokeDataKind para especificar si está interesado solo en el texto (incluidas las listas de viñetas y la estructura de documentos) o solo en los dibujos (incluido el reconocimiento de formas).

    • trazoIniciado
      Si se inicia un nuevo trazo antes del siguiente evento de tic del temporizador, detenga el temporizador, ya que es probable que el nuevo trazo sea la continuación de una única entrada de escritura a mano.

    // 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 el reconocimiento de escritura a mano. En este ejemplo, usamos el controlador de eventos Tick de un DispatcherTimer para iniciar el reconocimiento de escritura a mano.

    • Llame a AnalyzeAsync para iniciar el análisis de tinta y obtener el InkAnalysisResult.
    • Si Status devuelve un estado de Updated, llame a FindNodes para los tipos de nodo de InkAnalysisNodeKind.InkWord.
    • Recorre en iteración los nodos y muestra el texto reconocido.
    • Por último, elimine los nodos reconocidos de InkAnalyzer y los trazos de tinta correspondientes del lienzo de tinta.
    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();
        }
    }
    

Ejemplos de temas

Otros ejemplos