add:WinRT墨迹识别

This commit is contained in:
2026-03-28 18:30:40 +08:00
parent 97b0972fdf
commit dc9fb26260
4 changed files with 231 additions and 24 deletions
+174
View File
@@ -0,0 +1,174 @@
using System;
using System.Threading.Tasks;
using System.Windows.Ink;
namespace Ink_Canvas.Helpers
{
public sealed class InkRecognitionManager
{
private static InkRecognitionManager _instance;
private static readonly object _lock = new object();
private ModernInkProcessor _modernProcessor;
private ModernInkAnalyzer _modernAnalyzer;
private bool _isModernSystemAvailable;
private bool _isInitialized;
public static InkRecognitionManager Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
_instance = new InkRecognitionManager();
}
}
return _instance;
}
}
private InkRecognitionManager()
{
Initialize();
}
private void Initialize()
{
try
{
_isModernSystemAvailable = Environment.Is64BitProcess;
if (_isModernSystemAvailable)
{
try
{
_modernProcessor = new ModernInkProcessor();
_modernAnalyzer = new ModernInkAnalyzer();
LogHelper.WriteLogToFile("墨迹识别管理器:使用64位现代化墨迹识别系统");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile("WinRT API不可用,回退到IACore: " + ex.Message, LogHelper.LogType.Warning);
_isModernSystemAvailable = false;
_modernProcessor = null;
_modernAnalyzer = null;
}
}
if (!_isModernSystemAvailable)
LogHelper.WriteLogToFile("墨迹识别管理器:使用IACore墨迹识别系统");
_isInitialized = true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile("墨迹识别管理器初始化失败: " + ex.Message, LogHelper.LogType.Error);
_isInitialized = false;
}
}
public Task<InkShapeRecognitionResult> RecognizeShapeAsync(
StrokeCollection strokes,
ShapeRecognitionEngineMode mode)
{
if (!_isInitialized || strokes == null || strokes.Count == 0)
return Task.FromResult(InkShapeRecognitionResult.Empty);
try
{
if (ShapeRecognitionRouter.ResolveUseWinRt(mode)
&& _isModernSystemAvailable
&& _modernProcessor != null)
{
return _modernProcessor.RecognizeShapeAsync(strokes);
}
var legacy = InkRecognizeHelper.RecognizeShapeIACore(strokes);
return Task.FromResult(InkRecognizeHelper.FromIACoreOrEmpty(legacy));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile("墨迹形状识别失败: " + ex.Message, LogHelper.LogType.Error);
return Task.FromResult(InkShapeRecognitionResult.Empty);
}
}
public Task<StrokeCollection> CorrectInkAsync(
StrokeCollection strokes,
ShapeRecognitionEngineMode mode)
{
if (!_isInitialized || strokes == null || strokes.Count == 0)
return Task.FromResult(strokes);
try
{
if (ShapeRecognitionRouter.ResolveUseWinRt(mode) && _modernAnalyzer != null)
return _modernAnalyzer.AnalyzeAndCorrectAsync(strokes);
return Task.FromResult(strokes);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile("墨迹纠正失败: " + ex.Message, LogHelper.LogType.Error);
return Task.FromResult(strokes);
}
}
public bool IsValidShapeType(string shapeName)
{
return !string.IsNullOrEmpty(shapeName)
&& (shapeName.Contains("Triangle") || shapeName.Contains("Circle")
|| shapeName.Contains("Rectangle") || shapeName.Contains("Diamond")
|| shapeName.Contains("Parallelogram") || shapeName.Contains("Square")
|| shapeName.Contains("Ellipse") || shapeName.Contains("Line")
|| shapeName.Contains("Arrow"));
}
public string GetSystemInfo()
{
return _isModernSystemAvailable
? $"现代化64位墨迹识别系统 (Windows Runtime API) - 进程架构: {Environment.Is64BitProcess}"
: $"传统墨迹识别系统 (IACore) - 进程架构: {Environment.Is64BitProcess}";
}
public void Dispose()
{
_modernProcessor?.Dispose();
_modernAnalyzer?.Dispose();
_isInitialized = false;
}
}
internal sealed class ModernInkProcessor : IDisposable
{
public ModernInkProcessor()
{
if (!WinRtInkShapeRecognizer.IsApiAvailable)
throw new InvalidOperationException("WinRT 墨迹分析需要 Windows 10 及以上。");
}
public Task<InkShapeRecognitionResult> RecognizeShapeAsync(StrokeCollection strokes)
{
return WinRtInkShapeRecognizer.RecognizeShapeAsync(strokes);
}
public void Dispose()
{
}
}
internal sealed class ModernInkAnalyzer : IDisposable
{
public Task<StrokeCollection> AnalyzeAndCorrectAsync(StrokeCollection strokes)
{
return Task.FromResult(strokes);
}
public void Dispose()
{
}
}
}
+15 -2
View File
@@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Ink; using System.Windows.Ink;
using System.Windows.Media; using System.Windows.Media;
@@ -56,7 +57,7 @@ namespace Ink_Canvas.Helpers
public static ShapeRecognizeResult RecognizeShape(StrokeCollection strokes) => public static ShapeRecognizeResult RecognizeShape(StrokeCollection strokes) =>
RecognizeShapeIACore(strokes); RecognizeShapeIACore(strokes);
/// <summary>按设置选择 WinRT 或 IACore,返回统一识别结果。</summary> /// <summary>按设置选择 WinRT<see cref="InkRecognitionManager"/>)或 IACoreWinRT 请用 <see cref="RecognizeShapeUnifiedAsync"/>。</summary>
public static InkShapeRecognitionResult RecognizeShapeUnified( public static InkShapeRecognitionResult RecognizeShapeUnified(
StrokeCollection strokes, StrokeCollection strokes,
ShapeRecognitionEngineMode mode) ShapeRecognitionEngineMode mode)
@@ -65,16 +66,28 @@ namespace Ink_Canvas.Helpers
return InkShapeRecognitionResult.Empty; return InkShapeRecognitionResult.Empty;
if (ShapeRecognitionRouter.ResolveUseWinRt(mode)) if (ShapeRecognitionRouter.ResolveUseWinRt(mode))
return WinRtInkShapeRecognizer.RecognizeShape(strokes); return InkShapeRecognitionResult.Empty;
var legacy = RecognizeShapeIACore(strokes); var legacy = RecognizeShapeIACore(strokes);
return FromIACoreOrEmpty(legacy); return FromIACoreOrEmpty(legacy);
} }
/// <summary>与 CE 反编译版 <c>InkRecognitionManager.RecognizeShapeAsync</c> 对齐的统一入口。</summary>
public static Task<InkShapeRecognitionResult> RecognizeShapeUnifiedAsync(
StrokeCollection strokes,
ShapeRecognitionEngineMode mode)
{
if (strokes == null || strokes.Count == 0)
return Task.FromResult(InkShapeRecognitionResult.Empty);
return InkRecognitionManager.Instance.RecognizeShapeAsync(strokes, mode);
}
public static void WarmupShapeRecognition(ShapeRecognitionEngineMode mode) public static void WarmupShapeRecognition(ShapeRecognitionEngineMode mode)
{ {
try try
{ {
_ = InkRecognitionManager.Instance;
if (ShapeRecognitionRouter.ResolveUseWinRt(mode)) if (ShapeRecognitionRouter.ResolveUseWinRt(mode))
WinRtInkShapeRecognizer.Warmup(); WinRtInkShapeRecognizer.Warmup();
else else
+17 -19
View File
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices.WindowsRuntime; using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using System.Windows.Ink; using System.Windows.Ink;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
@@ -22,7 +23,19 @@ namespace Ink_Canvas.Helpers
if (!IsApiAvailable) return; if (!IsApiAvailable) return;
try try
{ {
RecognizeShape(new StrokeCollection()); var d = Application.Current?.Dispatcher;
if (d == null) return;
d.BeginInvoke(new Action(async () =>
{
try
{
await RecognizeShapeAsync(new StrokeCollection());
}
catch
{
// ignore
}
}));
} }
catch catch
{ {
@@ -30,23 +43,8 @@ namespace Ink_Canvas.Helpers
} }
} }
public static InkShapeRecognitionResult RecognizeShape(StrokeCollection strokes) /// <summary>由 <see cref="ModernInkProcessor"/> / <see cref="InkRecognitionManager"/> 在 UI 上 await(勿在收笔回调中同步阻塞)。</summary>
{ internal static async Task<InkShapeRecognitionResult> RecognizeShapeAsync(StrokeCollection strokes)
if (!IsApiAvailable || strokes == null || strokes.Count == 0)
return InkShapeRecognitionResult.Empty;
try
{
return RecognizeShapeAsync(strokes).GetAwaiter().GetResult();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
return InkShapeRecognitionResult.Empty;
}
}
private static async Task<InkShapeRecognitionResult> RecognizeShapeAsync(StrokeCollection strokes)
{ {
var analyzer = new WinRtInkAnalyzer(); var analyzer = new WinRtInkAnalyzer();
foreach (Stroke s in strokes) foreach (Stroke s in strokes)
@@ -61,7 +59,7 @@ namespace Ink_Canvas.Helpers
global::Windows.UI.Input.Inking.Analysis.InkAnalysisStrokeKind.Drawing); global::Windows.UI.Input.Inking.Analysis.InkAnalysisStrokeKind.Drawing);
} }
await analyzer.AnalyzeAsync().AsTask().ConfigureAwait(false); await analyzer.AnalyzeAsync().AsTask().ConfigureAwait(true);
var drawing = FindPrimaryDrawing(analyzer); var drawing = FindPrimaryDrawing(analyzer);
if (drawing == null || if (drawing == null ||
@@ -388,7 +388,7 @@ namespace Ink_Canvas
Settings.InkToShape.IsInkToShapeEnabled, Settings.InkToShape.IsInkToShapeEnabled,
ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine))) ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine)))
{ {
void InkToShapeProcess() async Task InkToShapeProcessCoreAsync()
{ {
try try
{ {
@@ -407,11 +407,11 @@ namespace Ink_Canvas
var shapeMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine); var shapeMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine);
var strokeReco = new StrokeCollection(); var strokeReco = new StrokeCollection();
var result = InkRecognizeHelper.RecognizeShapeUnified(newStrokes, shapeMode); var result = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(newStrokes, shapeMode);
for (var i = newStrokes.Count - 1; i >= 0; i--) for (var i = newStrokes.Count - 1; i >= 0; i--)
{ {
strokeReco.Add(newStrokes[i]); strokeReco.Add(newStrokes[i]);
var newResult = InkRecognizeHelper.RecognizeShapeUnified(strokeReco, shapeMode); var newResult = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(strokeReco, shapeMode);
if (newResult.IsSuccess && if (newResult.IsSuccess &&
(newResult.ShapeName == "Circle" || newResult.ShapeName == "Ellipse")) (newResult.ShapeName == "Circle" || newResult.ShapeName == "Ellipse"))
{ {
@@ -715,6 +715,28 @@ namespace Ink_Canvas
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
} }
void InkToShapeProcess()
{
var engineMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine);
if (ShapeRecognitionRouter.ResolveUseWinRt(engineMode))
{
Dispatcher.BeginInvoke(new Action(async () =>
{
try
{
await InkToShapeProcessCoreAsync();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}), DispatcherPriority.Background);
return;
}
InkToShapeProcessCoreAsync().GetAwaiter().GetResult();
}
InkToShapeProcess(); InkToShapeProcess();
} }