add:WinRT墨迹识别
This commit is contained in:
@@ -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()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"/>)或 IACore;WinRT 请用 <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
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user