Files
XplorePlane/XP.Camera/Calibration/ViewModels/CalibrationViewModel.cs
T

159 lines
5.6 KiB
C#

using Emgu.CV;
using Emgu.CV.Structure;
using Serilog;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Windows.Media.Imaging;
using Res = XP.Camera.Calibration.Resources.CalibrationResources;
namespace XP.Camera.Calibration.ViewModels;
public class CalibrationViewModel : BindableBase
{
private readonly ICalibrationDialogService _dialogService;
private readonly CalibrationProcessor _calibrator = new();
private Image<Bgr, byte>? _currentImage;
private static readonly ILogger _logger = Log.ForContext<CalibrationViewModel>();
private BitmapSource? _imageSource;
private string _statusText = Res.CalibrationStatusReady;
private bool _showWorldCoordinates;
public CalibrationViewModel(ICalibrationDialogService dialogService)
{
_dialogService = dialogService;
CalibrationPoints = new ObservableCollection<CalibrationProcessor.CalibrationPoint>();
LoadImageCommand = new DelegateCommand(LoadImage);
LoadCsvCommand = new DelegateCommand(LoadCsv);
CalibrateCommand = new DelegateCommand(Calibrate, CanCalibrate)
.ObservesProperty(() => CalibrationPoints.Count);
SaveCalibrationCommand = new DelegateCommand(SaveCalibration);
LoadCalibrationCommand = new DelegateCommand(LoadCalibration);
}
public ObservableCollection<CalibrationProcessor.CalibrationPoint> CalibrationPoints { get; }
public BitmapSource? ImageSource
{
get => _imageSource;
set => SetProperty(ref _imageSource, value);
}
public string StatusText
{
get => _statusText;
set => SetProperty(ref _statusText, value);
}
public bool ShowWorldCoordinates
{
get => _showWorldCoordinates;
set => SetProperty(ref _showWorldCoordinates, value);
}
public DelegateCommand LoadImageCommand { get; }
public DelegateCommand LoadCsvCommand { get; }
public DelegateCommand CalibrateCommand { get; }
public DelegateCommand SaveCalibrationCommand { get; }
public DelegateCommand LoadCalibrationCommand { get; }
private void LoadImage()
{
_logger.Information("Loading image file");
var fileName = _dialogService.ShowOpenFileDialog("图像文件|*.jpg;*.png;*.bmp;*.tif");
if (fileName == null) return;
_currentImage = new Image<Bgr, byte>(fileName);
ImageSource = MatToBitmapSource(_currentImage.Mat);
StatusText = string.Format(Res.CalibrationStatusImageLoaded, fileName);
RaiseEvent(ImageLoadedRequested);
}
private void LoadCsv()
{
var fileName = _dialogService.ShowOpenFileDialog("CSV文件|*.csv|所有文件|*.*");
if (fileName == null) return;
var points = _calibrator.LoadPointsFromCsv(fileName);
CalibrationPoints.Clear();
foreach (var pt in points)
CalibrationPoints.Add(pt);
StatusText = string.Format(Res.CalibrationStatusCsvLoaded, CalibrationPoints.Count, fileName);
}
private bool CanCalibrate() => CalibrationPoints.Count >= 4;
private void Calibrate()
{
if (CalibrationPoints.Count < 4)
{
_dialogService.ShowError(Res.CalibrationErrorMinPoints, Res.CalibrationSuccessTitle);
return;
}
if (_calibrator.Calibrate(new List<CalibrationProcessor.CalibrationPoint>(CalibrationPoints)))
{
StatusText = string.Format(Res.CalibrationStatusSuccess, CalibrationPoints.Count);
_dialogService.ShowInfo(Res.CalibrationSuccessMessage, Res.CalibrationSuccessTitle);
}
else
{
StatusText = Res.CalibrationStatusFailed;
_dialogService.ShowError(Res.CalibrationStatusFailed, Res.CalibrationSuccessTitle);
}
}
private void SaveCalibration()
{
var fileName = _dialogService.ShowSaveFileDialog("标定文件|*.json", "calibration.json");
if (fileName == null) return;
_calibrator.SaveCalibration(fileName, new List<CalibrationProcessor.CalibrationPoint>(CalibrationPoints));
StatusText = string.Format(Res.CalibrationStatusSaved, fileName);
_dialogService.ShowInfo(Res.CalibrationSaveSuccess, Res.CalibrationSuccessTitle);
}
private void LoadCalibration()
{
var fileName = _dialogService.ShowOpenFileDialog("标定文件|*.json");
if (fileName == null) return;
if (_calibrator.LoadCalibration(fileName))
{
StatusText = string.Format(Res.CalibrationStatusLoaded, fileName);
_dialogService.ShowInfo(Res.CalibrationLoadSuccess, Res.CalibrationSuccessTitle);
}
else
{
_dialogService.ShowError(Res.CalibrationLoadFailed, Res.CalibrationSuccessTitle);
}
}
public PointF ConvertPixelToWorld(PointF pixel) => _calibrator.PixelToWorld(pixel);
public Image<Bgr, byte>? CurrentImage => _currentImage;
public event EventHandler? ImageLoadedRequested;
private void RaiseEvent(EventHandler? handler) => handler?.Invoke(this, EventArgs.Empty);
private static BitmapSource MatToBitmapSource(Mat mat)
{
using var bitmap = mat.ToBitmap();
var hBitmap = bitmap.GetHbitmap();
try
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
hBitmap, IntPtr.Zero, System.Windows.Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap);
}
}
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
}