From 19b63fd41945330ceb154b9a07c62b20770dc6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E4=BC=9F?= Date: Mon, 1 Jun 2026 17:08:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=8C=B9=E9=85=8D=E5=8A=A9?= =?UTF-8?q?=E6=89=8B=E5=9C=A8=E4=BF=9D=E5=AD=98=E6=A8=A1=E5=9E=8B=E6=97=B6?= =?UTF-8?q?=E5=86=99=E5=85=A5=E5=90=8C=E5=90=8D=E5=8F=82=E8=80=83=E4=BD=8D?= =?UTF-8?q?=E5=A7=BF=20JSON=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 训练 ROI 后记录基准中心和角度,保存模型时同步生成同名元数据文件,并在加载模型时自动回读,减少后续对齐配置的人工录入。 Co-authored-by: Cursor --- .../TemplateMatchAssistantViewModel.cs | 112 +++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/XplorePlane/ViewModels/ImageProcessing/TemplateMatchAssistantViewModel.cs b/XplorePlane/ViewModels/ImageProcessing/TemplateMatchAssistantViewModel.cs index 2cb5579..f239834 100644 --- a/XplorePlane/ViewModels/ImageProcessing/TemplateMatchAssistantViewModel.cs +++ b/XplorePlane/ViewModels/ImageProcessing/TemplateMatchAssistantViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text.Json; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; @@ -47,6 +48,10 @@ public class TemplateMatchAssistantViewModel : BindableBase, IDisposable private bool _useSimd = true; private bool _useSubPixel; private bool _isModelReady; + private bool _hasReferencePose; + private double _referenceCenterX; + private double _referenceCenterY; + private double _referenceAngle; public TemplateMatchAssistantViewModel( IEventAggregator eventAggregator, @@ -244,6 +249,10 @@ public class TemplateMatchAssistantViewModel : BindableBase, IDisposable } IsModelReady = true; + _hasReferencePose = true; + _referenceCenterX = rx + rw * 0.5; + _referenceCenterY = ry + rh * 0.5; + _referenceAngle = 0.0; StatusMessage = $"模板已从 ROI 学习成功({rw}×{rh} 像素)。可保存模型或运行匹配。"; _logger.Info("Template assistant: learned from ROI {0},{1},{2},{3}", rx, ry, rw, rh); } @@ -284,7 +293,19 @@ public class TemplateMatchAssistantViewModel : BindableBase, IDisposable _eventAggregator.GetEvent().Publish(); IsModelReady = true; - StatusMessage = $"已加载模型: {Path.GetFileName(dlg.FileName)}"; + if (TryLoadModelMetadata(dlg.FileName, out var metadata)) + { + _hasReferencePose = true; + _referenceCenterX = metadata.ReferencePose.CenterX; + _referenceCenterY = metadata.ReferencePose.CenterY; + _referenceAngle = metadata.ReferencePose.Angle; + StatusMessage = $"已加载模型: {Path.GetFileName(dlg.FileName)}(已读取同名参考位姿 JSON)"; + } + else + { + _hasReferencePose = false; + StatusMessage = $"已加载模型: {Path.GetFileName(dlg.FileName)}"; + } } catch (Exception ex) { @@ -312,7 +333,14 @@ public class TemplateMatchAssistantViewModel : BindableBase, IDisposable lock (_matcherLock) ok = _matcher != null && _matcher.SaveModel(dlg.FileName); if (ok) - StatusMessage = $"模型已保存: {dlg.FileName}"; + { + if (TrySaveModelMetadata(dlg.FileName, out var metadataPath, out var metadataError)) + StatusMessage = $"模型已保存: {dlg.FileName};参考位姿已保存: {metadataPath}"; + else if (_hasReferencePose) + StatusMessage = $"模型已保存: {dlg.FileName};参考位姿 JSON 保存失败: {metadataError}"; + else + StatusMessage = $"模型已保存: {dlg.FileName};未生成参考位姿 JSON(当前无示教基准位姿)。"; + } else StatusMessage = "模型保存失败。"; } @@ -485,6 +513,71 @@ public class TemplateMatchAssistantViewModel : BindableBase, IDisposable return image; } + private bool TrySaveModelMetadata(string modelPath, out string metadataPath, out string? errorMessage) + { + metadataPath = Path.ChangeExtension(modelPath, ".json"); + errorMessage = null; + if (!_hasReferencePose) + return false; + + try + { + string modelFileName = Path.GetFileName(modelPath); + string templateName = Path.GetFileNameWithoutExtension(modelPath); + var metadata = new TemplateModelMetadata + { + TemplateName = templateName, + ModelFileName = modelFileName, + SavedAt = DateTime.Now, + ReferencePose = new TemplateReferencePose + { + CenterX = _referenceCenterX, + CenterY = _referenceCenterY, + Angle = _referenceAngle + } + }; + + var json = JsonSerializer.Serialize(metadata, new JsonSerializerOptions + { + WriteIndented = true + }); + File.WriteAllText(metadataPath, json); + return true; + } + catch (Exception ex) + { + errorMessage = ex.Message; + Log.Error(ex, "Save template metadata failed"); + return false; + } + } + + private static bool TryLoadModelMetadata(string modelPath, out TemplateModelMetadata metadata) + { + metadata = new TemplateModelMetadata(); + try + { + string metadataPath = Path.ChangeExtension(modelPath, ".json"); + if (!File.Exists(metadataPath)) + return false; + + string json = File.ReadAllText(metadataPath); + var parsed = JsonSerializer.Deserialize(json, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + if (parsed?.ReferencePose == null) + return false; + + metadata = parsed; + return true; + } + catch + { + return false; + } + } + public void Dispose() { if (_disposed) return; @@ -505,3 +598,18 @@ public class TemplateMatchAssistantViewModel : BindableBase, IDisposable } } } + +public sealed class TemplateModelMetadata +{ + public string TemplateName { get; set; } = string.Empty; + public string ModelFileName { get; set; } = string.Empty; + public DateTime SavedAt { get; set; } + public TemplateReferencePose ReferencePose { get; set; } = new(); +} + +public sealed class TemplateReferencePose +{ + public double CenterX { get; set; } + public double CenterY { get; set; } + public double Angle { get; set; } +}