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.Threading.Tasks;
using System.Windows;
using System.Windows.Ink;
using System.Windows.Media;
@@ -56,7 +57,7 @@ namespace Ink_Canvas.Helpers
public static ShapeRecognizeResult RecognizeShape(StrokeCollection strokes) =>
RecognizeShapeIACore(strokes);
/// <summary>按设置选择 WinRT 或 IACore,返回统一识别结果。</summary>
/// <summary>按设置选择 WinRT<see cref="InkRecognitionManager"/>)或 IACoreWinRT 请用 <see cref="RecognizeShapeUnifiedAsync"/>。</summary>
public static InkShapeRecognitionResult RecognizeShapeUnified(
StrokeCollection strokes,
ShapeRecognitionEngineMode mode)
@@ -65,16 +66,28 @@ namespace Ink_Canvas.Helpers
return InkShapeRecognitionResult.Empty;
if (ShapeRecognitionRouter.ResolveUseWinRt(mode))
return WinRtInkShapeRecognizer.RecognizeShape(strokes);
return InkShapeRecognitionResult.Empty;
var legacy = RecognizeShapeIACore(strokes);
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)
{
try
{
_ = InkRecognitionManager.Instance;
if (ShapeRecognitionRouter.ResolveUseWinRt(mode))
WinRtInkShapeRecognizer.Warmup();
else
+17 -19
View File
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
@@ -22,7 +23,19 @@ namespace Ink_Canvas.Helpers
if (!IsApiAvailable) return;
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
{
@@ -30,23 +43,8 @@ namespace Ink_Canvas.Helpers
}
}
public static InkShapeRecognitionResult RecognizeShape(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)
/// <summary>由 <see cref="ModernInkProcessor"/> / <see cref="InkRecognitionManager"/> 在 UI 上 await(勿在收笔回调中同步阻塞)。</summary>
internal static async Task<InkShapeRecognitionResult> RecognizeShapeAsync(StrokeCollection strokes)
{
var analyzer = new WinRtInkAnalyzer();
foreach (Stroke s in strokes)
@@ -61,7 +59,7 @@ namespace Ink_Canvas.Helpers
global::Windows.UI.Input.Inking.Analysis.InkAnalysisStrokeKind.Drawing);
}
await analyzer.AnalyzeAsync().AsTask().ConfigureAwait(false);
await analyzer.AnalyzeAsync().AsTask().ConfigureAwait(true);
var drawing = FindPrimaryDrawing(analyzer);
if (drawing == null ||
@@ -388,7 +388,7 @@ namespace Ink_Canvas
Settings.InkToShape.IsInkToShapeEnabled,
ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine)))
{
void InkToShapeProcess()
async Task InkToShapeProcessCoreAsync()
{
try
{
@@ -407,11 +407,11 @@ namespace Ink_Canvas
var shapeMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine);
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--)
{
strokeReco.Add(newStrokes[i]);
var newResult = InkRecognizeHelper.RecognizeShapeUnified(strokeReco, shapeMode);
var newResult = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(strokeReco, shapeMode);
if (newResult.IsSuccess &&
(newResult.ShapeName == "Circle" || newResult.ShapeName == "Ellipse"))
{
@@ -715,6 +715,28 @@ namespace Ink_Canvas
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();
}