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)
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>
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:
Em seguida, especificamos nossas variáveis globais:
InkAnalyzer inkAnalyzer = new InkAnalyzer(); IReadOnlyList<InkStroke> inkStrokes = null; InkAnalysisResult inkAnalysisResults = null;
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; }
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(); } } }
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); }
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 |
---|---|
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
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>
Para este exemplo, primeiro você precisa adicionar as referências de tipo de namespace necessárias para nossa funcionalidade de tinta:
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; }
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.
- Um InkPresenter armazena todos os traços de tinta em um objeto InkStrokeContainer. Os traços são expostos por meio da propriedade StrokeContainer de InkPresenter e recuperados usando o método GetStrokes.
// Get all strokes on the InkCanvas. IReadOnlyList<InkStroke> currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
- Um InkRecognizerContainer é criado para gerenciar o processo de reconhecimento de manuscrito.
// Create a manager for the InkRecognizer object // used in handwriting recognition. InkRecognizerContainer inkRecognizerContainer = new InkRecognizerContainer();
- RecognizeAsync é chamado para recuperar um conjunto de objetos InkRecognitionResult. Os resultados de reconhecimento são produzidos para cada palavra detectada por um InkRecognizer.
// 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:
- Acesse Configurações > Hora e idioma > Região e idioma.
- Selecione Adicionar um idioma.
- Selecione um idioma na lista e escolha a versão da região. O idioma agora é listado na página Região e idioma.
- Clique no idioma e selecione Opções.
- 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.
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>
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; }
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; } }
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); }
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.
- Um InkPresenter armazena todos os traços de tinta em um objeto InkStrokeContainer. Os traços são expostos por meio da propriedade StrokeContainer de InkPresenter e recuperados usando o método GetStrokes.
// Get all strokes on the InkCanvas. IReadOnlyList<InkStroke> currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
RecognizeAsync é chamado para recuperar um conjunto de objetos InkRecognitionResult.
Os resultados de reconhecimento são produzidos para cada palavra detectada por um InkRecognizer.
// 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.
Esses objetos globais (InkAnalyzer, InkStroke, InkAnalysisResult, DispatcherTimer) são usados em todo o nosso aplicativo.
// Stroke recognition globals. InkAnalyzer inkAnalyzer; DispatcherTimer recoTimer;
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; }
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(); }
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.
- Chame AnalyzeAsync para iniciar a análise de tinta e obter o InkAnalysisResult.
- Se Status retornar Updated, chame FindNodes para tipos de nó de InkAnalysisNodeKind.InkWord.
- Itere nos nós e exiba o texto reconhecido.
- Por último, exclua os nós reconhecidos de InkAnalyzer e os traços de tinta correspondentes da tela 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(); } }
Artigos relacionados
Amostras de tópico
Outras amostras
Windows developer