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 entrada de lápiz

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 de una sola línea, como la entrada del formulario, consulte Reconocimiento de escritura a mano restringido 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.

Descargue este ejemplo de Muestra de análisis de entrada lápiz (básico)

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

    La interfaz de usuario incluye un botón "Recognize", InkCanvas, y un lienzo estándar. Cuando se presiona el botón "Recognize", 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 espacio de nombres necesarias para nuestra funcionalidad de análisis de entrada de lápiz y entrada de lápiz:

  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:

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

    • En primer lugar, llame a GetStrokes en StrokeContainer de 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 el texto (incluida la estructura de documentos y las listas de viñetas) o solo en dibujos (incluido el reconocimiento de formas).
    • Llame a AnalyzeAsync para iniciar el análisis de entrada de lápiz y obtener InkAnalysisResult.
    • Si Status devuelve un estado actualizado, llame a FindNodes para InkAnalysisNodeKind.InkWord y nkAnalysisNodeKind.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 lápiz correspondientes del lienzo de entrada de lápiz.
    /// <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 de entrada de lápiz 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 puntos suspensivos y polígonos en nuestro lienzo de reconocimiento. Usamos el rectángulo delimitador del trazo de entrada de lápiz asociado en el lienzo de lápiz 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
Before analysis After analysis

Reconocimiento de escritura a mano restringida

En la sección anterior (reconocimiento de forma libre con análisis de entrada de lápiz), se mostró cómo usar las API de análisis de entrada de lápiz para analizar y reconocer trazos de lápiz arbitrarios dentro de un área 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 control InkCanvas en 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 de forma libre con análisis de entrada de lápiz.

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

Descargue este ejemplo del ejemplo de reconocimiento de escritura a mano de lápiz

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

    La interfaz de usuario incluye un botón "Recognize", InkCanvas y 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 entrada de lápiz:

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

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

    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();
    
    • Se crea un control InkRecognizerContainer para administrar el proceso de reconocimiento de escritura a mano.
    // 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 contiene un conjunto de candidatos de texto. El elemento más alto de esta lista se considera que el motor de reconocimiento es la mejor coincidencia, seguido de los candidatos restantes para disminuir la confianza.

      Recorremos en iteración cada InkRecognitionResult y compilamos la lista de candidatos. A continuación, se muestran los candidatos y se borra InkStrokeContainer (que también borra 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 InkRecognizer.

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 (también puede descargar el paquete de idioma completo, el motor de reconocimiento de voz y el diseño del teclado aquí).

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 "Recognize", un cuadro combinado que enumera todos los reconocedores de escritura a mano instalados, InkCanvas, y 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.

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

    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 agentes de escucha para el evento de clic en el botón "Recognize" y el evento de selección cambiado 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. Rellenamos el cuadro combinado del reconocedor con una lista de reconocedores de escritura a mano instalados.

    Se crea un control 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 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 InkRecognizerContainer para llamar a SetDefaultRecognizer en función del reconocedor seleccionado del cuadro combinado reconocedor.

    // 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 contiene un conjunto de candidatos de texto. El elemento más alto de esta lista se considera que el motor de reconocimiento es la mejor coincidencia, seguido de los candidatos restantes para disminuir la confianza.

      Recorremos en iteración cada InkRecognitionResult y compilamos la lista de candidatos. A continuación, se muestran los candidatos y se borra InkStrokeContainer (que también borra 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 el ejemplo de reconocimiento internacional anterior.

  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 intervalo de tic de un 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. 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).

    • StrokesCollected
      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 ratón). Después de un segundo sin entrada de lápiz, se inicia el reconocimiento.

      Use el método SetStrokeDataKind para especificar si solo le interesa el texto (incluidas las listas de viñetas amd de estructura de documentos) o solo en dibujos (incluido el reconocimiento de formas).

    • StrokeStarted
      Si se inicia un nuevo trazo antes del siguiente evento de graduación del temporizador, detenga el temporizador, ya que es probable que el nuevo trazo sea la continuación de una sola 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 Tic de DispatcherTimer para iniciar el reconocimiento de escritura a mano.

    • Llame a AnalyzeAsync para iniciar el análisis de entrada de lápiz y obtener InkAnalysisResult.
    • Si Status devuelve un estado de Actualizado, llame a FindNodes para los tipos de nodo 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 lápiz correspondientes del lienzo de entrada de lápiz.
    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 del tema

Otros ejemplos