add:WinRT墨迹识别
This commit is contained in:
@@ -40,14 +40,19 @@ namespace Ink_Canvas.Helpers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_isModernSystemAvailable = Environment.Is64BitProcess;
|
var tryModern = WinRtInkShapeRecognizer.IsApiAvailable
|
||||||
if (_isModernSystemAvailable)
|
&& (Environment.Is64BitProcess || Environment.Is64BitOperatingSystem);
|
||||||
|
|
||||||
|
_isModernSystemAvailable = false;
|
||||||
|
if (tryModern)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_modernProcessor = new ModernInkProcessor();
|
_modernProcessor = new ModernInkProcessor();
|
||||||
_modernAnalyzer = new ModernInkAnalyzer();
|
_modernAnalyzer = new ModernInkAnalyzer();
|
||||||
LogHelper.WriteLogToFile("墨迹识别管理器:使用64位现代化墨迹识别系统");
|
_isModernSystemAvailable = true;
|
||||||
|
LogHelper.WriteLogToFile(
|
||||||
|
$"墨迹识别管理器:使用现代化墨迹识别系统 (WinRT) - 进程64位: {Environment.Is64BitProcess}, OS64位: {Environment.Is64BitOperatingSystem}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -80,10 +85,9 @@ namespace Ink_Canvas.Helpers
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (ShapeRecognitionRouter.ResolveUseWinRt(mode)
|
if (ShapeRecognitionRouter.ResolveUseWinRt(mode)
|
||||||
&& _isModernSystemAvailable
|
&& WinRtInkShapeRecognizer.IsApiAvailable)
|
||||||
&& _modernProcessor != null)
|
|
||||||
{
|
{
|
||||||
return _modernProcessor.RecognizeShapeAsync(strokes);
|
return RunWinRtOrLogAsync(strokes);
|
||||||
}
|
}
|
||||||
|
|
||||||
var legacy = InkRecognizeHelper.RecognizeShapeIACore(strokes);
|
var legacy = InkRecognizeHelper.RecognizeShapeIACore(strokes);
|
||||||
@@ -96,6 +100,19 @@ namespace Ink_Canvas.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task<InkShapeRecognitionResult> RunWinRtOrLogAsync(StrokeCollection strokes)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await WinRtInkShapeRecognizer.RecognizeShapeAsync(strokes).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile("WinRT 墨迹形状识别异常: " + ex, LogHelper.LogType.Error);
|
||||||
|
return InkShapeRecognitionResult.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Task<StrokeCollection> CorrectInkAsync(
|
public Task<StrokeCollection> CorrectInkAsync(
|
||||||
StrokeCollection strokes,
|
StrokeCollection strokes,
|
||||||
ShapeRecognitionEngineMode mode)
|
ShapeRecognitionEngineMode mode)
|
||||||
|
|||||||
@@ -26,19 +26,29 @@ namespace Ink_Canvas.Helpers
|
|||||||
var alternates = analyzer.GetAlternates();
|
var alternates = analyzer.GetAlternates();
|
||||||
if (alternates.Count > 0)
|
if (alternates.Count > 0)
|
||||||
{
|
{
|
||||||
while ((!alternates[0].Strokes.Contains(strokes.Last()) ||
|
while (strokesCount >= 2)
|
||||||
!IsContainShapeType(((InkDrawingNode)alternates[0].AlternateNodes[0]).GetShapeName()))
|
|
||||||
&& strokesCount >= 2)
|
|
||||||
{
|
{
|
||||||
|
var alt0 = alternates[0];
|
||||||
|
if (alt0?.AlternateNodes == null || alt0.AlternateNodes.Count == 0)
|
||||||
|
break;
|
||||||
|
var drawNode = alt0.AlternateNodes[0] as InkDrawingNode;
|
||||||
|
if (drawNode == null)
|
||||||
|
break;
|
||||||
|
var shapeOk = IsContainShapeType(drawNode.GetShapeName());
|
||||||
|
if (alt0.Strokes.Contains(strokes.Last()) && shapeOk)
|
||||||
|
break;
|
||||||
analyzer.RemoveStroke(strokes[strokes.Count - strokesCount]);
|
analyzer.RemoveStroke(strokes[strokes.Count - strokesCount]);
|
||||||
strokesCount--;
|
strokesCount--;
|
||||||
sfsaf = analyzer.Analyze();
|
sfsaf = analyzer.Analyze();
|
||||||
if (sfsaf.Successful)
|
if (sfsaf.Successful)
|
||||||
{
|
|
||||||
alternates = analyzer.GetAlternates();
|
alternates = analyzer.GetAlternates();
|
||||||
}
|
else
|
||||||
|
break;
|
||||||
|
if (alternates.Count == 0)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
analysisAlternate = alternates[0];
|
if (alternates.Count > 0)
|
||||||
|
analysisAlternate = alternates[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +57,8 @@ namespace Ink_Canvas.Helpers
|
|||||||
if (analysisAlternate != null && analysisAlternate.AlternateNodes.Count > 0)
|
if (analysisAlternate != null && analysisAlternate.AlternateNodes.Count > 0)
|
||||||
{
|
{
|
||||||
var node = analysisAlternate.AlternateNodes[0] as InkDrawingNode;
|
var node = analysisAlternate.AlternateNodes[0] as InkDrawingNode;
|
||||||
|
if (node == null)
|
||||||
|
return default;
|
||||||
return new ShapeRecognizeResult(node.Centroid, node.HotPoints, analysisAlternate, node);
|
return new ShapeRecognizeResult(node.Centroid, node.HotPoints, analysisAlternate, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,41 +46,70 @@ namespace Ink_Canvas.Helpers
|
|||||||
/// <summary>由 <see cref="ModernInkProcessor"/> / <see cref="InkRecognitionManager"/> 在 UI 上 await(勿在收笔回调中同步阻塞)。</summary>
|
/// <summary>由 <see cref="ModernInkProcessor"/> / <see cref="InkRecognitionManager"/> 在 UI 上 await(勿在收笔回调中同步阻塞)。</summary>
|
||||||
internal static async Task<InkShapeRecognitionResult> RecognizeShapeAsync(StrokeCollection strokes)
|
internal static async Task<InkShapeRecognitionResult> RecognizeShapeAsync(StrokeCollection strokes)
|
||||||
{
|
{
|
||||||
var analyzer = new WinRtInkAnalyzer();
|
if (!IsApiAvailable || strokes == null || strokes.Count == 0)
|
||||||
foreach (Stroke s in strokes)
|
return InkShapeRecognitionResult.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var inkStroke = CreateInkStrokeFromWpf(s);
|
var analyzer = new WinRtInkAnalyzer();
|
||||||
if (inkStroke == null)
|
var added = 0;
|
||||||
continue;
|
foreach (Stroke s in strokes)
|
||||||
|
{
|
||||||
|
var inkStroke = CreateInkStrokeFromWpf(s);
|
||||||
|
if (inkStroke == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
analyzer.AddDataForStroke(inkStroke);
|
analyzer.AddDataForStroke(inkStroke);
|
||||||
analyzer.SetStrokeDataKind(
|
analyzer.SetStrokeDataKind(
|
||||||
inkStroke.Id,
|
inkStroke.Id,
|
||||||
global::Windows.UI.Input.Inking.Analysis.InkAnalysisStrokeKind.Drawing);
|
global::Windows.UI.Input.Inking.Analysis.InkAnalysisStrokeKind.Drawing);
|
||||||
|
added++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (added == 0)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile(
|
||||||
|
"WinRT 形状识别:未能从 WPF 笔迹生成 InkStroke(检查 StylusPoints/DrawingAttributes)。",
|
||||||
|
LogHelper.LogType.Warning);
|
||||||
|
return InkShapeRecognitionResult.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
await analyzer.AnalyzeAsync().AsTask().ConfigureAwait(true);
|
||||||
|
|
||||||
|
var drawing = FindPrimaryDrawing(analyzer);
|
||||||
|
if (drawing == null)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile("WinRT 形状识别:分析完成但未找到 InkAnalysisInkDrawing 节点。", LogHelper.LogType.Trace);
|
||||||
|
return InkShapeRecognitionResult.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawing.DrawingKind == global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Drawing)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile("WinRT 形状识别:结果为 Drawing(未识别为规则形状)。", LogHelper.LogType.Trace);
|
||||||
|
return InkShapeRecognitionResult.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = MapDrawingKindToShapeName(drawing.DrawingKind);
|
||||||
|
if (string.IsNullOrEmpty(name) || name == "Drawing")
|
||||||
|
return InkShapeRecognitionResult.Empty;
|
||||||
|
|
||||||
|
var winPts = CopyWinRtPoints(drawing);
|
||||||
|
var hot = ToWpfPointCollection(winPts);
|
||||||
|
var c = drawing.Center;
|
||||||
|
var centroid = new SysPoint(c.X, c.Y);
|
||||||
|
BoundsFromPoints(winPts, out double w, out double h);
|
||||||
|
|
||||||
|
var toRemove = new StrokeCollection();
|
||||||
|
foreach (Stroke s in strokes)
|
||||||
|
toRemove.Add(s);
|
||||||
|
|
||||||
|
return new InkShapeRecognitionResult(name, centroid, hot, w, h, toRemove);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
await analyzer.AnalyzeAsync().AsTask().ConfigureAwait(true);
|
{
|
||||||
|
LogHelper.WriteLogToFile("WinRtInkShapeRecognizer 异常: " + ex, LogHelper.LogType.Error);
|
||||||
var drawing = FindPrimaryDrawing(analyzer);
|
|
||||||
if (drawing == null ||
|
|
||||||
drawing.DrawingKind == global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Drawing)
|
|
||||||
return InkShapeRecognitionResult.Empty;
|
return InkShapeRecognitionResult.Empty;
|
||||||
|
}
|
||||||
var name = MapDrawingKindToShapeName(drawing.DrawingKind);
|
|
||||||
if (string.IsNullOrEmpty(name) || name == "Drawing")
|
|
||||||
return InkShapeRecognitionResult.Empty;
|
|
||||||
|
|
||||||
var winPts = CopyWinRtPoints(drawing);
|
|
||||||
var hot = ToWpfPointCollection(winPts);
|
|
||||||
var c = drawing.Center;
|
|
||||||
var centroid = new SysPoint(c.X, c.Y);
|
|
||||||
BoundsFromPoints(winPts, out double w, out double h);
|
|
||||||
|
|
||||||
var toRemove = new StrokeCollection();
|
|
||||||
foreach (Stroke s in strokes)
|
|
||||||
toRemove.Add(s);
|
|
||||||
|
|
||||||
return new InkShapeRecognitionResult(name, centroid, hot, w, h, toRemove);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static global::Windows.UI.Input.Inking.InkStroke CreateInkStrokeFromWpf(Stroke stroke)
|
private static global::Windows.UI.Input.Inking.InkStroke CreateInkStrokeFromWpf(Stroke stroke)
|
||||||
@@ -117,7 +146,8 @@ namespace Ink_Canvas.Helpers
|
|||||||
{
|
{
|
||||||
global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing best = null;
|
global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing best = null;
|
||||||
double bestArea = -1;
|
double bestArea = -1;
|
||||||
Visit(analyzer.AnalysisRoot);
|
if (analyzer?.AnalysisRoot != null)
|
||||||
|
Visit(analyzer.AnalysisRoot);
|
||||||
return best;
|
return best;
|
||||||
|
|
||||||
void Visit(global::Windows.UI.Input.Inking.Analysis.IInkAnalysisNode node)
|
void Visit(global::Windows.UI.Input.Inking.Analysis.IInkAnalysisNode node)
|
||||||
@@ -135,7 +165,11 @@ namespace Ink_Canvas.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var child in node.Children)
|
// WinRT IInkAnalysisNode.Children 可能为 null,不可直接 foreach。
|
||||||
|
var children = node.Children;
|
||||||
|
if (children == null) return;
|
||||||
|
|
||||||
|
foreach (var child in children)
|
||||||
Visit(child);
|
Visit(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,17 +30,17 @@
|
|||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||||
<Prefer32Bit>True</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86 Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86 Debug|AnyCPU'">
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||||
<Prefer32Bit>True</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||||
<Prefer32Bit>True</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ApplicationIcon>Resources\icc.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\icc.ico</ApplicationIcon>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
@@ -39,6 +40,9 @@ namespace Ink_Canvas
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private List<Circle> circles = new List<Circle>();
|
private List<Circle> circles = new List<Circle>();
|
||||||
|
|
||||||
|
/// <summary>串行执行墨迹转形状(尤其 WinRT 异步延后),避免多笔 BeginInvoke 交错修改 newStrokes。</summary>
|
||||||
|
private static readonly SemaphoreSlim InkToShapeSerial = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -390,6 +394,7 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
async Task InkToShapeProcessCoreAsync()
|
async Task InkToShapeProcessCoreAsync()
|
||||||
{
|
{
|
||||||
|
await InkToShapeSerial.WaitAsync().ConfigureAwait(true);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
newStrokes.Add(e.Stroke);
|
newStrokes.Add(e.Stroke);
|
||||||
@@ -713,6 +718,10 @@ namespace Ink_Canvas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
InkToShapeSerial.Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InkToShapeProcess()
|
void InkToShapeProcess()
|
||||||
@@ -730,7 +739,7 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine(ex);
|
System.Diagnostics.Debug.WriteLine(ex);
|
||||||
}
|
}
|
||||||
}), DispatcherPriority.Background);
|
}), DispatcherPriority.Normal);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user