模板匹配助手在保存模型时写入同名参考位姿 JSON。

训练 ROI 后记录基准中心和角度,保存模型时同步生成同名元数据文件,并在加载模型时自动回读,减少后续对齐配置的人工录入。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
李伟
2026-06-01 17:08:20 +08:00
parent 5b4ff89ef0
commit 19b63fd419
@@ -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<TemplateMatchClearRoiOverlayEvent>().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<TemplateModelMetadata>(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; }
}