將 Windows Ink 筆劃辨識為文字和圖案 \(部分機器翻譯\)
使用 Windows Ink 內建的辨識功能,將筆跡筆劃轉換成文字和圖形。
Important APIs:InkCanvas、Windows.UI.Input.Inking
使用筆跡分析進行自由格式辨識
在此處,我們將示範如何使用 Windows Ink 分析引擎 (Windows.UI.Input.Inking.Analysis) 將 InkCanvas 上的一組自由格式筆劃分類、分析和辨識為文字或圖形。 (除了文字和圖形辨識之外,筆跡分析還可以用來辨識文件結構、項目符號清單和一般繪圖。)
注意
對於基本、單行、純文字案例 (例如表單輸入),請參閱本主題後面的受限手寫辨識。
在此範例中,當使用者按一下按鈕以指出完成繪圖時,就會起始辨識。
首先,我們設定 UI (MainPage.xaml)。
UI 包含 [辨識] 按鈕、InkCanvas 和標準 Canvas。 按下 [辨識] 按鈕時,會分析筆跡畫布上的所有筆跡筆劃,並在標準畫布上繪製對應的圖形和文字。。 然後,原始筆跡筆劃會從筆跡畫布中刪除。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0"> <TextBlock x:Name="Header" Text="Basic ink analysis sample" Style="{ThemeResource HeaderTextBlockStyle}" Margin="10,0,0,0" /> <Button x:Name="recognize" Content="Recognize" Margin="50,0,10,0"/> </StackPanel> <Grid x:Name="drawingCanvas" Grid.Row="1"> <!-- The canvas where we render the replacement text and shapes. --> <Canvas x:Name="recognitionCanvas" /> <!-- The canvas for ink input. --> <InkCanvas x:Name="inkCanvas" /> </Grid> </Grid>
在 UI 程式碼後置檔案 (MainPage.xaml.cs),新增筆跡和筆跡分析功能所需的命名空間類型參考:
然後,我們會指定全域變數:
InkAnalyzer inkAnalyzer = new InkAnalyzer(); IReadOnlyList<InkStroke> inkStrokes = null; InkAnalysisResult inkAnalysisResults = null;
接下來,我們會設定一些基本的筆跡輸入行為:
- InkPresenter 設定為將來自筆、滑鼠和觸控的輸入資料解釋為筆跡筆劃 (InputDeviceTypes)。
- 筆跡筆劃會使用指定的 InkDrawingAttributes 在 InkCanvas 上轉譯。
- 也會宣告「辨識」按鈕上 Click 事件的接聽程式。
/// <summary> /// Initialize the UI page. /// </summary> public MainPage() { this.InitializeComponent(); // Set supported inking device types. inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse | Windows.UI.Core.CoreInputDeviceTypes.Pen | Windows.UI.Core.CoreInputDeviceTypes.Touch; // Set initial ink stroke attributes. InkDrawingAttributes drawingAttributes = new InkDrawingAttributes(); drawingAttributes.Color = Windows.UI.Colors.Black; drawingAttributes.IgnorePressure = false; drawingAttributes.FitToCurve = true; inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes); // Listen for button click to initiate recognition. recognize.Click += RecognizeStrokes_Click; }
在此範例中,我們會在 [辨識] 按鈕的 Click 事件處理常式中執行筆跡分析。
- 首先,在 InkCanvas.InkPresenter 的 StrokeContainer 上呼叫 GetStrokes,以取得所有目前筆跡筆劃的集合。
- 如果筆跡筆劃存在,請在 InkAnalyzer 的 AddDataForStrokes 呼叫中傳遞筆劃。
- 我們嘗試同時辨識繪圖和文字,但您可以使用 SetStrokeDataKind 方法來指定您只對文字感興趣 (包括文件結構和項目符號清單) 或只在繪圖中 (包括圖形辨識)。
- 呼叫 AnalyzeAsync 以起始筆跡分析並取得 InkAnalysisResult。
- 如果 Status 傳回 Updated 的狀態,請針對 InkAnalysisNodeKind.InkWord 和 InkAnalysisNodeKind.InkDrawing 呼叫 FindNodes。
- 逐一查看這兩組節點類型,並在辨識畫布上繪製個別的文字或圖形 (在筆跡畫布下方)。
- 最後,從 InkAnalyzer 刪除已辨識的節點,並從筆跡畫布中刪除對應的筆跡筆劃。
/// <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(); } } }
以下是在辨識畫布上繪製 TextBlock 的函式。 我們會使用筆跡畫布上相關聯筆墨筆劃的週框,來設定 TextBlock 的位置和字型大小。
/// <summary> /// Draw ink recognition text string on the recognitionCanvas. /// </summary> /// <param name="recognizedText">The string returned by text recognition.</param> /// <param name="boundingRect">The bounding rect of the original ink writing.</param> private void DrawText(string recognizedText, Rect boundingRect) { TextBlock text = new TextBlock(); Canvas.SetTop(text, boundingRect.Top); Canvas.SetLeft(text, boundingRect.Left); text.Text = recognizedText; text.FontSize = boundingRect.Height; recognitionCanvas.Children.Add(text); }
以下是在辨識畫布上繪製省略號和多邊形的函式。 我們會使用筆跡畫布上相關聯筆墨筆劃的週框,來設定形狀的位置和字型大小。
// Draw an ellipse on the recognitionCanvas. private void DrawEllipse(InkAnalysisInkDrawing shape) { var points = shape.Points; Ellipse ellipse = new Ellipse(); ellipse.Width = shape.BoundingRect.Width; ellipse.Height = shape.BoundingRect.Height; Canvas.SetTop(ellipse, shape.BoundingRect.Top); Canvas.SetLeft(ellipse, shape.BoundingRect.Left); var brush = new SolidColorBrush(Windows.UI.ColorHelper.FromArgb(255, 0, 0, 255)); ellipse.Stroke = brush; ellipse.StrokeThickness = 2; recognitionCanvas.Children.Add(ellipse); } // Draw a polygon on the recognitionCanvas. private void DrawPolygon(InkAnalysisInkDrawing shape) { List<Point> points = new List<Point>(shape.Points); Polygon polygon = new Polygon(); foreach (Point point in points) { polygon.Points.Add(point); } var brush = new SolidColorBrush(Windows.UI.ColorHelper.FromArgb(255, 0, 0, 255)); polygon.Stroke = brush; polygon.StrokeThickness = 2; recognitionCanvas.Children.Add(polygon); }
以下是此動作中的範例:
分析之前 | 分析之後 |
---|---|
限制手寫辨識
在上一節中 (使用筆跡分析進行自由格式辨識),我們示範如何使用筆跡分析 API 來分析和辨識 InkCanvas 區域內的任意筆跡筆劃。
在本節中,我們將示範如何使用 Windows Ink 手寫辨識引擎 (而非筆跡分析) 將 InkCanvas 上的一組筆劃轉換成文字 (根據已安裝的預設語言套件)。
注意
本節中顯示的基本手寫辨識最適合單行文字輸入案例 (例如表單輸入)。 如需更豐富的辨識案例,包括文件結構、清單專案、圖形和繪圖的分析與解譯 (除了文字辨識之外),請參閱上一節:使用筆跡分析進行自由格式辨識。
在此範例中,當使用者按一下按鈕以指出完成書寫時,就會起始辨識。
首先,我們會設定 UI。
UI 包含 [辨識] 按鈕、InkCanvas 和顯示辨識結果的區域。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0"> <TextBlock x:Name="Header" Text="Basic ink recognition sample" Style="{ThemeResource HeaderTextBlockStyle}" Margin="10,0,0,0" /> <Button x:Name="recognize" Content="Recognize" Margin="50,0,10,0"/> </StackPanel> <Grid Grid.Row="1"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <InkCanvas x:Name="inkCanvas" Grid.Row="0"/> <TextBlock x:Name="recognitionResult" Grid.Row="1" Margin="50,0,10,0"/> </Grid> </Grid>
在此範例中,您必須先新增筆跡功能所需的命名空間類型參考:
然後,我們會設定一些基本的筆跡輸入行為。
InkPresenter 配置為將來自筆和滑鼠的輸入資料解釋為筆跡筆劃 (InputDeviceTypes)。 筆跡筆劃會使用指定的 InkDrawingAttributes 在 InkCanvas 上轉譯。 也會宣告「辨識」按鈕上 Click 事件的接聽程式。
public MainPage() { this.InitializeComponent(); // Set supported inking device types. inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse | Windows.UI.Core.CoreInputDeviceTypes.Pen; // Set initial ink stroke attributes. InkDrawingAttributes drawingAttributes = new InkDrawingAttributes(); drawingAttributes.Color = Windows.UI.Colors.Black; drawingAttributes.IgnorePressure = false; drawingAttributes.FitToCurve = true; inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes); // Listen for button click to initiate recognition. recognize.Click += Recognize_Click; }
最後,我們會執行基本的手寫辨識。 在此範例中,我們使用 [辨識] 按鈕的 click 事件處理常式來執行手寫辨識。
- InkPresenter 將所有筆跡筆劃儲存在 InkStrokeContainer 物件中。 筆劃透過 InkPresenter 的 StrokeContainer 屬性公開,並使用 GetStrokes 方法檢索。
// Get all strokes on the InkCanvas. IReadOnlyList<InkStroke> currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
- 建立 InkRecognizerContainer 來管理手寫辨識程序。
// Create a manager for the InkRecognizer object // used in handwriting recognition. InkRecognizerContainer inkRecognizerContainer = new InkRecognizerContainer();
- 會呼叫 RecognizeAsync 來擷取一組 InkRecognitionResult 物件。 辨識結果會針對 InkRecognizer 偵測到的每個字產生。
// Recognize all ink strokes on the ink canvas. IReadOnlyList<InkRecognitionResult> recognitionResults = await inkRecognizerContainer.RecognizeAsync( inkCanvas.InkPresenter.StrokeContainer, InkRecognitionTarget.All);
每個 InkRecognitionResult 物件都包含一組文字候選項目。 此清單中的最上層專案會被視為辨識引擎的最佳相符項目,後面接著剩餘的候選項目,以降低信賴度。
我們會逐一查看每個 InkRecognitionResult,並編譯候選項目清單。 然後會顯示候選項目,並清除 InkStrokeContainer (這也清除 InkCanvas)。
string str = "Recognition result\n"; // Iterate through the recognition results. foreach (var result in recognitionResults) { // Get all recognition candidates from each recognition result. IReadOnlyList<string> candidates = result.GetTextCandidates(); str += "Candidates: " + candidates.Count.ToString() + "\n"; foreach (string candidate in candidates) { str += candidate + " "; } } // Display the recognition candidates. recognitionResult.Text = str; // Clear the ink canvas once recognition is complete. inkCanvas.InkPresenter.StrokeContainer.Clear();
- 以下是完整按一下處理常式範例。
// Handle button click to initiate recognition. private async void Recognize_Click(object sender, RoutedEventArgs e) { // Get all strokes on the InkCanvas. IReadOnlyList<InkStroke> currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes(); // Ensure an ink stroke is present. if (currentStrokes.Count > 0) { // Create a manager for the InkRecognizer object // used in handwriting recognition. InkRecognizerContainer inkRecognizerContainer = new InkRecognizerContainer(); // inkRecognizerContainer is null if a recognition engine is not available. if (!(inkRecognizerContainer == null)) { // Recognize all ink strokes on the ink canvas. IReadOnlyList<InkRecognitionResult> recognitionResults = await inkRecognizerContainer.RecognizeAsync( inkCanvas.InkPresenter.StrokeContainer, InkRecognitionTarget.All); // Process and display the recognition results. if (recognitionResults.Count > 0) { string str = "Recognition result\n"; // Iterate through the recognition results. foreach (var result in recognitionResults) { // Get all recognition candidates from each recognition result. IReadOnlyList<string> candidates = result.GetTextCandidates(); str += "Candidates: " + candidates.Count.ToString() + "\n"; foreach (string candidate in candidates) { str += candidate + " "; } } // Display the recognition candidates. recognitionResult.Text = str; // Clear the ink canvas once recognition is complete. inkCanvas.InkPresenter.StrokeContainer.Clear(); } else { recognitionResult.Text = "No recognition results."; } } else { Windows.UI.Popups.MessageDialog messageDialog = new Windows.UI.Popups.MessageDialog("You must install handwriting recognition engine."); await messageDialog.ShowAsync(); } } else { recognitionResult.Text = "No ink strokes to recognize."; } }
國際辨識
Windows 筆跡平台內建的手寫辨識包含 Windows 所支援地區設定和語言的廣泛子集。
如需 InkRecognizer 支援的語言清單,請參閱 InkRecognizer.Name 屬性主題。
您的應用程式可以查詢已安裝的手寫辨識引擎集,並使用其中一個引擎,或讓使用者選取他們慣用的語言。
注意:使用者可以前往設定 - >時間&語言來查看已安裝的語言清單。 已安裝的語言列在 [語言] 底下。
若要安裝新的語言套件,並啟用該語言的手寫辨識:
- 前往設定>時間&語言>區域&語言。
- 選取新增語言。
- 從清單中選取語言,然後選擇區域版本。 語言現在會列在 [區域&語言] 頁面上。
- 按一下語言,然後選取 [選項]。
- 在 [語言選項] 頁面上,下載手寫辨識引擎 (您也可以在此處下載完整的語言套件、語音辨識引擎和鍵盤配置)。
在此處,我們將示範如何使用手寫辨識引擎,根據選取的識別器來解譯 InkCanvas 上的一組筆劃。
當使用者完成寫入時,按一下按鈕就會起始辨識。
首先,我們會設定 UI。
UI 包含 [辨識] 按鈕、列出所有已安裝手寫識別器的下拉式方塊、InkCanvas,以及顯示辨識結果的區域。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0"> <TextBlock x:Name="Header" Text="Advanced international ink recognition sample" Style="{ThemeResource HeaderTextBlockStyle}" Margin="10,0,0,0" /> <ComboBox x:Name="comboInstalledRecognizers" Margin="50,0,10,0"> <ComboBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}" /> </StackPanel> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> <Button x:Name="buttonRecognize" Content="Recognize" IsEnabled="False" Margin="50,0,10,0"/> </StackPanel> <Grid Grid.Row="1"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <InkCanvas x:Name="inkCanvas" Grid.Row="0"/> <TextBlock x:Name="recognitionResult" Grid.Row="1" Margin="50,0,10,0"/> </Grid> </Grid>
然後,我們會設定一些基本的筆跡輸入行為。
InkPresenter 配置為將來自筆和滑鼠的輸入資料解釋為筆跡筆劃 (InputDeviceTypes)。 筆跡筆劃會使用指定的 InkDrawingAttributes 在 InkCanvas 上轉譯。
我們呼叫
InitializeRecognizerList
函式來填入識別器下拉式方塊,其中包含已安裝的手寫識別器清單。我們也會在 [辨識] 按鈕上宣告 Click 事件的接聽程式,並在識別器下拉式方塊上宣告選取範圍變更事件。
public MainPage() { this.InitializeComponent(); // Set supported inking device types. inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse | Windows.UI.Core.CoreInputDeviceTypes.Pen; // Set initial ink stroke attributes. InkDrawingAttributes drawingAttributes = new InkDrawingAttributes(); drawingAttributes.Color = Windows.UI.Colors.Black; drawingAttributes.IgnorePressure = false; drawingAttributes.FitToCurve = true; inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes); // Populate the recognizer combo box with installed recognizers. InitializeRecognizerList(); // Listen for combo box selection. comboInstalledRecognizers.SelectionChanged += comboInstalledRecognizers_SelectionChanged; // Listen for button click to initiate recognition. buttonRecognize.Click += Recognize_Click; }
我們會在識別器下拉式方塊中填入已安裝手寫識別器的清單。
建立 InkRecognizerContainer 來管理手寫辨識程序。 使用此物件呼叫 GetRecognizers 並擷取已安裝的識別器清單,以填入識別器下拉式方塊。
// Populate the recognizer combo box with installed recognizers. private void InitializeRecognizerList() { // Create a manager for the handwriting recognition process. inkRecognizerContainer = new InkRecognizerContainer(); // Retrieve the collection of installed handwriting recognizers. IReadOnlyList<InkRecognizer> installedRecognizers = inkRecognizerContainer.GetRecognizers(); // inkRecognizerContainer is null if a recognition engine is not available. if (!(inkRecognizerContainer == null)) { comboInstalledRecognizers.ItemsSource = installedRecognizers; buttonRecognize.IsEnabled = true; } }
如果識別器下拉式方塊選取範圍變更,請更新手寫識別器。
使用 InkRecognizerContainer 根據從識別器下拉式方塊中選取的識別器呼叫 SetDefaultRecognizer。
// Handle recognizer change. private void comboInstalledRecognizers_SelectionChanged( object sender, SelectionChangedEventArgs e) { inkRecognizerContainer.SetDefaultRecognizer( (InkRecognizer)comboInstalledRecognizers.SelectedItem); }
最後,我們會根據選取的手寫識別器來執行手寫辨識。 在此範例中,我們使用 [辨識] 按鈕的 click 事件處理常式來執行手寫辨識。
- InkPresenter 將所有筆跡筆劃儲存在 InkStrokeContainer 物件中。 筆劃透過 InkPresenter 的 StrokeContainer 屬性公開,並使用 GetStrokes 方法檢索。
// Get all strokes on the InkCanvas. IReadOnlyList<InkStroke> currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
會呼叫 RecognizeAsync 來擷取一組 InkRecognitionResult 物件。
辨識結果會針對 InkRecognizer 偵測到的每個字產生。
// Recognize all ink strokes on the ink canvas. IReadOnlyList<InkRecognitionResult> recognitionResults = await inkRecognizerContainer.RecognizeAsync( inkCanvas.InkPresenter.StrokeContainer, InkRecognitionTarget.All);
每個 InkRecognitionResult 物件都包含一組文字候選項目。 此清單中的最上層專案會被視為辨識引擎的最佳相符項目,後面接著剩餘的候選項目,以降低信賴度。
我們會逐一查看每個 InkRecognitionResult,並編譯候選項目清單。 然後會顯示候選項目,並清除 InkStrokeContainer (這也清除 InkCanvas)。
string str = "Recognition result\n"; // Iterate through the recognition results. foreach (InkRecognitionResult result in recognitionResults) { // Get all recognition candidates from each recognition result. IReadOnlyList<string> candidates = result.GetTextCandidates(); str += "Candidates: " + candidates.Count.ToString() + "\n"; foreach (string candidate in candidates) { str += candidate + " "; } } // Display the recognition candidates. recognitionResult.Text = str; // Clear the ink canvas once recognition is complete. inkCanvas.InkPresenter.StrokeContainer.Clear();
- 以下是完整按一下處理常式範例。
// Handle button click to initiate recognition. private async void Recognize_Click(object sender, RoutedEventArgs e) { // Get all strokes on the InkCanvas. IReadOnlyList<InkStroke> currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes(); // Ensure an ink stroke is present. if (currentStrokes.Count > 0) { // inkRecognizerContainer is null if a recognition engine is not available. if (!(inkRecognizerContainer == null)) { // Recognize all ink strokes on the ink canvas. IReadOnlyList<InkRecognitionResult> recognitionResults = await inkRecognizerContainer.RecognizeAsync( inkCanvas.InkPresenter.StrokeContainer, InkRecognitionTarget.All); // Process and display the recognition results. if (recognitionResults.Count > 0) { string str = "Recognition result\n"; // Iterate through the recognition results. foreach (InkRecognitionResult result in recognitionResults) { // Get all recognition candidates from each recognition result. IReadOnlyList<string> candidates = result.GetTextCandidates(); str += "Candidates: " + candidates.Count.ToString() + "\n"; foreach (string candidate in candidates) { str += candidate + " "; } } // Display the recognition candidates. recognitionResult.Text = str; // Clear the ink canvas once recognition is complete. inkCanvas.InkPresenter.StrokeContainer.Clear(); } else { recognitionResult.Text = "No recognition results."; } } else { Windows.UI.Popups.MessageDialog messageDialog = new Windows.UI.Popups.MessageDialog( "You must install handwriting recognition engine."); await messageDialog.ShowAsync(); } } else { recognitionResult.Text = "No ink strokes to recognize."; } }
動態辨識
雖然上述兩個範例會要求使用者按下按鈕來開始辨識,您也可以使用與基本計時函式配對的筆劃輸入來執行動態辨識。
在此範例中,我們將使用與先前的國際辨識範例相同的 UI 和筆劃設定。
這些全域物件 (InkAnalyzer、InkStroke、InkAnalysisResult、DispatcherTimer) 會在整個應用程式中使用。
// Stroke recognition globals. InkAnalyzer inkAnalyzer; DispatcherTimer recoTimer;
我們不是起始辨識的按鈕,而是新增兩個 InkPresenter 筆劃事件的接聽程式 (StrokesCollected 和 StrokeStarted),並使用一秒的 Tick 間隔設定基本計時器 (DispatcherTimer)。
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; }
接著,我們會定義我們在第一個步驟中宣告的 InkPresenter 事件的處理常式 (我們也覆寫 OnNavigatingFrom 頁面事件來管理計時器)。
StrokesCollected
將筆跡筆劃 (AddDataForStrokes) 新增至 InkAnalyzer,並在使用者停止筆跡時啟動辨識計時器 (舉起手寫筆或手指或放開滑鼠按鈕)。 在第一秒沒有筆跡輸入之後,就會起始辨識。使用 SetStrokeDataKind 方法來指定您只對文字感興趣 (包括檔案結構和項目符號清單) 或只在繪圖中 (包括圖形辨識)。
StrokeStarted
如果新的筆劃在下一個計時器刻度事件之前開始,請停止計時器,因為新的筆劃可能是單一手寫項目的接續。
// 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(); }
最後,我們會執行手寫辨識。 在此範例中,我們使用 DispatcherTimer 的 Tick 事件處理常式來起始手寫辨識。
- 呼叫 AnalyzeAsync 以起始筆跡分析並取得 InkAnalysisResult。
- 如果 Status 傳回 Updated 的狀態,請針對 InkAnalysisNodeKind.InkWord 的節點類型呼叫 FindNodes。
- 逐一查看節點並顯示已辨識的文字。
- 最後,從 InkAnalyzer 刪除已辨識的節點,並從筆跡畫布中刪除對應的筆跡筆劃。
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(); } }