diff --git a/XplorePlane/App.xaml.cs b/XplorePlane/App.xaml.cs index 5c3be5b..07dfcb3 100644 --- a/XplorePlane/App.xaml.cs +++ b/XplorePlane/App.xaml.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Windows; using XplorePlane.Views; using XplorePlane.ViewModels; diff --git a/XplorePlane/Libs/ImageProcessing/ImageProcessing.Controls.dll b/XplorePlane/Libs/ImageProcessing/ImageProcessing.Controls.dll new file mode 100644 index 0000000..93b6866 Binary files /dev/null and b/XplorePlane/Libs/ImageProcessing/ImageProcessing.Controls.dll differ diff --git a/XplorePlane/Libs/ImageProcessing/ImageProcessing.Core.dll b/XplorePlane/Libs/ImageProcessing/ImageProcessing.Core.dll new file mode 100644 index 0000000..84f4757 Binary files /dev/null and b/XplorePlane/Libs/ImageProcessing/ImageProcessing.Core.dll differ diff --git a/XplorePlane/Libs/ImageProcessing/ImageProcessing.Core.pdb b/XplorePlane/Libs/ImageProcessing/ImageProcessing.Core.pdb new file mode 100644 index 0000000..2c67e60 Binary files /dev/null and b/XplorePlane/Libs/ImageProcessing/ImageProcessing.Core.pdb differ diff --git a/XplorePlane/Libs/ImageProcessing/ImageProcessing.Processors.dll b/XplorePlane/Libs/ImageProcessing/ImageProcessing.Processors.dll new file mode 100644 index 0000000..78e74c6 Binary files /dev/null and b/XplorePlane/Libs/ImageProcessing/ImageProcessing.Processors.dll differ diff --git a/XplorePlane/Libs/ImageProcessing/ImageProcessing.Processors.pdb b/XplorePlane/Libs/ImageProcessing/ImageProcessing.Processors.pdb new file mode 100644 index 0000000..0c5e1a2 Binary files /dev/null and b/XplorePlane/Libs/ImageProcessing/ImageProcessing.Processors.pdb differ diff --git a/XplorePlane/Services/IImageProcessingService.cs b/XplorePlane/Services/IImageProcessingService.cs index c9e5f12..45b4692 100644 --- a/XplorePlane/Services/IImageProcessingService.cs +++ b/XplorePlane/Services/IImageProcessingService.cs @@ -11,6 +11,7 @@ namespace XplorePlane.Services { IReadOnlyList GetAvailableProcessors(); IReadOnlyList GetProcessorParameters(string processorName); + ImageProcessorBase GetProcessor(string processorName); void RegisterProcessor(string name, ImageProcessorBase processor); Task ProcessImageAsync( diff --git a/XplorePlane/Services/ImageProcessingService.cs b/XplorePlane/Services/ImageProcessingService.cs index 57a5cdb..87b0e77 100644 --- a/XplorePlane/Services/ImageProcessingService.cs +++ b/XplorePlane/Services/ImageProcessingService.cs @@ -71,6 +71,13 @@ namespace XplorePlane.Services throw new ArgumentException($"Processor not registered: {processorName}", nameof(processorName)); } + public ImageProcessorBase GetProcessor(string processorName) + { + if (_processorRegistry.TryGetValue(processorName, out var processor)) + return processor; + throw new ArgumentException($"Processor not registered or is 16-bit only: {processorName}", nameof(processorName)); + } + public async Task ProcessImageAsync( BitmapSource source, string processorName, diff --git a/XplorePlane/ViewModels/ImageProcessingViewModel.cs b/XplorePlane/ViewModels/ImageProcessingViewModel.cs index 8856a02..d1b1028 100644 --- a/XplorePlane/ViewModels/ImageProcessingViewModel.cs +++ b/XplorePlane/ViewModels/ImageProcessingViewModel.cs @@ -1,6 +1,9 @@ +using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.IO; using System.Windows.Media.Imaging; +using Microsoft.Win32; using Prism.Commands; using Prism.Mvvm; using Serilog; @@ -36,6 +39,8 @@ namespace XplorePlane.ViewModels SelectProcessorCommand = new DelegateCommand(OnSelectProcessor); ApplyProcessingCommand = new DelegateCommand(OnApplyProcessing); ResetImageCommand = new DelegateCommand(OnResetImage); + LoadImageCommand = new DelegateCommand(OnLoadImage); + SaveResultCommand = new DelegateCommand(OnSaveResult, () => CurrentImage != null); } public ObservableCollection AvailableProcessors { get; } @@ -50,7 +55,11 @@ namespace XplorePlane.ViewModels public BitmapSource CurrentImage { get => _currentImage; - set => SetProperty(ref _currentImage, value); + set + { + SetProperty(ref _currentImage, value); + SaveResultCommand?.RaiseCanExecuteChanged(); + } } public BitmapSource OriginalImage @@ -80,6 +89,8 @@ namespace XplorePlane.ViewModels public DelegateCommand SelectProcessorCommand { get; } public DelegateCommand ApplyProcessingCommand { get; } public DelegateCommand ResetImageCommand { get; } + public DelegateCommand LoadImageCommand { get; } + public DelegateCommand SaveResultCommand { get; } private void OnSelectProcessor(string processorName) { @@ -153,5 +164,67 @@ namespace XplorePlane.ViewModels StatusMessage = "Image reset to original"; ProcessingProgress = 0; } + + private void OnLoadImage() + { + var dlg = new OpenFileDialog + { + Title = "加载图像", + Filter = "图像文件|*.bmp;*.png;*.jpg;*.jpeg;*.tif;*.tiff|所有文件|*.*" + }; + if (dlg.ShowDialog() != true) return; + + try + { + var bitmap = new BitmapImage(); + bitmap.BeginInit(); + bitmap.UriSource = new Uri(dlg.FileName); + bitmap.CacheOption = BitmapCacheOption.OnLoad; + bitmap.EndInit(); + bitmap.Freeze(); + + OriginalImage = bitmap; + CurrentImage = bitmap; + StatusMessage = $"已加载:{Path.GetFileName(dlg.FileName)}"; + ProcessingProgress = 0; + } + catch (Exception ex) + { + StatusMessage = $"加载失败:{ex.Message}"; + _logger.Error(ex, "Failed to load image: {Path}", dlg.FileName); + } + } + + private void OnSaveResult() + { + if (CurrentImage == null) return; + + var dlg = new SaveFileDialog + { + Title = "保存结果", + Filter = "PNG 图像|*.png|BMP 图像|*.bmp|JPEG 图像|*.jpg", + DefaultExt = ".png" + }; + if (dlg.ShowDialog() != true) return; + + try + { + BitmapEncoder encoder = Path.GetExtension(dlg.FileName).ToLower() switch + { + ".bmp" => new BmpBitmapEncoder(), + ".jpg" or ".jpeg" => new JpegBitmapEncoder(), + _ => new PngBitmapEncoder() + }; + encoder.Frames.Add(BitmapFrame.Create(CurrentImage)); + using var stream = File.OpenWrite(dlg.FileName); + encoder.Save(stream); + StatusMessage = $"已保存:{Path.GetFileName(dlg.FileName)}"; + } + catch (Exception ex) + { + StatusMessage = $"保存失败:{ex.Message}"; + _logger.Error(ex, "Failed to save image: {Path}", dlg.FileName); + } + } } } diff --git a/XplorePlane/ViewModels/MainViewModel.cs b/XplorePlane/ViewModels/MainViewModel.cs index a325af7..bd35a80 100644 --- a/XplorePlane/ViewModels/MainViewModel.cs +++ b/XplorePlane/ViewModels/MainViewModel.cs @@ -24,6 +24,7 @@ namespace XplorePlane.ViewModels public DelegateCommand ExportCommand { get; set; } public DelegateCommand ClearCommand { get; set; } public DelegateCommand EditPropertiesCommand { get; set; } + public DelegateCommand OpenImageProcessingCommand { get; set; } public MainViewModel(ILogger logger) { @@ -37,6 +38,12 @@ namespace XplorePlane.ViewModels ExportCommand = new DelegateCommand(OnExport); ClearCommand = new DelegateCommand(OnClear); EditPropertiesCommand = new DelegateCommand(OnEditProperties); + OpenImageProcessingCommand = new DelegateCommand(() => + { + var window = new Views.ImageProcessingWindow(); + window.Show(); + _logger.Information("图像处理窗口已打开"); + }); _logger.Information("MainViewModel 已初始化"); } diff --git a/XplorePlane/ViewModels/ProcessorParameterVM.cs b/XplorePlane/ViewModels/ProcessorParameterVM.cs index 116ffec..d8539f2 100644 --- a/XplorePlane/ViewModels/ProcessorParameterVM.cs +++ b/XplorePlane/ViewModels/ProcessorParameterVM.cs @@ -11,7 +11,7 @@ namespace XplorePlane.ViewModels { Name = parameter.Name; DisplayName = parameter.DisplayName; - _value = parameter.DefaultValue; + _value = parameter.Value; MinValue = parameter.MinValue; MaxValue = parameter.MaxValue; ParameterType = parameter.ValueType?.Name?.ToLower() switch diff --git a/XplorePlane/Views/ImageProcessingPanelView.xaml b/XplorePlane/Views/ImageProcessingPanelView.xaml index 7f70273..4948b1c 100644 --- a/XplorePlane/Views/ImageProcessingPanelView.xaml +++ b/XplorePlane/Views/ImageProcessingPanelView.xaml @@ -3,73 +3,135 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:controls="clr-namespace:ImageProcessing.Controls;assembly=ImageProcessing.Controls" mc:Ignorable="d" - d:DesignHeight="600" d:DesignWidth="800"> + d:DesignHeight="700" d:DesignWidth="1000"> - - - - + + + + - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - -