improve:手写体识别
This commit is contained in:
@@ -19,6 +19,9 @@ namespace Ink_Canvas.Helpers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class WinRtHandwritingRecognizer
|
internal static class WinRtHandwritingRecognizer
|
||||||
{
|
{
|
||||||
|
private static WinRtInk.InkRecognizer _preferredHandwritingRecognizer;
|
||||||
|
private static bool _preferredHandwritingRecognizerResolved;
|
||||||
|
|
||||||
private static void LogHandwriting(string message, LogHelper.LogType logType = LogHelper.LogType.Info)
|
private static void LogHandwriting(string message, LogHelper.LogType logType = LogHelper.LogType.Info)
|
||||||
{
|
{
|
||||||
LogHelper.WriteLogToFile("[手写体] " + message, logType);
|
LogHelper.WriteLogToFile("[手写体] " + message, logType);
|
||||||
@@ -66,6 +69,9 @@ namespace Ink_Canvas.Helpers
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var recognizer = new WinRtInk.InkRecognizerContainer();
|
||||||
|
TryApplyPreferredHandwritingRecognizer(recognizer, traceRecognition);
|
||||||
|
|
||||||
var analyzer = new WinAnalysis.InkAnalyzer();
|
var analyzer = new WinAnalysis.InkAnalyzer();
|
||||||
var idToWpf = new Dictionary<uint, Stroke>();
|
var idToWpf = new Dictionary<uint, Stroke>();
|
||||||
|
|
||||||
@@ -92,19 +98,21 @@ namespace Ink_Canvas.Helpers
|
|||||||
LogHandwriting(
|
LogHandwriting(
|
||||||
"识别:AnalyzeAsync 未得到 Updated,Status=" +
|
"识别:AnalyzeAsync 未得到 Updated,Status=" +
|
||||||
(analysisResult == null ? "null" : analysisResult.Status.ToString()) +
|
(analysisResult == null ? "null" : analysisResult.Status.ToString()) +
|
||||||
",有效笔画数=" + idToWpf.Count);
|
",有效笔画数=" + idToWpf.Count +
|
||||||
return HandwritingRecognitionResult.Empty;
|
",尝试整批 RecognizeAsync 回退。");
|
||||||
|
return await RecognizeHandwritingWholeInkAsync(strokes, traceRecognition).ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var wordNodes = analyzer.AnalysisRoot?.FindNodes(WinAnalysis.InkAnalysisNodeKind.InkWord);
|
var wordNodes = analyzer.AnalysisRoot?.FindNodes(WinAnalysis.InkAnalysisNodeKind.InkWord);
|
||||||
if (wordNodes == null || wordNodes.Count == 0)
|
if (wordNodes == null || wordNodes.Count == 0)
|
||||||
{
|
{
|
||||||
if (traceRecognition)
|
if (traceRecognition)
|
||||||
LogHandwriting("识别:未找到 InkWord 节点(可能被判为绘图或非书写),有效笔画数=" + idToWpf.Count);
|
LogHandwriting(
|
||||||
return HandwritingRecognitionResult.Empty;
|
"识别:未找到 InkWord 节点(墨迹分析常将非横平笔划判为绘图),有效笔画数=" + idToWpf.Count +
|
||||||
|
",改用整批 RecognizeAsync 回退。");
|
||||||
|
return await RecognizeHandwritingWholeInkAsync(strokes, traceRecognition).ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var recognizer = new WinRtInk.InkRecognizerContainer();
|
|
||||||
var segments = new List<HandwritingWordSegment>();
|
var segments = new List<HandwritingWordSegment>();
|
||||||
|
|
||||||
foreach (var node in wordNodes)
|
foreach (var node in wordNodes)
|
||||||
@@ -232,6 +240,243 @@ namespace Ink_Canvas.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void TryApplyPreferredHandwritingRecognizer(
|
||||||
|
WinRtInk.InkRecognizerContainer container,
|
||||||
|
bool logDetail)
|
||||||
|
{
|
||||||
|
if (container == null)
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_preferredHandwritingRecognizerResolved)
|
||||||
|
{
|
||||||
|
_preferredHandwritingRecognizerResolved = true;
|
||||||
|
var all = container.GetRecognizers();
|
||||||
|
_preferredHandwritingRecognizer = SelectBestInkRecognizer(all);
|
||||||
|
if (logDetail)
|
||||||
|
{
|
||||||
|
if (_preferredHandwritingRecognizer != null)
|
||||||
|
LogHandwriting("识别器:已选用 \"" + _preferredHandwritingRecognizer.Name + "\"。");
|
||||||
|
else if (all != null && all.Count > 0)
|
||||||
|
LogHandwriting("识别器:未匹配到与 UI/区域语言对应的引擎,使用系统默认(共 " + all.Count + " 个)。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_preferredHandwritingRecognizer != null)
|
||||||
|
container.SetDefaultRecognizer(_preferredHandwritingRecognizer);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile("[手写体] 设置默认手写识别器失败: " + ex.Message, LogHelper.LogType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static WinRtInk.InkRecognizer SelectBestInkRecognizer(
|
||||||
|
IReadOnlyList<WinRtInk.InkRecognizer> list)
|
||||||
|
{
|
||||||
|
if (list == null || list.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var culture = PrimaryHandwritingCulture();
|
||||||
|
var lang = (culture?.TwoLetterISOLanguageName ?? string.Empty).ToLowerInvariant();
|
||||||
|
var name = culture?.Name ?? string.Empty;
|
||||||
|
|
||||||
|
bool wantZhHans = lang == "zh" &&
|
||||||
|
(name.IndexOf("hans", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
name.Equals("zh-cn", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
name.Equals("zh-sg", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
(name.IndexOf("hant", StringComparison.OrdinalIgnoreCase) < 0 &&
|
||||||
|
!name.Equals("zh-tw", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!name.Equals("zh-hk", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!name.Equals("zh-mo", StringComparison.OrdinalIgnoreCase)));
|
||||||
|
|
||||||
|
bool wantZhHant = lang == "zh" &&
|
||||||
|
(name.IndexOf("hant", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
name.Equals("zh-tw", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
name.Equals("zh-hk", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
name.Equals("zh-mo", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
WinRtInk.InkRecognizer Pick(Func<string, bool> match)
|
||||||
|
{
|
||||||
|
foreach (var r in list)
|
||||||
|
{
|
||||||
|
var n = r?.Name;
|
||||||
|
if (string.IsNullOrEmpty(n))
|
||||||
|
continue;
|
||||||
|
if (match(n))
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantZhHans)
|
||||||
|
{
|
||||||
|
var r = Pick(n =>
|
||||||
|
n.IndexOf("简体", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("簡體", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
(n.IndexOf("中文", StringComparison.OrdinalIgnoreCase) >= 0 &&
|
||||||
|
(n.IndexOf("简体", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("簡體", StringComparison.OrdinalIgnoreCase) >= 0)) ||
|
||||||
|
(n.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0 &&
|
||||||
|
(n.IndexOf("Simplified", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("Hans", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("PRC", StringComparison.OrdinalIgnoreCase) >= 0)));
|
||||||
|
if (r != null)
|
||||||
|
return r;
|
||||||
|
r = Pick(n =>
|
||||||
|
n.IndexOf("中文", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0);
|
||||||
|
if (r != null)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
else if (wantZhHant)
|
||||||
|
{
|
||||||
|
var r = Pick(n =>
|
||||||
|
n.IndexOf("繁体", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("繁體", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
(n.IndexOf("中文", StringComparison.OrdinalIgnoreCase) >= 0 &&
|
||||||
|
(n.IndexOf("繁体", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("繁體", StringComparison.OrdinalIgnoreCase) >= 0)) ||
|
||||||
|
(n.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0 &&
|
||||||
|
(n.IndexOf("Traditional", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("Hant", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("Taiwan", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("Hong Kong", StringComparison.OrdinalIgnoreCase) >= 0)));
|
||||||
|
if (r != null)
|
||||||
|
return r;
|
||||||
|
r = Pick(n =>
|
||||||
|
n.IndexOf("中文", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0);
|
||||||
|
if (r != null)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
else if (lang == "ja")
|
||||||
|
{
|
||||||
|
var r = Pick(n =>
|
||||||
|
n.IndexOf("Japanese", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("日本語", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("日语", StringComparison.OrdinalIgnoreCase) >= 0);
|
||||||
|
if (r != null)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
else if (lang == "en")
|
||||||
|
{
|
||||||
|
var r = Pick(n => n.IndexOf("English", StringComparison.OrdinalIgnoreCase) >= 0);
|
||||||
|
if (r != null)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lang == "zh")
|
||||||
|
{
|
||||||
|
var r = Pick(n =>
|
||||||
|
n.IndexOf("中文", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
n.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0);
|
||||||
|
if (r != null)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CultureInfo PrimaryHandwritingCulture()
|
||||||
|
{
|
||||||
|
var ui = CultureInfo.CurrentUICulture;
|
||||||
|
var ct = CultureInfo.CurrentCulture;
|
||||||
|
if (string.Equals(ui.TwoLetterISOLanguageName, "zh", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return ui;
|
||||||
|
if (string.Equals(ct.TwoLetterISOLanguageName, "zh", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return ct;
|
||||||
|
return ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<HandwritingRecognitionResult> RecognizeHandwritingWholeInkAsync(
|
||||||
|
StrokeCollection strokes,
|
||||||
|
bool traceRecognition)
|
||||||
|
{
|
||||||
|
if (strokes == null || strokes.Count == 0)
|
||||||
|
return HandwritingRecognitionResult.Empty;
|
||||||
|
|
||||||
|
var container = new WinRtInk.InkStrokeContainer();
|
||||||
|
foreach (Stroke s in strokes)
|
||||||
|
{
|
||||||
|
var ink = WinRtInkShapeRecognizer.CreateInkStrokeFromWpf(s);
|
||||||
|
if (ink != null)
|
||||||
|
container.AddStroke(ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
var winStrokes = container.GetStrokes();
|
||||||
|
if (winStrokes == null || winStrokes.Count == 0)
|
||||||
|
{
|
||||||
|
if (traceRecognition)
|
||||||
|
LogHandwriting("整批回退:无有效 WinRT 笔画。");
|
||||||
|
return HandwritingRecognitionResult.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var reco = new WinRtInk.InkRecognizerContainer();
|
||||||
|
TryApplyPreferredHandwritingRecognizer(reco, false);
|
||||||
|
|
||||||
|
IReadOnlyList<WinRtInk.InkRecognitionResult> rr;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rr = await reco
|
||||||
|
.RecognizeAsync(container, WinRtInk.InkRecognitionTarget.All)
|
||||||
|
.AsTask()
|
||||||
|
.ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (traceRecognition)
|
||||||
|
LogHandwriting("整批回退:RecognizeAsync 异常:" + ex.Message);
|
||||||
|
return HandwritingRecognitionResult.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rr == null || rr.Count == 0 || rr[0] == null)
|
||||||
|
{
|
||||||
|
if (traceRecognition)
|
||||||
|
LogHandwriting("整批回退:RecognizeAsync 无结果。");
|
||||||
|
return HandwritingRecognitionResult.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cands = rr[0].GetTextCandidates();
|
||||||
|
var primary = (cands != null && cands.Count > 0) ? cands[0] : string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(primary))
|
||||||
|
{
|
||||||
|
if (traceRecognition)
|
||||||
|
LogHandwriting("整批回退:候选文本为空。");
|
||||||
|
return HandwritingRecognitionResult.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var merged = new List<string>();
|
||||||
|
if (cands != null)
|
||||||
|
{
|
||||||
|
foreach (var c in cands)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(c) && !merged.Contains(c))
|
||||||
|
merged.Add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bounds = UnionStrokeBounds(strokes);
|
||||||
|
var group = new List<Stroke>();
|
||||||
|
foreach (Stroke s in strokes)
|
||||||
|
group.Add(s);
|
||||||
|
|
||||||
|
var seg = new HandwritingWordSegment(primary, merged, bounds, group);
|
||||||
|
return new HandwritingRecognitionResult(new List<HandwritingWordSegment> { seg });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Rect UnionStrokeBounds(StrokeCollection strokes)
|
||||||
|
{
|
||||||
|
if (strokes == null || strokes.Count == 0)
|
||||||
|
return Rect.Empty;
|
||||||
|
|
||||||
|
var r = strokes[0].GetBounds();
|
||||||
|
for (var i = 1; i < strokes.Count; i++)
|
||||||
|
r = Rect.Union(r, strokes[i].GetBounds());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
private const string DefaultHandwritingFontFamilyList = "Ink Free,KaiTi,Segoe Script";
|
private const string DefaultHandwritingFontFamilyList = "Ink Free,KaiTi,Segoe Script";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -142,14 +142,24 @@ namespace Ink_Canvas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunStrokeCollectedPostShapeRecognitionTail(InkCanvasStrokeCollectedEventArgs e, bool wasStraightened)
|
/// <summary>
|
||||||
|
/// 收笔后压感/墨迹平滑等尾部处理。返回「当前应登记到手写字形替换批次」的画布笔画引用:
|
||||||
|
/// 同步贝塞尔平滑若替换了笔画,则为新 <see cref="Stroke"/>;否则为 <paramref name="e"/>.Stroke。
|
||||||
|
/// 直线拉直后事件参数中的笔画可能已不在画布上,调用方需另行传入画布上的笔画(见收笔处)。
|
||||||
|
/// </summary>
|
||||||
|
private Stroke RunStrokeCollectedPostShapeRecognitionTail(InkCanvasStrokeCollectedEventArgs e, bool wasStraightened)
|
||||||
{
|
{
|
||||||
|
if (e?.Stroke == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var handwritingScheduleStroke = e.Stroke;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var stylusPoint in e.Stroke.StylusPoints)
|
foreach (var stylusPoint in e.Stroke.StylusPoints)
|
||||||
if ((stylusPoint.PressureFactor > 0.501 || stylusPoint.PressureFactor < 0.5) &&
|
if ((stylusPoint.PressureFactor > 0.501 || stylusPoint.PressureFactor < 0.5) &&
|
||||||
stylusPoint.PressureFactor != 0)
|
stylusPoint.PressureFactor != 0)
|
||||||
return;
|
return e.Stroke;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -202,7 +212,7 @@ namespace Ink_Canvas
|
|||||||
var n = e.Stroke.StylusPoints.Count - 1;
|
var n = e.Stroke.StylusPoints.Count - 1;
|
||||||
var pressure = 0.1;
|
var pressure = 0.1;
|
||||||
var x = 10;
|
var x = 10;
|
||||||
if (n == 1) return;
|
if (n == 1) return e.Stroke;
|
||||||
if (n >= x)
|
if (n >= x)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < n - x; i++)
|
for (var i = 0; i < n - x; i++)
|
||||||
@@ -276,6 +286,7 @@ namespace Ink_Canvas
|
|||||||
inkCanvas.Strokes.Remove(e.Stroke);
|
inkCanvas.Strokes.Remove(e.Stroke);
|
||||||
inkCanvas.Strokes.Add(smoothedStroke);
|
inkCanvas.Strokes.Add(smoothedStroke);
|
||||||
_currentCommitType = CommitReason.UserInput;
|
_currentCommitType = CommitReason.UserInput;
|
||||||
|
handwritingScheduleStroke = smoothedStroke;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,6 +304,8 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
drawingAttributes.FitToCurve = true;
|
drawingAttributes.FitToCurve = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return handwritingScheduleStroke;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -325,6 +338,10 @@ namespace Ink_Canvas
|
|||||||
&& Math.Abs(strokeDrawingAttributes.Width - BoardBrushInkWidth) < 0.01
|
&& Math.Abs(strokeDrawingAttributes.Width - BoardBrushInkWidth) < 0.01
|
||||||
&& Math.Abs(strokeDrawingAttributes.Height - BoardBrushInkHeight) < 0.01;
|
&& Math.Abs(strokeDrawingAttributes.Height - BoardBrushInkHeight) < 0.01;
|
||||||
|
|
||||||
|
// 手写识别须与画布显示分离:在压感/触摸模拟/笔锋/直线拉直等修改 e.Stroke 之前快照原始落笔点集。
|
||||||
|
var handwritingRawPointsForRecognizer =
|
||||||
|
CloneStylusPointCollectionForHandwritingInput(e.Stroke?.StylusPoints);
|
||||||
|
|
||||||
// 检查是否启用墨迹渐隐功能
|
// 检查是否启用墨迹渐隐功能
|
||||||
if (Settings.Canvas.EnableInkFade && !isBoardBrushStroke)
|
if (Settings.Canvas.EnableInkFade && !isBoardBrushStroke)
|
||||||
{
|
{
|
||||||
@@ -372,6 +389,8 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
// 标记是否进行了直线拉直
|
// 标记是否进行了直线拉直
|
||||||
bool wasStraightened = false;
|
bool wasStraightened = false;
|
||||||
|
StylusPointCollection preBrushHandwritingPoints = null;
|
||||||
|
Stroke strokeForHandwritingBeautify = null;
|
||||||
|
|
||||||
if (Settings.Canvas.FitToCurve) drawingAttributes.FitToCurve = false;
|
if (Settings.Canvas.FitToCurve) drawingAttributes.FitToCurve = false;
|
||||||
|
|
||||||
@@ -379,7 +398,7 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
inkCanvas.Opacity = 1;
|
inkCanvas.Opacity = 1;
|
||||||
var touchPressureSimulationApplied = false;
|
var touchPressureSimulationApplied = false;
|
||||||
var preBrushHandwritingPoints = CloneStylusPointCollectionForHandwritingInput(e.Stroke?.StylusPoints);
|
preBrushHandwritingPoints = handwritingRawPointsForRecognizer;
|
||||||
|
|
||||||
if (Settings.Canvas.DisablePressure)
|
if (Settings.Canvas.DisablePressure)
|
||||||
{
|
{
|
||||||
@@ -500,6 +519,8 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
// Apply line straightening and endpoint snapping if ink-to-shape is enabled
|
// Apply line straightening and endpoint snapping if ink-to-shape is enabled
|
||||||
|
|
||||||
|
Stroke straightStrokeForHandwritingKey = null;
|
||||||
|
|
||||||
if (Settings.InkToShape.IsInkToShapeEnabled)
|
if (Settings.InkToShape.IsInkToShapeEnabled)
|
||||||
{
|
{
|
||||||
// 检查是否启用了直线自动拉直功能
|
// 检查是否启用了直线自动拉直功能
|
||||||
@@ -542,6 +563,8 @@ namespace Ink_Canvas
|
|||||||
inkCanvas.Strokes.Add(straightStroke);
|
inkCanvas.Strokes.Add(straightStroke);
|
||||||
_currentCommitType = CommitReason.UserInput;
|
_currentCommitType = CommitReason.UserInput;
|
||||||
|
|
||||||
|
straightStrokeForHandwritingKey = straightStroke;
|
||||||
|
|
||||||
// We can't modify e.Stroke directly, but we need to update newStrokes
|
// We can't modify e.Stroke directly, but we need to update newStrokes
|
||||||
// to ensure proper shape recognition for the straightened line
|
// to ensure proper shape recognition for the straightened line
|
||||||
if (newStrokes.Contains(e.Stroke))
|
if (newStrokes.Contains(e.Stroke))
|
||||||
@@ -555,8 +578,10 @@ namespace Ink_Canvas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Stroke strokeForHandwritingBeautify = e.Stroke;
|
strokeForHandwritingBeautify = e.Stroke;
|
||||||
if (wasStraightened && inkCanvas.Strokes.Count > 0)
|
if (wasStraightened && straightStrokeForHandwritingKey != null)
|
||||||
|
strokeForHandwritingBeautify = straightStrokeForHandwritingKey;
|
||||||
|
else if (wasStraightened && inkCanvas.Strokes.Count > 0)
|
||||||
strokeForHandwritingBeautify = inkCanvas.Strokes[inkCanvas.Strokes.Count - 1];
|
strokeForHandwritingBeautify = inkCanvas.Strokes[inkCanvas.Strokes.Count - 1];
|
||||||
|
|
||||||
|
|
||||||
@@ -910,9 +935,15 @@ namespace Ink_Canvas
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await InkToShapeProcessCoreAsync();
|
await InkToShapeProcessCoreAsync();
|
||||||
|
var strokeAfterTail = RunStrokeCollectedPostShapeRecognitionTail(e, wsTail);
|
||||||
if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify)
|
if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify)
|
||||||
ScheduleHandwritingGlyphReplaceAfterStrokeCollected(strokeHw, isBoardBrushStroke, preBrushHwPts);
|
{
|
||||||
RunStrokeCollectedPostShapeRecognitionTail(e, wsTail);
|
var canvasStrokeForHw = wsTail ? strokeHw : strokeAfterTail;
|
||||||
|
ScheduleHandwritingGlyphReplaceAfterStrokeCollected(
|
||||||
|
canvasStrokeForHw,
|
||||||
|
isBoardBrushStroke,
|
||||||
|
preBrushHwPts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -925,14 +956,21 @@ namespace Ink_Canvas
|
|||||||
if (InkToShapeProcess())
|
if (InkToShapeProcess())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify)
|
|
||||||
{
|
|
||||||
ScheduleHandwritingGlyphReplaceAfterStrokeCollected(strokeForHandwritingBeautify, isBoardBrushStroke, preBrushHandwritingPoints);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
||||||
|
|
||||||
RunStrokeCollectedPostShapeRecognitionTail(e, wasStraightened);
|
var strokeAfterTailSync = RunStrokeCollectedPostShapeRecognitionTail(e, wasStraightened);
|
||||||
|
if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify
|
||||||
|
&& !ShapeRecognitionRouter.ShouldRunShapeRecognition(
|
||||||
|
Settings.InkToShape.IsInkToShapeEnabled,
|
||||||
|
ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine)))
|
||||||
|
{
|
||||||
|
var canvasStrokeForHw = wasStraightened ? strokeForHandwritingBeautify : strokeAfterTailSync;
|
||||||
|
ScheduleHandwritingGlyphReplaceAfterStrokeCollected(
|
||||||
|
canvasStrokeForHw,
|
||||||
|
isBoardBrushStroke,
|
||||||
|
preBrushHandwritingPoints);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -967,6 +1005,8 @@ namespace Ink_Canvas
|
|||||||
inkCanvas.Strokes.Remove(original);
|
inkCanvas.Strokes.Remove(original);
|
||||||
inkCanvas.Strokes.Add(smoothed);
|
inkCanvas.Strokes.Add(smoothed);
|
||||||
_currentCommitType = CommitReason.UserInput;
|
_currentCommitType = CommitReason.UserInput;
|
||||||
|
// 收笔尾部仍以 original 登记手写批次;异步平滑后画布对象变为 smoothed,须迁移引用,否则防抖识别时字典 miss 会退回画布几何(非实时笔锋常见)。
|
||||||
|
MigrateHandwritingBeautifyCanvasStrokeReference(original, smoothed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2730,6 +2770,29 @@ namespace Ink_Canvas
|
|||||||
return corners.OrderBy(p => Math.Atan2(p.Y - center.Y, p.X - center.X)).ToList();
|
return corners.OrderBy(p => Math.Atan2(p.Y - center.Y, p.X - center.X)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步墨迹平滑将画布上的 <paramref name="fromStroke"/> 替换为 <paramref name="toStroke"/> 后,把手写字形替换批次里的画布引用一并迁移,使识别仍命中「原始点快照」字典项。
|
||||||
|
/// </summary>
|
||||||
|
private void MigrateHandwritingBeautifyCanvasStrokeReference(Stroke fromStroke, Stroke toStroke)
|
||||||
|
{
|
||||||
|
if (fromStroke == null || toStroke == null || ReferenceEquals(fromStroke, toStroke))
|
||||||
|
return;
|
||||||
|
if (!Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_handwritingBeautifyInkInputByCanvasStroke.TryGetValue(fromStroke, out var inkInput))
|
||||||
|
{
|
||||||
|
_handwritingBeautifyInkInputByCanvasStroke.Remove(fromStroke);
|
||||||
|
_handwritingBeautifyInkInputByCanvasStroke[toStroke] = inkInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < _handwritingRecentStrokesForBeautify.Count; i++)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(_handwritingRecentStrokesForBeautify[i], fromStroke))
|
||||||
|
_handwritingRecentStrokesForBeautify[i] = toStroke;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 收笔后:在墨迹转形状(若启用)完成之后,将笔画并入批次并启动/重置停笔防抖计时器,再于延迟后多笔合并矫正。
|
/// 收笔后:在墨迹转形状(若启用)完成之后,将笔画并入批次并启动/重置停笔防抖计时器,再于延迟后多笔合并矫正。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2788,10 +2851,19 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
if (preBrushHandwritingPoints != null && preBrushHandwritingPoints.Count > 0)
|
if (preBrushHandwritingPoints != null && preBrushHandwritingPoints.Count > 0)
|
||||||
{
|
{
|
||||||
_handwritingBeautifyInkInputByCanvasStroke[strokeForBeautify] = new Stroke(preBrushHandwritingPoints)
|
// 再拷贝一份给识别专用 Stroke,避免与外部 StylusPointCollection 或 WPF Stroke 内部共享后被改写。
|
||||||
|
var ptsForRecognizer = CloneStylusPointCollectionForHandwritingInput(preBrushHandwritingPoints);
|
||||||
|
if (ptsForRecognizer != null && ptsForRecognizer.Count > 0)
|
||||||
{
|
{
|
||||||
DrawingAttributes = strokeForBeautify.DrawingAttributes.Clone()
|
_handwritingBeautifyInkInputByCanvasStroke[strokeForBeautify] = new Stroke(ptsForRecognizer)
|
||||||
};
|
{
|
||||||
|
DrawingAttributes = strokeForBeautify.DrawingAttributes.Clone()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_handwritingBeautifyInkInputByCanvasStroke.Remove(strokeForBeautify);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2856,9 +2928,17 @@ namespace Ink_Canvas
|
|||||||
continue;
|
continue;
|
||||||
canvasStrokes.Add(s);
|
canvasStrokes.Add(s);
|
||||||
if (_handwritingBeautifyInkInputByCanvasStroke.TryGetValue(s, out var inkInput) && inkInput != null)
|
if (_handwritingBeautifyInkInputByCanvasStroke.TryGetValue(s, out var inkInput) && inkInput != null)
|
||||||
|
{
|
||||||
recognitionInput.Add(inkInput);
|
recognitionInput.Add(inkInput);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile(
|
||||||
|
"[手写体] 批次识别输入回退为画布笔画(未命中原始点快照)。画布点数=" +
|
||||||
|
(s.StylusPoints?.Count ?? 0),
|
||||||
|
LogHelper.LogType.Info);
|
||||||
recognitionInput.Add(s);
|
recognitionInput.Add(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canvasStrokes.Count == 0)
|
if (canvasStrokes.Count == 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user