|
|
@@ -4,7 +4,10 @@ using OxyPlot;
|
|
|
using OxyPlot.Axes;
|
|
|
using OxyPlot.Series;
|
|
|
using Prism.Commands;
|
|
|
+using Prism.Events;
|
|
|
+using Prism.Ioc;
|
|
|
using Prism.Mvvm;
|
|
|
+using Prism.Regions;
|
|
|
using SqlSugar;
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
@@ -17,116 +20,225 @@ using System.Windows.Media;
|
|
|
using System.Windows.Media.Imaging;
|
|
|
using System.Windows.Threading;
|
|
|
using TeamAAS_VP.Core;
|
|
|
+using TeamAAS_VP.Core.Lights;
|
|
|
using TeamAAS_VP.Enums;
|
|
|
+using TeamAAS_VP.Interfaces;
|
|
|
using TeamAAS_VP.Models;
|
|
|
using TeamAAS_VP.Services;
|
|
|
+using static TeamAAS_VP.ViewModels.DebugMod.LightManualViewModel;
|
|
|
|
|
|
-namespace TeamAAS_VP.Controls
|
|
|
+namespace TeamAAS_VP.ViewModels.DebugMod
|
|
|
{
|
|
|
/// <summary>
|
|
|
- /// FocusAnalyzer用户控件ViewModel
|
|
|
+ /// 用于棋盘格清晰度分析的 ViewModel。
|
|
|
+ /// 提供图像加载、棋盘格检测、清晰度分析、ROI 管理、结果导出及趋势绘制等功能。
|
|
|
+ /// 绑定到对应的用户控件,负责对 UI 的数据和命令交互。
|
|
|
/// </summary>
|
|
|
- public class FocusAnalyzerViewModel : BindableBase
|
|
|
+ public class FocusAnalyzerViewModel : BindableBase, INavigationAware
|
|
|
{
|
|
|
+ // 事件聚合器、配置服务、相机服务的引用(由容器注入)
|
|
|
+ IEventAggregator _eventAggregator;
|
|
|
+ IConfigService _configService;
|
|
|
+ ICameraService _cameraService;
|
|
|
+
|
|
|
+ // 辅助服务(对话框、图像分析、棋盘检测)和定时器
|
|
|
private readonly FileDialogService _fileDialogService;
|
|
|
private readonly ImageAnalysisService _imageAnalysisService;
|
|
|
private readonly CheckerboardDetectionService _checkerboardService;
|
|
|
private readonly DispatcherTimer _analysisTimer;
|
|
|
|
|
|
- private string _title = "棋盘格清晰度分析工具";
|
|
|
- private BitmapSource _currentImage;
|
|
|
+ // 当前加载的 OpenCV Mat(需要及时释放)
|
|
|
private Mat _currentMat;
|
|
|
- private AnalysisParameters _parameters;
|
|
|
- private AnalysisResult _currentResult;
|
|
|
- private RoiModel _currentRoi;
|
|
|
- private ChessboardInfo _chessboardInfo;
|
|
|
- private RoiOperationMode _roiMode;
|
|
|
- private string _statusText;
|
|
|
- private bool _isAnalyzing;
|
|
|
- private double _bestSharpness;
|
|
|
- private int _frameCount;
|
|
|
- private PlotModel _plotModel;
|
|
|
+
|
|
|
+ // 分析结果历史记录(用于导出与绘图)
|
|
|
private List<AnalysisResult> _resultHistory;
|
|
|
|
|
|
- // function to convert ROI from canvas coords to image pixel Rect (set by view)
|
|
|
+ #region 属性
|
|
|
+ /// <summary>
|
|
|
+ /// 将 View 中的 ROI(画布坐标)转换为图像像素坐标的函数。
|
|
|
+ /// View 侧需要设置此委托以便 ViewModel 能够将 ROI 转换为 OpenCvSharp.Rect。
|
|
|
+ /// 返回 null 表示无法转换或 ROI 无效。
|
|
|
+ /// </summary>
|
|
|
public Func<RoiModel, OpenCvSharp.Rect?> RoiToImageRectConverter { get; set; }
|
|
|
|
|
|
+ private string _title = "棋盘格清晰度分析工具";
|
|
|
+ /// <summary>
|
|
|
+ /// 窗口或控件标题(用于显示)。
|
|
|
+ /// </summary>
|
|
|
public string Title
|
|
|
{
|
|
|
get => _title;
|
|
|
set => SetProperty(ref _title, value);
|
|
|
}
|
|
|
|
|
|
+ private BitmapSource _currentImage;
|
|
|
+ /// <summary>
|
|
|
+ /// 当前用于显示的 WPF 位图(从 OpenCV Mat 转换)。
|
|
|
+ /// 该属性用于 UI 绑定,显示加载或绘制结果图像。
|
|
|
+ /// </summary>
|
|
|
public BitmapSource CurrentImage
|
|
|
{
|
|
|
get => _currentImage;
|
|
|
set => SetProperty(ref _currentImage, value);
|
|
|
}
|
|
|
|
|
|
+ private AnalysisParameters _parameters;
|
|
|
+ /// <summary>
|
|
|
+ /// 分析参数集合(例如:聚焦方法、分析模式、是否实时分析等)。
|
|
|
+ /// 外部可以通过绑定修改这些参数以影响分析行为。
|
|
|
+ /// </summary>
|
|
|
public AnalysisParameters Parameters
|
|
|
{
|
|
|
get => _parameters;
|
|
|
set => SetProperty(ref _parameters, value);
|
|
|
}
|
|
|
|
|
|
+ private AnalysisResult _currentResult;
|
|
|
+ /// <summary>
|
|
|
+ /// 当前最新一次分析的结果(包含清晰度、对比度、质量评分等)。
|
|
|
+ /// 供 UI 显示分析数值。
|
|
|
+ /// </summary>
|
|
|
public AnalysisResult CurrentResult
|
|
|
{
|
|
|
get => _currentResult;
|
|
|
set => SetProperty(ref _currentResult, value);
|
|
|
}
|
|
|
|
|
|
+ private RoiModel _currentRoi;
|
|
|
+ /// <summary>
|
|
|
+ /// 当前定义的 ROI(来自 UI),表示要分析的图像区域(以画布坐标表示)。
|
|
|
+ /// </summary>
|
|
|
public RoiModel CurrentRoi
|
|
|
{
|
|
|
get => _currentRoi;
|
|
|
set => SetProperty(ref _currentRoi, value);
|
|
|
}
|
|
|
|
|
|
+ private ChessboardInfo _chessboardInfo;
|
|
|
+ /// <summary>
|
|
|
+ /// 棋盘格检测信息(是否检测到、角点数组、包围矩形等)。
|
|
|
+ /// 检测成功后可用于限定分析区域或在图像上绘制棋盘格。
|
|
|
+ /// </summary>
|
|
|
public ChessboardInfo ChessboardInfo
|
|
|
{
|
|
|
get => _chessboardInfo;
|
|
|
set => SetProperty(ref _chessboardInfo, value);
|
|
|
}
|
|
|
|
|
|
+ private RoiOperationMode _roiMode;
|
|
|
+ /// <summary>
|
|
|
+ /// ROI 操作模式(绘制、移动、缩放等),由 UI 控制。
|
|
|
+ /// </summary>
|
|
|
public RoiOperationMode RoiMode
|
|
|
{
|
|
|
get => _roiMode;
|
|
|
set => SetProperty(ref _roiMode, value);
|
|
|
}
|
|
|
|
|
|
+ private string _statusText;
|
|
|
+ /// <summary>
|
|
|
+ /// 状态文本,用于在 UI 中显示当前操作或错误信息。
|
|
|
+ /// </summary>
|
|
|
public string StatusText
|
|
|
{
|
|
|
get => _statusText;
|
|
|
set => SetProperty(ref _statusText, value);
|
|
|
}
|
|
|
|
|
|
+ private bool _isAnalyzing;
|
|
|
+ /// <summary>
|
|
|
+ /// 指示是否正在进行实时分析(用于启用/禁用命令)。
|
|
|
+ /// </summary>
|
|
|
public bool IsAnalyzing
|
|
|
{
|
|
|
get => _isAnalyzing;
|
|
|
set => SetProperty(ref _isAnalyzing, value);
|
|
|
}
|
|
|
|
|
|
+ private PlotModel _plotModel;
|
|
|
+ /// <summary>
|
|
|
+ /// OxyPlot 的 PlotModel,用于在 UI 中绘制清晰度趋势图。
|
|
|
+ /// </summary>
|
|
|
public PlotModel PlotModel
|
|
|
{
|
|
|
get => _plotModel;
|
|
|
set => SetProperty(ref _plotModel, value);
|
|
|
}
|
|
|
|
|
|
+ private double _bestSharpness;
|
|
|
+ /// <summary>
|
|
|
+ /// 最佳清晰度值(内部字段)。BestSharpnessText 为其格式化显示。
|
|
|
+ /// </summary>
|
|
|
public string BestSharpnessText => _bestSharpness > 0 ? $"{_bestSharpness:F2}" : "N/A";
|
|
|
+
|
|
|
+ private int _frameCount;
|
|
|
+ /// <summary>
|
|
|
+ /// 已分析的帧数文本(格式化)。
|
|
|
+ /// </summary>
|
|
|
public string FrameCountText => $"帧数: {_frameCount}";
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 支持的清晰度计算方法集合(用于 UI 下拉选择)。
|
|
|
+ /// </summary>
|
|
|
public ObservableCollection<FocusMethod> FocusMethods { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 支持的分析模式集合(用于 UI 下拉选择)。
|
|
|
+ /// </summary>
|
|
|
public ObservableCollection<AnalysisMode> AnalysisModes { get; }
|
|
|
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region 命令
|
|
|
+ /// <summary>
|
|
|
+ /// 加载图像命令(弹出文件对话并加载图像)。
|
|
|
+ /// </summary>
|
|
|
public DelegateCommand LoadImageCommand { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 开始分析命令(支持实时和一次性分析)。
|
|
|
+ /// </summary>
|
|
|
public DelegateCommand StartAnalysisCommand { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 停止实时分析命令。
|
|
|
+ /// </summary>
|
|
|
public DelegateCommand StopAnalysisCommand { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 重置所有统计与图表的命令。
|
|
|
+ /// </summary>
|
|
|
public DelegateCommand ResetCommand { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 导出历史分析结果为 CSV 的命令。
|
|
|
+ /// </summary>
|
|
|
public DelegateCommand ExportCommand { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 清除当前 ROI 的命令。
|
|
|
+ /// </summary>
|
|
|
public DelegateCommand ClearRoiCommand { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检测棋盘格的命令(在当前图像或 ROI 上检测)。
|
|
|
+ /// </summary>
|
|
|
public DelegateCommand DetectChessboardCommand { get; }
|
|
|
|
|
|
- public FocusAnalyzerViewModel()
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 构造函数:注入必要服务并初始化命令、绘图和定时器。
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="eventAggregator">事件聚合器(用于模块间通信)</param>
|
|
|
+ /// <param name="configService">配置服务</param>
|
|
|
+ /// <param name="cameraService">相机服务</param>
|
|
|
+ public FocusAnalyzerViewModel(IEventAggregator eventAggregator, IConfigService configService, ICameraService cameraService)
|
|
|
{
|
|
|
+ _eventAggregator = eventAggregator;
|
|
|
+ _configService = configService;
|
|
|
+ _cameraService = cameraService;
|
|
|
+
|
|
|
_fileDialogService = new FileDialogService();
|
|
|
_imageAnalysisService = new ImageAnalysisService();
|
|
|
_checkerboardService = new CheckerboardDetectionService();
|
|
|
@@ -137,9 +249,11 @@ namespace TeamAAS_VP.Controls
|
|
|
_resultHistory = new List<AnalysisResult>();
|
|
|
_roiMode = RoiOperationMode.Draw; // 默认为绘制模式
|
|
|
|
|
|
+ // 初始化可枚举集合用于 UI 绑定
|
|
|
FocusMethods = new ObservableCollection<FocusMethod>(Enum.GetValues(typeof(FocusMethod)).Cast<FocusMethod>());
|
|
|
AnalysisModes = new ObservableCollection<AnalysisMode>(Enum.GetValues(typeof(AnalysisMode)).Cast<AnalysisMode>());
|
|
|
|
|
|
+ // 初始化命令并绑定 CanExecute/ObservesProperty
|
|
|
LoadImageCommand = new DelegateCommand(OnLoadImage);
|
|
|
StartAnalysisCommand = new DelegateCommand(OnStartAnalysis, CanStartAnalysis).ObservesProperty(() => CurrentImage);
|
|
|
StopAnalysisCommand = new DelegateCommand(OnStopAnalysis, () => IsAnalyzing).ObservesProperty(() => IsAnalyzing);
|
|
|
@@ -150,6 +264,7 @@ namespace TeamAAS_VP.Controls
|
|
|
|
|
|
InitializePlot();
|
|
|
|
|
|
+ // 分析定时器(用于实时分析)
|
|
|
_analysisTimer = new DispatcherTimer
|
|
|
{
|
|
|
Interval = TimeSpan.FromMilliseconds(100)
|
|
|
@@ -159,6 +274,12 @@ namespace TeamAAS_VP.Controls
|
|
|
StatusText = "就绪";
|
|
|
}
|
|
|
|
|
|
+ #region 方法
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 加载图像(通过文件对话框),并在加载后尝试自动检测棋盘格和/或触发实时分析。
|
|
|
+ /// 异常会被捕获并通过 StatusText 通知 UI。
|
|
|
+ /// </summary>
|
|
|
private void OnLoadImage()
|
|
|
{
|
|
|
try
|
|
|
@@ -167,10 +288,10 @@ namespace TeamAAS_VP.Controls
|
|
|
if (string.IsNullOrEmpty(filePath))
|
|
|
return;
|
|
|
|
|
|
- // 释放之前的图像
|
|
|
+ // 释放之前的图像以防内存泄漏
|
|
|
_currentMat?.Dispose();
|
|
|
|
|
|
- // 加载图像
|
|
|
+ // 从磁盘读取图像(彩色)
|
|
|
_currentMat = Cv2.ImRead(filePath, ImreadModes.Color);
|
|
|
if (_currentMat == null || _currentMat.Empty())
|
|
|
{
|
|
|
@@ -178,13 +299,13 @@ namespace TeamAAS_VP.Controls
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 转换为WPF图像
|
|
|
+ // 转换为 WPF 可显示的位图并更新 UI 绑定
|
|
|
CurrentImage = _currentMat.ToWriteableBitmap();
|
|
|
|
|
|
- // 自动检测棋盘格
|
|
|
+ // 自动检测棋盘格(异步)
|
|
|
OnDetectChessboard();
|
|
|
|
|
|
- // 如果启用了实时分析,立即分析
|
|
|
+ // 如果配置为实时分析,则立即进行一次分析(无需等待定时器)
|
|
|
if (Parameters.EnableRealtimeAnalysis)
|
|
|
{
|
|
|
AnalyzeCurrentImage();
|
|
|
@@ -198,6 +319,10 @@ namespace TeamAAS_VP.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 在当前图像或当前 ROI 上检测棋盘格(异步执行检测以避免阻塞 UI)。
|
|
|
+ /// 检测到后会将角点坐标从子图像坐标转换回全图坐标并在图片上绘制结果。
|
|
|
+ /// </summary>
|
|
|
private void OnDetectChessboard()
|
|
|
{
|
|
|
if (_currentMat == null || _currentMat.Empty())
|
|
|
@@ -211,6 +336,7 @@ namespace TeamAAS_VP.Controls
|
|
|
{
|
|
|
ChessboardInfo info = null;
|
|
|
|
|
|
+ // 优先在用户选定的 ROI 内检测
|
|
|
if (RoiToImageRectConverter != null && CurrentRoi?.IsValid == true)
|
|
|
{
|
|
|
var imgRoi = RoiToImageRectConverter(CurrentRoi);
|
|
|
@@ -221,6 +347,7 @@ namespace TeamAAS_VP.Controls
|
|
|
info = _checkerboardService.DetectChessboard(sub);
|
|
|
}
|
|
|
|
|
|
+ // 如果在子图中检测到角点,需要将角点坐标偏移回全图坐标
|
|
|
if (info != null && info.IsDetected)
|
|
|
{
|
|
|
for (int i = 0; i < info.Corners.Length; i++)
|
|
|
@@ -234,11 +361,13 @@ namespace TeamAAS_VP.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 如果 ROI 内未检测到,则在整张图上检测
|
|
|
if (info == null || !info.IsDetected)
|
|
|
{
|
|
|
info = _checkerboardService.DetectChessboard(_currentMat);
|
|
|
}
|
|
|
|
|
|
+ // 回到 UI 线程更新绑定与显示
|
|
|
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
|
|
{
|
|
|
ChessboardInfo = info;
|
|
|
@@ -247,7 +376,7 @@ namespace TeamAAS_VP.Controls
|
|
|
{
|
|
|
StatusText = $"检测成功: {info.CornersWidth}x{info.CornersHeight} 角点";
|
|
|
|
|
|
- // 绘制棋盘格
|
|
|
+ // 在图像上绘制检测到的棋盘并更新 CurrentImage
|
|
|
var displayMat = _currentMat.Clone();
|
|
|
_checkerboardService.DrawChessboard(displayMat, info);
|
|
|
CurrentImage = displayMat.ToWriteableBitmap();
|
|
|
@@ -266,11 +395,20 @@ namespace TeamAAS_VP.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 判断是否可以开始分析:需要有当前图像且当前没有正在分析。
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>如果可以开始分析返回 true,否则 false。</returns>
|
|
|
private bool CanStartAnalysis()
|
|
|
{
|
|
|
return CurrentImage != null && !IsAnalyzing;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 开始分析流程。
|
|
|
+ /// - 重置统计(帧数、最佳值、历史)
|
|
|
+ /// - 根据是否启用实时分析决定启动定时器或直接分析一次
|
|
|
+ /// </summary>
|
|
|
private void OnStartAnalysis()
|
|
|
{
|
|
|
IsAnalyzing = true;
|
|
|
@@ -291,6 +429,9 @@ namespace TeamAAS_VP.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 停止实时分析:停止定时器并更新状态。
|
|
|
+ /// </summary>
|
|
|
private void OnStopAnalysis()
|
|
|
{
|
|
|
_analysisTimer.Stop();
|
|
|
@@ -298,11 +439,20 @@ namespace TeamAAS_VP.Controls
|
|
|
StatusText = "已停止";
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 定时器回调:周期性触发一次图像分析。
|
|
|
+ /// </summary>
|
|
|
private void OnAnalysisTick(object sender, EventArgs e)
|
|
|
{
|
|
|
AnalyzeCurrentImage();
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 执行一次图像分析:
|
|
|
+ /// - 根据 AnalysisMode 决定使用 ROI、棋盘格或全图进行分析
|
|
|
+ /// - 调用 ImageAnalysisService 执行具体分析
|
|
|
+ /// - 更新 CurrentResult、历史、统计与趋势图
|
|
|
+ /// </summary>
|
|
|
private void AnalyzeCurrentImage()
|
|
|
{
|
|
|
if (_currentMat == null || _currentMat.Empty())
|
|
|
@@ -324,6 +474,7 @@ namespace TeamAAS_VP.Controls
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
+ // 如果未提供转换器,则直接按 RoiModel 的像素值构建(视为已是像素)
|
|
|
roi = new Rect(
|
|
|
(int)CurrentRoi.X,
|
|
|
(int)CurrentRoi.Y,
|
|
|
@@ -341,23 +492,25 @@ namespace TeamAAS_VP.Controls
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- // 执行分析
|
|
|
+ // 执行实际图像分析并获取结果
|
|
|
var result = _imageAnalysisService.AnalyzeImage(_currentMat, Parameters.FocusMethod, roi);
|
|
|
|
|
|
+ // 更新当前结果与历史记录
|
|
|
CurrentResult = result;
|
|
|
_resultHistory.Add(result);
|
|
|
_frameCount++;
|
|
|
|
|
|
- // 更新最佳清晰度
|
|
|
+ // 更新最佳清晰度并通知绑定
|
|
|
if (result.Sharpness > _bestSharpness)
|
|
|
{
|
|
|
_bestSharpness = result.Sharpness;
|
|
|
RaisePropertyChanged(nameof(BestSharpnessText));
|
|
|
}
|
|
|
|
|
|
+ // 更新帧数显示
|
|
|
RaisePropertyChanged(nameof(FrameCountText));
|
|
|
|
|
|
- // 更新图表
|
|
|
+ // 更新图表数据
|
|
|
UpdatePlot();
|
|
|
|
|
|
StatusText = $"清晰度: {result.Sharpness:F2}, 对比度: {result.Contrast:F2}";
|
|
|
@@ -368,6 +521,9 @@ namespace TeamAAS_VP.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 重置所有统计数据与图表,停止分析。
|
|
|
+ /// </summary>
|
|
|
private void OnReset()
|
|
|
{
|
|
|
OnStopAnalysis();
|
|
|
@@ -381,6 +537,9 @@ namespace TeamAAS_VP.Controls
|
|
|
RaisePropertyChanged(nameof(FrameCountText));
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 导出历史分析结果为 CSV 文件。使用 FileDialogService 获取保存路径。
|
|
|
+ /// </summary>
|
|
|
private void OnExport()
|
|
|
{
|
|
|
try
|
|
|
@@ -409,12 +568,18 @@ namespace TeamAAS_VP.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 清除当前 ROI(重置为默认),并通知 UI。
|
|
|
+ /// </summary>
|
|
|
private void OnClearRoi()
|
|
|
{
|
|
|
CurrentRoi = new RoiModel();
|
|
|
StatusText = "ROI已清除";
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 初始化 OxyPlot 的 PlotModel(坐标轴与清晰度折线序列)。
|
|
|
+ /// </summary>
|
|
|
private void InitializePlot()
|
|
|
{
|
|
|
PlotModel = new PlotModel
|
|
|
@@ -454,6 +619,9 @@ namespace TeamAAS_VP.Controls
|
|
|
PlotModel.Series.Add(series);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 用于根据历史结果更新趋势图。会清空旧点并重新添加所有点,然后刷新绘图。
|
|
|
+ /// </summary>
|
|
|
private void UpdatePlot()
|
|
|
{
|
|
|
if (PlotModel.Series.Count == 0)
|
|
|
@@ -463,14 +631,56 @@ namespace TeamAAS_VP.Controls
|
|
|
if (series == null)
|
|
|
return;
|
|
|
|
|
|
+ // 先清空已有点(保持序列样式不变)
|
|
|
series.Points.Clear();
|
|
|
|
|
|
+ // 将历史清晰度数据逐点加入序列(X 为帧索引,Y 为清晰度)
|
|
|
for (int i = 0; i < _resultHistory.Count; i++)
|
|
|
{
|
|
|
series.Points.Add(new OxyPlot.DataPoint(i + 1, _resultHistory[i].Sharpness));
|
|
|
}
|
|
|
|
|
|
+ // 通知 PlotModel 重绘
|
|
|
PlotModel.InvalidatePlot(true);
|
|
|
}
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region 继承
|
|
|
+ /// <summary>
|
|
|
+ /// 导航请求确认(Prism INavigationAware)。
|
|
|
+ /// 永远允许导航离开/进入(可以在需要时扩展为提示保存或取消操作)。
|
|
|
+ /// </summary>
|
|
|
+ public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
|
|
|
+ {
|
|
|
+ continuationCallback(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 导航到达时调用(可用于加载外部资源或初始化通道等)。
|
|
|
+ /// 当前保留为异步签名,便于以后扩展异步加载逻辑。
|
|
|
+ /// </summary>
|
|
|
+ public async void OnNavigatedTo(NavigationContext navigationContext)
|
|
|
+ {
|
|
|
+ // TODO: 需要时从灯管理服务加载通道或其他初始化逻辑
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 指示此实例是否作为目标处理此导航请求(通常返回 true)。
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>如果为导航目标返回 true,否则 false。</returns>
|
|
|
+ public bool IsNavigationTarget(NavigationContext navigationContext)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 导航离开时调用(可用于清理资源或停止定时器)。
|
|
|
+ /// </summary>
|
|
|
+ public void OnNavigatedFrom(NavigationContext navigationContext)
|
|
|
+ {
|
|
|
+ // nothing
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
}
|
|
|
}
|