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
{
///
/// FocusAnalyzer用户控件ViewModel
///
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 _resultHistory;
// function to convert ROI from canvas coords to image pixel Rect (set by view)
public Func 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 FocusMethods { get; }
public ObservableCollection 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();
_roiMode = RoiOperationMode.Draw; // 默认为绘制模式
FocusMethods = new ObservableCollection(Enum.GetValues(typeof(FocusMethod)).Cast());
AnalysisModes = new ObservableCollection(Enum.GetValues(typeof(AnalysisMode)).Cast());
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);
}
}
}