| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- using OpenCvSharp;
- using OpenCvSharp.WpfExtensions;
- using OxyPlot;
- using OxyPlot.Axes;
- using OxyPlot.Series;
- using Prism.Commands;
- using Prism.Mvvm;
- using SqlSugar;
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
- using System.Windows.Threading;
- using TeamAAS_VP.Core;
- using TeamAAS_VP.Enums;
- using TeamAAS_VP.Models;
- using TeamAAS_VP.Services;
- namespace TeamAAS_VP.Controls
- {
- /// <summary>
- /// FocusAnalyzer用户控件ViewModel
- /// </summary>
- public class FocusAnalyzerViewModel : BindableBase
- {
- private readonly FileDialogService _fileDialogService;
- private readonly ImageAnalysisService _imageAnalysisService;
- private readonly CheckerboardDetectionService _checkerboardService;
- private readonly DispatcherTimer _analysisTimer;
- private string _title = "棋盘格清晰度分析工具";
- private BitmapSource _currentImage;
- 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)
- public Func<RoiModel, OpenCvSharp.Rect?> RoiToImageRectConverter { get; set; }
- public string Title
- {
- get => _title;
- set => SetProperty(ref _title, value);
- }
- public BitmapSource CurrentImage
- {
- get => _currentImage;
- set => SetProperty(ref _currentImage, value);
- }
- public AnalysisParameters Parameters
- {
- get => _parameters;
- set => SetProperty(ref _parameters, value);
- }
- public AnalysisResult CurrentResult
- {
- get => _currentResult;
- set => SetProperty(ref _currentResult, value);
- }
- public RoiModel CurrentRoi
- {
- get => _currentRoi;
- set => SetProperty(ref _currentRoi, value);
- }
- public ChessboardInfo ChessboardInfo
- {
- get => _chessboardInfo;
- set => SetProperty(ref _chessboardInfo, value);
- }
- public RoiOperationMode RoiMode
- {
- get => _roiMode;
- set => SetProperty(ref _roiMode, value);
- }
- public string StatusText
- {
- get => _statusText;
- set => SetProperty(ref _statusText, value);
- }
- public bool IsAnalyzing
- {
- get => _isAnalyzing;
- set => SetProperty(ref _isAnalyzing, value);
- }
- public PlotModel PlotModel
- {
- get => _plotModel;
- set => SetProperty(ref _plotModel, value);
- }
- public string BestSharpnessText => _bestSharpness > 0 ? $"{_bestSharpness:F2}" : "N/A";
- public string FrameCountText => $"帧数: {_frameCount}";
- public ObservableCollection<FocusMethod> FocusMethods { get; }
- public ObservableCollection<AnalysisMode> AnalysisModes { get; }
- public DelegateCommand LoadImageCommand { get; }
- public DelegateCommand StartAnalysisCommand { get; }
- public DelegateCommand StopAnalysisCommand { get; }
- public DelegateCommand ResetCommand { get; }
- public DelegateCommand ExportCommand { get; }
- public DelegateCommand ClearRoiCommand { get; }
- public DelegateCommand DetectChessboardCommand { get; }
- public FocusAnalyzerViewModel()
- {
- _fileDialogService = new FileDialogService();
- _imageAnalysisService = new ImageAnalysisService();
- _checkerboardService = new CheckerboardDetectionService();
- _parameters = new AnalysisParameters();
- _currentResult = new AnalysisResult();
- _currentRoi = new RoiModel();
- _resultHistory = new List<AnalysisResult>();
- _roiMode = RoiOperationMode.Draw; // 默认为绘制模式
- FocusMethods = new ObservableCollection<FocusMethod>(Enum.GetValues(typeof(FocusMethod)).Cast<FocusMethod>());
- AnalysisModes = new ObservableCollection<AnalysisMode>(Enum.GetValues(typeof(AnalysisMode)).Cast<AnalysisMode>());
- LoadImageCommand = new DelegateCommand(OnLoadImage);
- StartAnalysisCommand = new DelegateCommand(OnStartAnalysis, CanStartAnalysis).ObservesProperty(() => CurrentImage);
- StopAnalysisCommand = new DelegateCommand(OnStopAnalysis, () => IsAnalyzing).ObservesProperty(() => IsAnalyzing);
- ResetCommand = new DelegateCommand(OnReset);
- ExportCommand = new DelegateCommand(OnExport, () => _resultHistory.Count > 0);
- ClearRoiCommand = new DelegateCommand(OnClearRoi);
- DetectChessboardCommand = new DelegateCommand(OnDetectChessboard, () => _currentMat != null);
- InitializePlot();
- _analysisTimer = new DispatcherTimer
- {
- Interval = TimeSpan.FromMilliseconds(100)
- };
- _analysisTimer.Tick += OnAnalysisTick;
- StatusText = "就绪";
- }
- private void OnLoadImage()
- {
- try
- {
- string filePath = _fileDialogService.OpenImageFile();
- if (string.IsNullOrEmpty(filePath))
- return;
- // 释放之前的图像
- _currentMat?.Dispose();
- // 加载图像
- _currentMat = Cv2.ImRead(filePath, ImreadModes.Color);
- if (_currentMat == null || _currentMat.Empty())
- {
- StatusText = "无法加载图像";
- return;
- }
- // 转换为WPF图像
- CurrentImage = _currentMat.ToWriteableBitmap();
- // 自动检测棋盘格
- OnDetectChessboard();
- // 如果启用了实时分析,立即分析
- if (Parameters.EnableRealtimeAnalysis)
- {
- AnalyzeCurrentImage();
- }
- StatusText = $"图像已加载: {_currentMat.Width}x{_currentMat.Height}";
- }
- catch (Exception ex)
- {
- StatusText = $"加载图像失败: {ex.Message}";
- }
- }
- private void OnDetectChessboard()
- {
- if (_currentMat == null || _currentMat.Empty())
- return;
- try
- {
- StatusText = "正在检测棋盘格...";
- Task.Run(() =>
- {
- ChessboardInfo info = null;
- if (RoiToImageRectConverter != null && CurrentRoi?.IsValid == true)
- {
- var imgRoi = RoiToImageRectConverter(CurrentRoi);
- if (imgRoi.HasValue)
- {
- using (var sub = new Mat(_currentMat, imgRoi.Value))
- {
- info = _checkerboardService.DetectChessboard(sub);
- }
- if (info != null && info.IsDetected)
- {
- for (int i = 0; i < info.Corners.Length; i++)
- {
- info.Corners[i].X += imgRoi.Value.X;
- info.Corners[i].Y += imgRoi.Value.Y;
- }
- var br = info.BoundingRect;
- info.BoundingRect = new OpenCvSharp.Rect(br.X + imgRoi.Value.X, br.Y + imgRoi.Value.Y, br.Width, br.Height);
- }
- }
- }
- if (info == null || !info.IsDetected)
- {
- info = _checkerboardService.DetectChessboard(_currentMat);
- }
- System.Windows.Application.Current.Dispatcher.Invoke(() =>
- {
- ChessboardInfo = info;
- if (info.IsDetected)
- {
- StatusText = $"检测成功: {info.CornersWidth}x{info.CornersHeight} 角点";
- // 绘制棋盘格
- var displayMat = _currentMat.Clone();
- _checkerboardService.DrawChessboard(displayMat, info);
- CurrentImage = displayMat.ToWriteableBitmap();
- displayMat.Dispose();
- }
- else
- {
- StatusText = "未检测到棋盘格";
- }
- });
- });
- }
- catch (Exception ex)
- {
- StatusText = $"检测失败: {ex.Message}";
- }
- }
- private bool CanStartAnalysis()
- {
- return CurrentImage != null && !IsAnalyzing;
- }
- private void OnStartAnalysis()
- {
- IsAnalyzing = true;
- _frameCount = 0;
- _bestSharpness = 0;
- _resultHistory.Clear();
- StatusText = "分析中...";
- if (Parameters.EnableRealtimeAnalysis)
- {
- _analysisTimer.Start();
- }
- else
- {
- AnalyzeCurrentImage();
- IsAnalyzing = false;
- StatusText = "分析完成";
- }
- }
- private void OnStopAnalysis()
- {
- _analysisTimer.Stop();
- IsAnalyzing = false;
- StatusText = "已停止";
- }
- private void OnAnalysisTick(object sender, EventArgs e)
- {
- AnalyzeCurrentImage();
- }
- private void AnalyzeCurrentImage()
- {
- if (_currentMat == null || _currentMat.Empty())
- return;
- try
- {
- Rect? roi = null;
- // 根据分析模式确定分析区域
- switch (Parameters.AnalysisMode)
- {
- case AnalysisMode.RoiOnly:
- if (CurrentRoi.IsValid)
- {
- if (RoiToImageRectConverter != null)
- {
- roi = RoiToImageRectConverter(CurrentRoi);
- }
- else
- {
- roi = new Rect(
- (int)CurrentRoi.X,
- (int)CurrentRoi.Y,
- (int)CurrentRoi.Width,
- (int)CurrentRoi.Height);
- }
- }
- break;
- case AnalysisMode.ChessboardOnly:
- if (ChessboardInfo?.IsDetected == true)
- {
- roi = ChessboardInfo.BoundingRect;
- }
- 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}";
- }
- catch (Exception ex)
- {
- StatusText = $"分析错误: {ex.Message}";
- }
- }
- private void OnReset()
- {
- OnStopAnalysis();
- _frameCount = 0;
- _bestSharpness = 0;
- _resultHistory.Clear();
- CurrentResult = new AnalysisResult();
- InitializePlot();
- StatusText = "已重置";
- RaisePropertyChanged(nameof(BestSharpnessText));
- RaisePropertyChanged(nameof(FrameCountText));
- }
- private void OnExport()
- {
- try
- {
- string filePath = _fileDialogService.SaveFile(
- $"分析报告_{DateTime.Now:yyyyMMdd_HHmmss}.csv",
- "CSV文件|*.csv|所有文件|*.*");
- if (string.IsNullOrEmpty(filePath))
- return;
- using (var writer = new System.IO.StreamWriter(filePath))
- {
- writer.WriteLine("时间,清晰度,对比度,质量评分");
- foreach (var result in _resultHistory)
- {
- writer.WriteLine($"{result.Timestamp:yyyy-MM-dd HH:mm:ss.fff},{result.Sharpness:F2},{result.Contrast:F2},{result.QualityScore:F2}");
- }
- }
- StatusText = $"报告已导出: {filePath}";
- }
- catch (Exception ex)
- {
- StatusText = $"导出失败: {ex.Message}";
- }
- }
- private void OnClearRoi()
- {
- CurrentRoi = new RoiModel();
- StatusText = "ROI已清除";
- }
- private void InitializePlot()
- {
- PlotModel = new PlotModel
- {
- Title = "清晰度趋势",
- Background = OxyColors.White
- };
- PlotModel.Axes.Add(new LinearAxis
- {
- Position = AxisPosition.Bottom,
- Title = "帧数",
- MajorGridlineStyle = LineStyle.Solid,
- MinorGridlineStyle = LineStyle.Dot,
- MajorGridlineColor = OxyColor.FromRgb(230, 230, 230)
- });
- PlotModel.Axes.Add(new LinearAxis
- {
- Position = AxisPosition.Left,
- Title = "清晰度",
- MajorGridlineStyle = LineStyle.Solid,
- MinorGridlineStyle = LineStyle.Dot,
- MajorGridlineColor = OxyColor.FromRgb(230, 230, 230)
- });
- var series = new LineSeries
- {
- Title = "清晰度",
- Color = OxyColor.FromRgb(0, 122, 204),
- StrokeThickness = 2,
- MarkerType = MarkerType.Circle,
- MarkerSize = 3,
- MarkerFill = OxyColor.FromRgb(0, 122, 204)
- };
- PlotModel.Series.Add(series);
- }
- private void UpdatePlot()
- {
- if (PlotModel.Series.Count == 0)
- return;
- var series = PlotModel.Series[0] as LineSeries;
- if (series == null)
- return;
- series.Points.Clear();
- for (int i = 0; i < _resultHistory.Count; i++)
- {
- series.Points.Add(new OxyPlot.DataPoint(i + 1, _resultHistory[i].Sharpness));
- }
- PlotModel.InvalidatePlot(true);
- }
- }
- }
|