diff --git a/OnnxStack.StableDiffusion/Enums/DiffuserType.cs b/OnnxStack.StableDiffusion/Enums/DiffuserType.cs index dd664aab..f759bdc8 100644 --- a/OnnxStack.StableDiffusion/Enums/DiffuserType.cs +++ b/OnnxStack.StableDiffusion/Enums/DiffuserType.cs @@ -6,6 +6,6 @@ public enum DiffuserType ImageToImage = 1, ImageInpaint = 2, ImageInpaintLegacy = 3, - ImageToAnimation = 4, + ImageToAnimation = 4 } } diff --git a/OnnxStack.StableDiffusion/Services/StableDiffusionService.cs b/OnnxStack.StableDiffusion/Services/StableDiffusionService.cs index b5f90968..052f30b6 100644 --- a/OnnxStack.StableDiffusion/Services/StableDiffusionService.cs +++ b/OnnxStack.StableDiffusion/Services/StableDiffusionService.cs @@ -12,6 +12,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -217,6 +218,10 @@ private async Task> DiffuseAsync(IModelOptions modelOptions, if (diffuser is null) throw new Exception("Diffuser not found or is unsupported"); + var schedulerSupported = pipeline.PipelineType.GetSchedulerTypes().Contains(schedulerOptions.SchedulerType); + if (!schedulerSupported) + throw new Exception($"Scheduler '{schedulerOptions.SchedulerType}' is not compatible with the `{pipeline.PipelineType}` pipeline."); + return await diffuser.DiffuseAsync(modelOptions, promptOptions, schedulerOptions, progress, cancellationToken); } @@ -230,6 +235,10 @@ private IAsyncEnumerable DiffuseBatchAsync(IModelOptions modelOptio if (diffuser is null) throw new Exception("Diffuser not found or is unsupported"); + var schedulerSupported = pipeline.PipelineType.GetSchedulerTypes().Contains(schedulerOptions.SchedulerType); + if (!schedulerSupported) + throw new Exception($"Scheduler '{schedulerOptions.SchedulerType}' is not compatible with the `{pipeline.PipelineType}` pipeline."); + return diffuser.DiffuseBatchAsync(modelOptions, promptOptions, schedulerOptions, batchOptions, progress, cancellationToken); } } diff --git a/OnnxStack.UI/App.xaml b/OnnxStack.UI/App.xaml index 181eab26..038a9c28 100644 --- a/OnnxStack.UI/App.xaml +++ b/OnnxStack.UI/App.xaml @@ -106,8 +106,7 @@ - - + @@ -248,10 +247,10 @@ - @@ -1978,7 +1977,35 @@ + + + + diff --git a/OnnxStack.UI/Behaviors/SliderMouseWheelBehavior.cs b/OnnxStack.UI/Behaviors/SliderMouseWheelBehavior.cs index 1f8b3fc7..80ce3b39 100644 --- a/OnnxStack.UI/Behaviors/SliderMouseWheelBehavior.cs +++ b/OnnxStack.UI/Behaviors/SliderMouseWheelBehavior.cs @@ -1,4 +1,5 @@ using Microsoft.Xaml.Behaviors; +using Newtonsoft.Json.Linq; using System.Windows.Controls; using System.Windows.Input; @@ -16,11 +17,19 @@ private void AssociatedObject_PreviewMouseWheel(object sender, MouseWheelEventAr var slider = (Slider)sender; if (e.Delta > 0) { - slider.Value += slider.TickFrequency; + var newValue = slider.Value + slider.TickFrequency; + if (newValue > slider.Maximum) + return; + + slider.Value = newValue; } else { - slider.Value -= slider.TickFrequency; + var newValue = slider.Value - slider.TickFrequency; + if (newValue < slider.Minimum) + return; + + slider.Value = newValue; } } diff --git a/OnnxStack.UI/MainWindow.xaml b/OnnxStack.UI/MainWindow.xaml index c7fcd9bd..02e22227 100644 --- a/OnnxStack.UI/MainWindow.xaml +++ b/OnnxStack.UI/MainWindow.xaml @@ -18,7 +18,6 @@ TextOptions.TextFormattingMode="Ideal" TextOptions.TextRenderingMode="ClearType" TextOptions.TextHintingMode="Fixed" - UseLayoutRounding="True" SnapsToDevicePixels="True" Style="{StaticResource BaseWindow}"> @@ -51,7 +50,7 @@ - + @@ -59,14 +58,29 @@ - + - + + + + + + + + + + + + + + + + @@ -78,7 +92,7 @@ - + @@ -90,7 +104,7 @@ - + diff --git a/OnnxStack.UI/MainWindow.xaml.cs b/OnnxStack.UI/MainWindow.xaml.cs index 796ec21b..f3efd04f 100644 --- a/OnnxStack.UI/MainWindow.xaml.cs +++ b/OnnxStack.UI/MainWindow.xaml.cs @@ -37,7 +37,7 @@ public MainWindow(StableDiffusionConfig configuration, OnnxStackUIConfig uiSetti NavigateTextToImageCommand = new AsyncRelayCommand(NavigateTextToImage); NavigateImageToImageCommand = new AsyncRelayCommand(NavigateImageToImage); NavigateImageInpaintCommand = new AsyncRelayCommand(NavigateImageInpaint); - NavigateImageUpscaleCommand = new AsyncRelayCommand(NavigateImageUpscale); + NavigateImagePaintToImageCommand = new AsyncRelayCommand(NavigateImagePaintToImage); WindowCloseCommand = new AsyncRelayCommand(WindowClose); WindowRestoreCommand = new AsyncRelayCommand(WindowRestore); @@ -56,7 +56,7 @@ public MainWindow(StableDiffusionConfig configuration, OnnxStackUIConfig uiSetti public AsyncRelayCommand NavigateTextToImageCommand { get; } public AsyncRelayCommand NavigateImageToImageCommand { get; } public AsyncRelayCommand NavigateImageInpaintCommand { get; } - public AsyncRelayCommand NavigateImageUpscaleCommand { get; } + public AsyncRelayCommand NavigateImagePaintToImageCommand { get; } public OnnxStackUIConfig UISettings { @@ -86,31 +86,39 @@ public INavigatable SelectedTabItem private async Task NavigateTextToImage(ImageResult result) { - await NavigateToTab(DiffuserType.TextToImage, result); + await NavigateToTab(TabId.TextToImage, result); } private async Task NavigateImageToImage(ImageResult result) { - await NavigateToTab(DiffuserType.ImageToImage, result); + await NavigateToTab(TabId.ImageToImage, result); } private async Task NavigateImageInpaint(ImageResult result) { - await NavigateToTab(DiffuserType.ImageInpaint, result); + await NavigateToTab(TabId.ImageInpaint, result); } - private Task NavigateImageUpscale(ImageResult result) + private async Task NavigateImagePaintToImage(ImageResult result) { - return Task.CompletedTask; + await NavigateToTab(TabId.PaintToImage, result); } - private async Task NavigateToTab(DiffuserType diffuserType, ImageResult imageResult) + private async Task NavigateToTab(TabId tab, ImageResult imageResult) { - SelectedTabIndex = (int)diffuserType; + SelectedTabIndex = (int)tab; await SelectedTabItem.NavigateAsync(imageResult); } + private enum TabId + { + TextToImage = 0, + ImageToImage = 1, + ImageInpaint = 2, + PaintToImage = 3 + } + private ObservableCollection CreateModelOptions(List onnxModelSets) { var models = onnxModelSets diff --git a/OnnxStack.UI/Models/BatchOptionsModel.cs b/OnnxStack.UI/Models/BatchOptionsModel.cs index bd5019ec..22d3bfa3 100644 --- a/OnnxStack.UI/Models/BatchOptionsModel.cs +++ b/OnnxStack.UI/Models/BatchOptionsModel.cs @@ -15,6 +15,8 @@ public class BatchOptionsModel : INotifyPropertyChanged private int _stepsValue = 1; private int _batchValue; private int _batchsValue = 1; + private bool _disableHistory = true; + private bool _isRealtimeEnabled; public BatchOptionType BatchType { @@ -40,12 +42,6 @@ public float Increment set { _increment = value; NotifyPropertyChanged(); } } - public bool IsAutomationEnabled - { - get { return _isAutomationEnabled; } - set { _isAutomationEnabled = value; NotifyPropertyChanged(); } - } - public int StepValue { get { return _stepValue; } @@ -70,6 +66,35 @@ public int BatchsValue set { _batchsValue = value; NotifyPropertyChanged(); } } + public bool DisableHistory + { + get { return _disableHistory; } + set { _disableHistory = value; NotifyPropertyChanged(); } + } + + public bool IsAutomationEnabled + { + get { return _isAutomationEnabled; } + set + { + _isAutomationEnabled = value; + if (_isAutomationEnabled) + IsRealtimeEnabled = false; + NotifyPropertyChanged(); + } + } + + public bool IsRealtimeEnabled + { + get { return _isRealtimeEnabled; } + set + { + _isRealtimeEnabled = value; + if (_isRealtimeEnabled) + IsAutomationEnabled = false; + NotifyPropertyChanged(); + } + } #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; diff --git a/OnnxStack.UI/Models/OnnxStackUIConfig.cs b/OnnxStack.UI/Models/OnnxStackUIConfig.cs index 6fca48c4..e202b5c1 100644 --- a/OnnxStack.UI/Models/OnnxStackUIConfig.cs +++ b/OnnxStack.UI/Models/OnnxStackUIConfig.cs @@ -14,8 +14,8 @@ public class OnnxStackUIConfig : IConfigSection public bool ImageAutoSave { get; set; } public bool ImageAutoSaveBlueprint { get; set; } public string ImageAutoSaveDirectory { get; set; } - - + public int RealtimeRefreshRate { get; set; } = 100; + public bool RealtimeHistoryEnabled { get; set; } public int DefaultDeviceId { get; set; } public int DefaultInterOpNumThreads { get; set; } public int DefaultIntraOpNumThreads { get; set; } diff --git a/OnnxStack.UI/Models/PromptOptionsModel.cs b/OnnxStack.UI/Models/PromptOptionsModel.cs index 46ca68dd..31e8175a 100644 --- a/OnnxStack.UI/Models/PromptOptionsModel.cs +++ b/OnnxStack.UI/Models/PromptOptionsModel.cs @@ -10,6 +10,7 @@ public class PromptOptionsModel : INotifyPropertyChanged { private string _prompt; private string _negativePrompt; + private bool _hasChanged; [Required] [StringLength(512, MinimumLength = 1)] @@ -26,10 +27,20 @@ public string NegativePrompt set { _prompt = value; NotifyPropertyChanged(); } } + public bool HasChanged + { + get { return _hasChanged; } + set { _hasChanged = value; NotifyPropertyChanged(); } + } + + #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; public void NotifyPropertyChanged([CallerMemberName] string property = "") { + if (!property.Equals(nameof(HasChanged)) && !HasChanged) + HasChanged = true; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); } #endregion diff --git a/OnnxStack.UI/Models/SchedulerOptionsModel.cs b/OnnxStack.UI/Models/SchedulerOptionsModel.cs index 14fcb788..4cadc296 100644 --- a/OnnxStack.UI/Models/SchedulerOptionsModel.cs +++ b/OnnxStack.UI/Models/SchedulerOptionsModel.cs @@ -33,6 +33,7 @@ public class SchedulerOptionsModel : INotifyPropertyChanged private float _maximumBeta = 0.999f; private int _originalInferenceSteps = 100; private SchedulerType _schedulerType; + private bool _hasChanged; /// /// Gets or sets the height. @@ -210,11 +211,21 @@ public SchedulerType SchedulerType set { _schedulerType = value; NotifyPropertyChanged(); } } + public bool HasChanged + { + get { return _hasChanged; } + set { _hasChanged = value; NotifyPropertyChanged(); } + } + + #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; public void NotifyPropertyChanged([CallerMemberName] string property = "") { + if (!property.Equals(nameof(HasChanged)) && !HasChanged) + HasChanged = true; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); } #endregion diff --git a/OnnxStack.UI/OnnxStack.UI.csproj b/OnnxStack.UI/OnnxStack.UI.csproj index fffb3223..9acbefd4 100644 --- a/OnnxStack.UI/OnnxStack.UI.csproj +++ b/OnnxStack.UI/OnnxStack.UI.csproj @@ -40,6 +40,7 @@ + diff --git a/OnnxStack.UI/UserControls/ImageInputControl.xaml b/OnnxStack.UI/UserControls/ImageInputControl.xaml index ba9e54aa..3f072b06 100644 --- a/OnnxStack.UI/UserControls/ImageInputControl.xaml +++ b/OnnxStack.UI/UserControls/ImageInputControl.xaml @@ -28,7 +28,9 @@ - - diff --git a/OnnxStack.UI/UserControls/ImageInputControl.xaml.cs b/OnnxStack.UI/UserControls/ImageInputControl.xaml.cs index 6dc2089c..4687a059 100644 --- a/OnnxStack.UI/UserControls/ImageInputControl.xaml.cs +++ b/OnnxStack.UI/UserControls/ImageInputControl.xaml.cs @@ -5,6 +5,7 @@ using OnnxStack.UI.Services; using System; using System.ComponentModel; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -39,7 +40,6 @@ public ImageInputControl() LoadImageCommand = new AsyncRelayCommand(LoadImage); ClearImageCommand = new AsyncRelayCommand(ClearImage); MaskModeCommand = new AsyncRelayCommand(MaskMode); - SaveMaskCommand = new AsyncRelayCommand(SaveMask); CopyImageCommand = new AsyncRelayCommand(CopyImage); PasteImageCommand = new AsyncRelayCommand(PasteImage); InitializeComponent(); @@ -48,7 +48,6 @@ public ImageInputControl() public AsyncRelayCommand LoadImageCommand { get; } public AsyncRelayCommand ClearImageCommand { get; } public AsyncRelayCommand MaskModeCommand { get; } - public AsyncRelayCommand SaveMaskCommand { get; } public AsyncRelayCommand CopyImageCommand { get; } public AsyncRelayCommand PasteImageCommand { get; } public ImageInput Result @@ -58,7 +57,11 @@ public ImageInput Result } public static readonly DependencyProperty ResultProperty = - DependencyProperty.Register("Result", typeof(ImageInput), typeof(ImageInputControl)); + DependencyProperty.Register("Result", typeof(ImageInput), typeof(ImageInputControl), new PropertyMetadata((s, e) => + { + if (s is ImageInputControl control) + control.SaveMask(); + })); public ImageInput MaskResult { @@ -242,6 +245,9 @@ private void UpdateMaskAttributes() /// public BitmapSource CreateMaskImage() { + if (MaskCanvas.ActualWidth == 0) + return CreateEmptyMaskImage(); + // Create a RenderTargetBitmap to render the Canvas content. var renderBitmap = new RenderTargetBitmap((int)MaskCanvas.ActualWidth, (int)MaskCanvas.ActualHeight, 96, 96, PixelFormats.Pbgra32); @@ -256,7 +262,27 @@ public BitmapSource CreateMaskImage() return renderBitmap; } - private void ShowCropImageDialog(BitmapSource source = null, string sourceFile = null) + + public BitmapSource CreateEmptyMaskImage() + { + var wbm = new WriteableBitmap(SchedulerOptions.Width, SchedulerOptions.Height, 96, 96, PixelFormats.Bgra32, null); + BitmapImage bmImage = new BitmapImage(); + using (MemoryStream stream = new MemoryStream()) + { + PngBitmapEncoder encoder = new PngBitmapEncoder(); + encoder.Frames.Add(BitmapFrame.Create(wbm)); + encoder.Save(stream); + bmImage.BeginInit(); + bmImage.CacheOption = BitmapCacheOption.OnLoad; + bmImage.StreamSource = stream; + bmImage.EndInit(); + bmImage.Freeze(); + } + return bmImage; + } + + + private async void ShowCropImageDialog(BitmapSource source = null, string sourceFile = null) { try { @@ -269,13 +295,14 @@ private void ShowCropImageDialog(BitmapSource source = null, string sourceFile = loadImageDialog.Initialize(SchedulerOptions.Width, SchedulerOptions.Height, source); if (loadImageDialog.ShowDialog() == true) { - ClearImage(); + await ClearImage(); Result = new ImageInput { Image = loadImageDialog.GetImageResult(), FileName = loadImageDialog.ImageFile, }; HasResult = true; + await SaveMask(); } } @@ -333,6 +360,16 @@ private void MaskCanvas_MouseLeftButtonDown(object sender, System.Windows.Input. } + /// + /// Handles the MouseLeftButtonUp event of the MaskCanvas control. + /// + /// The source of the event. + /// The instance containing the event data. + private async void MaskCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) + { + await SaveMask(); + } + /// /// Called on key down. /// diff --git a/OnnxStack.UI/UserControls/ImageResultControl.xaml b/OnnxStack.UI/UserControls/ImageResultControl.xaml index 2f02c9d0..baf03877 100644 --- a/OnnxStack.UI/UserControls/ImageResultControl.xaml +++ b/OnnxStack.UI/UserControls/ImageResultControl.xaml @@ -65,10 +65,10 @@ - diff --git a/OnnxStack.UI/UserControls/PaintInputControl.xaml b/OnnxStack.UI/UserControls/PaintInputControl.xaml new file mode 100644 index 00000000..6b0ee68d --- /dev/null +++ b/OnnxStack.UI/UserControls/PaintInputControl.xaml @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OnnxStack.UI/UserControls/PaintInputControl.xaml.cs b/OnnxStack.UI/UserControls/PaintInputControl.xaml.cs new file mode 100644 index 00000000..db57adff --- /dev/null +++ b/OnnxStack.UI/UserControls/PaintInputControl.xaml.cs @@ -0,0 +1,508 @@ +using OnnxStack.Core; +using OnnxStack.UI.Commands; +using OnnxStack.UI.Dialogs; +using OnnxStack.UI.Models; +using OnnxStack.UI.Services; +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using Xceed.Wpf.Toolkit; + +namespace OnnxStack.UI.UserControls +{ + public partial class PaintInputControl : UserControl, INotifyPropertyChanged + { + private readonly IDialogService _dialogService; + + private int _drawingToolSize; + public DateTime _canvasLastUpdate; + private DrawingAttributes _drawingAttributes; + private Color _selectedColor = Colors.Black; + private Brush _backgroundBrush = new SolidColorBrush(Colors.White); + private InkCanvasEditingMode _canvasEditingMode = InkCanvasEditingMode.Ink; + private DrawingTool _canvasDrawingTool; + private ObservableCollection _recentColors = new ObservableCollection(); + + /// + /// Initializes a new instance of the class. + /// + public PaintInputControl() + { + if (!DesignerProperties.GetIsInDesignMode(this)) + _dialogService = App.GetService(); + + DrawingToolSize = 10; + LoadImageCommand = new AsyncRelayCommand(LoadImage); + ClearCanvasCommand = new AsyncRelayCommand(ClearCanvas); + FillCanvasCommand = new AsyncRelayCommand(FillCanvas); + CopyImageCommand = new AsyncRelayCommand(CopyImage); + PasteImageCommand = new AsyncRelayCommand(PasteImage); + SelectToolCommand = new AsyncRelayCommand(SelectDrawingTool); + InitializeComponent(); + SetRecentColors(); + } + + + + public AsyncRelayCommand LoadImageCommand { get; } + public AsyncRelayCommand ClearCanvasCommand { get; } + public AsyncRelayCommand FillCanvasCommand { get; } + public AsyncRelayCommand CopyImageCommand { get; } + public AsyncRelayCommand PasteImageCommand { get; } + public AsyncRelayCommand SelectToolCommand { get; } + + + public OnnxStackUIConfig UISettings + { + get { return (OnnxStackUIConfig)GetValue(UISettingsProperty); } + set { SetValue(UISettingsProperty, value); } + } + public static readonly DependencyProperty UISettingsProperty = + DependencyProperty.Register("UISettings", typeof(OnnxStackUIConfig), typeof(PaintInputControl), new PropertyMetadata(async (s, e) => + { + if (s is PaintInputControl control && e.NewValue is OnnxStackUIConfig image) + { + await Task.Delay(500); // TODO: Fix race condition + await control.SaveCanvas(); + } + })); + + + public ImageInput InputImage + { + get { return (ImageInput)GetValue(InputImageProperty); } + set { SetValue(InputImageProperty, value); } + } + public static readonly DependencyProperty InputImageProperty = + DependencyProperty.Register("InputImage", typeof(ImageInput), typeof(PaintInputControl), new PropertyMetadata(async (s, e) => + { + if (s is PaintInputControl control && e.NewValue is ImageInput image) + { + control.BackgroundBrush = new ImageBrush(image.Image); + await Task.Delay(500); // TODO: Fix race condition + await control.SaveCanvas(); + } + })); + + public ImageInput CanvasResult + { + get { return (ImageInput)GetValue(CanvasResultProperty); } + set { SetValue(CanvasResultProperty, value); } + } + public static readonly DependencyProperty CanvasResultProperty = + DependencyProperty.Register("CanvasResult", typeof(ImageInput), typeof(PaintInputControl)); + + + public SchedulerOptionsModel SchedulerOptions + { + get { return (SchedulerOptionsModel)GetValue(SchedulerOptionsProperty); } + set { SetValue(SchedulerOptionsProperty, value); } + } + public static readonly DependencyProperty SchedulerOptionsProperty = + DependencyProperty.Register("SchedulerOptions", typeof(SchedulerOptionsModel), typeof(PaintInputControl)); + + + public bool HasCanvasChanged + { + get { return (bool)GetValue(HasCanvasChangedProperty); } + set { SetValue(HasCanvasChangedProperty, value); } + } + public static readonly DependencyProperty HasCanvasChangedProperty = + DependencyProperty.Register("HasCanvasChanged", typeof(bool), typeof(PaintInputControl)); + + + public InkCanvasEditingMode CanvasEditingMode + { + get { return _canvasEditingMode; } + set { _canvasEditingMode = value; NotifyPropertyChanged(); } + } + + public DrawingAttributes BrushAttributes + { + get { return _drawingAttributes; } + set { _drawingAttributes = value; NotifyPropertyChanged(); } + } + + + public int DrawingToolSize + { + get { return _drawingToolSize; } + set + { + _drawingToolSize = value; + NotifyPropertyChanged(); + UpdateBrushAttributes(); + } + } + + public Color SelectedColor + { + get { return _selectedColor; } + set + { + _selectedColor = value; + NotifyPropertyChanged(); + UpdateBrushAttributes(); + } + } + + public Brush BackgroundBrush + { + get { return _backgroundBrush; } + set { _backgroundBrush = value; NotifyPropertyChanged(); } + } + + public DrawingTool CanvasDrawingTool + { + get { return _canvasDrawingTool; } + set { _canvasDrawingTool = value; NotifyPropertyChanged(); } + } + + public ObservableCollection RecentColors + { + get { return _recentColors; } + set { _recentColors = value; NotifyPropertyChanged(); } + } + + + + + + /// + /// Loads the image. + /// + /// + private Task LoadImage() + { + ShowCropImageDialog(); + return Task.CompletedTask; + } + + + /// + /// Clears the image. + /// + /// + private async Task ClearCanvas() + { + PaintCanvas.Strokes.Clear(); + BackgroundBrush = new SolidColorBrush(Colors.White); + CanvasResult = new ImageInput + { + Image = CreateCanvasImage(), + FileName = "Canvas Image", + }; + await Task.Delay(100); + await SaveCanvas(); + } + + + /// + /// Fills the canvas with the SelectedColor. + /// + /// + private async Task FillCanvas() + { + BackgroundBrush = new SolidColorBrush(SelectedColor); + await Task.Delay(100); + await SaveCanvas(); + } + + + /// + /// Saves the Canvas + /// + /// + private Task SaveCanvas() + { + CanvasResult = new ImageInput + { + Image = CreateCanvasImage(), + FileName = "Canvas Image", + }; + HasCanvasChanged = true; + return Task.CompletedTask; + } + + + /// + /// Updates the brush attributes. + /// + private void UpdateBrushAttributes() + { + BrushAttributes = new DrawingAttributes + { + Color = _selectedColor, + Height = _drawingToolSize, + Width = _drawingToolSize + }; + + if (CanvasDrawingTool == DrawingTool.Highlight) + BrushAttributes.Color = Color.FromArgb(128, BrushAttributes.Color.R, BrushAttributes.Color.G, BrushAttributes.Color.B); + + CanvasEditingMode = CanvasDrawingTool != DrawingTool.Eraser + ? InkCanvasEditingMode.Ink + : InkCanvasEditingMode.EraseByPoint; + } + + + /// + /// Selects the drawing tool. + /// + /// The selected tool. + /// + private Task SelectDrawingTool(DrawingTool selectedTool) + { + CanvasDrawingTool = selectedTool; + UpdateBrushAttributes(); + return Task.CompletedTask; + } + + + /// + /// Creates the canvas image. + /// + /// + public BitmapSource CreateCanvasImage() + { + if (PaintCanvas.ActualWidth == 0) + return CreateEmptyCanvasImage(); + + // Create a RenderTargetBitmap to render the Canvas content. + var renderBitmap = new RenderTargetBitmap((int)PaintCanvas.ActualWidth, (int)PaintCanvas.ActualHeight, 96, 96, PixelFormats.Pbgra32); + + // Make a drawing visual to render. + var visual = new DrawingVisual(); + using (DrawingContext context = visual.RenderOpen()) + { + VisualBrush brush = new VisualBrush(PaintCanvas); + context.DrawRectangle(brush, null, new Rect(new Point(0, 0), new Point(PaintCanvas.ActualWidth, PaintCanvas.ActualHeight))); + } + renderBitmap.Render(visual); + return renderBitmap; + } + + + /// + /// Creates the empty canvas image. + /// + /// + public BitmapSource CreateEmptyCanvasImage() + { + var wbm = new WriteableBitmap(SchedulerOptions.Width, SchedulerOptions.Height, 96, 96, PixelFormats.Bgra32, null); + BitmapImage bmImage = new BitmapImage(); + using (MemoryStream stream = new MemoryStream()) + { + PngBitmapEncoder encoder = new PngBitmapEncoder(); + encoder.Frames.Add(BitmapFrame.Create(wbm)); + encoder.Save(stream); + bmImage.BeginInit(); + bmImage.CacheOption = BitmapCacheOption.OnLoad; + bmImage.StreamSource = stream; + bmImage.EndInit(); + bmImage.Freeze(); + } + return bmImage; + } + + + /// + /// Shows the crop image dialog. + /// + /// The source. + /// The source file. + private async void ShowCropImageDialog(BitmapSource source = null, string sourceFile = null) + { + try + { + if (!string.IsNullOrEmpty(sourceFile)) + source = new BitmapImage(new Uri(sourceFile)); + } + catch { } + + var loadImageDialog = _dialogService.GetDialog(); + loadImageDialog.Initialize(SchedulerOptions.Width, SchedulerOptions.Height, source); + if (loadImageDialog.ShowDialog() == true) + { + BackgroundBrush = new ImageBrush(loadImageDialog.GetImageResult()); + await SaveCanvas(); + } + } + + + /// + /// Copies the image. + /// + /// + private Task CopyImage() + { + if (CanvasResult?.Image != null) + Clipboard.SetImage(CanvasResult.Image); + return Task.CompletedTask; + } + + + /// + /// Paste the image. + /// + /// + private Task PasteImage() + { + return HandleClipboardInput(); + } + + + /// + /// Handles the clipboard input. + /// + /// + private Task HandleClipboardInput() + { + if (Clipboard.ContainsImage()) + ShowCropImageDialog(Clipboard.GetImage()); + else if (Clipboard.ContainsFileDropList()) + { + var imageFile = Clipboard.GetFileDropList() + .OfType() + .FirstOrDefault(); + ShowCropImageDialog(null, imageFile); + } + return Task.CompletedTask; + } + + + /// + /// Sets the recent colors. + /// + private void SetRecentColors() + { + RecentColors.Add(new ColorItem(Colors.Red, "Red")); + RecentColors.Add(new ColorItem(Colors.Green, "Green")); + RecentColors.Add(new ColorItem(Colors.Blue, "Blue")); + RecentColors.Add(new ColorItem(Colors.Gray, "Gray")); + RecentColors.Add(new ColorItem(Colors.Yellow, "Yellow")); + + RecentColors.Add(new ColorItem(Colors.Orange, "Orange")); + RecentColors.Add(new ColorItem(Colors.Brown, "Brown")); + RecentColors.Add(new ColorItem(Colors.Fuchsia, "Fuchsia")); + RecentColors.Add(new ColorItem(Colors.Black, "Black")); + RecentColors.Add(new ColorItem(Colors.White, "White")); + } + + + /// + /// Handles the MouseLeftButtonDown event of the PaintCanvas control. + /// + /// The source of the event. + /// The instance containing the event data. + private async void PaintCanvas_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + await SaveCanvas(); + } + + + /// + /// Handles the MouseLeftButtonUp event of the PaintCanvas control. + /// + /// The source of the event. + /// The instance containing the event data. + private async void PaintCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) + { + await SaveCanvas(); + } + + /// + /// Called on key down. + /// + /// The sender. + /// The instance containing the event data. + private async void OnPreviewKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.V && Keyboard.Modifiers == ModifierKeys.Control) + { + await HandleClipboardInput(); + e.Handled = true; + } + else if (e.Key == Key.C && Keyboard.Modifiers == ModifierKeys.Control) + { + await CopyImage(); + e.Handled = true; + } + } + + + /// + /// Called when mouse enters. + /// + /// The sender. + /// The instance containing the event data. + private void OnMouseEnter(object sender, MouseEventArgs e) + { + Focus(); + } + + + /// + /// Handles the PreviewMouseMove event of the PaintCanvas control. + /// + /// The source of the event. + /// The instance containing the event data. + private async void PaintCanvas_PreviewMouseMove(object sender, MouseEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed) + { + if (DateTime.Now > _canvasLastUpdate) + { + _canvasLastUpdate = DateTime.Now.AddMilliseconds(UISettings.RealtimeRefreshRate); + await SaveCanvas(); + } + } + } + + + /// + /// Called when [preview drop]. + /// + /// The sender. + /// The instance containing the event data. + private void OnPreviewDrop(object sender, DragEventArgs e) + { + var fileNames = (string[])e.Data.GetData(DataFormats.FileDrop); + if (!fileNames.IsNullOrEmpty()) + ShowCropImageDialog(null, fileNames.FirstOrDefault()); + } + + + #region INotifyPropertyChanged + public event PropertyChangedEventHandler PropertyChanged; + public void NotifyPropertyChanged([CallerMemberName] string property = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); + } + #endregion + + private void OnMouseWheel(object sender, MouseWheelEventArgs e) + { + DrawingToolSize = e.Delta > 0 + ? DrawingToolSize = Math.Min(40, DrawingToolSize + 1) + : DrawingToolSize = Math.Max(1, DrawingToolSize - 1); + } + } + + public enum DrawingTool + { + Brush = 0, + Highlight = 1, + Eraser = 2, + } +} diff --git a/OnnxStack.UI/UserControls/SchedulerControl.xaml b/OnnxStack.UI/UserControls/SchedulerControl.xaml index 0d05fd5d..4e3927ee 100644 --- a/OnnxStack.UI/UserControls/SchedulerControl.xaml +++ b/OnnxStack.UI/UserControls/SchedulerControl.xaml @@ -26,31 +26,119 @@ - - - - - - - - - - - - - - - + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -75,7 +163,7 @@ - + @@ -164,12 +252,9 @@ + - - - + @@ -184,8 +269,8 @@ - - + + @@ -219,7 +304,7 @@ - + @@ -235,7 +320,7 @@ - + @@ -270,14 +355,14 @@ - + - + @@ -397,17 +482,29 @@ - - - - - + + + + + + + + + + + - - - + + diff --git a/OnnxStack.UI/UserControls/SchedulerControl.xaml.cs b/OnnxStack.UI/UserControls/SchedulerControl.xaml.cs index 25b71ef5..38f9d29e 100644 --- a/OnnxStack.UI/UserControls/SchedulerControl.xaml.cs +++ b/OnnxStack.UI/UserControls/SchedulerControl.xaml.cs @@ -1,5 +1,6 @@ using Models; using OnnxStack.Core; +using OnnxStack.StableDiffusion; using OnnxStack.StableDiffusion.Config; using OnnxStack.StableDiffusion.Enums; using OnnxStack.UI.Commands; @@ -27,12 +28,14 @@ public partial class SchedulerControl : UserControl, INotifyPropertyChanged public SchedulerControl() { ValidSizes = new ObservableCollection(Constants.ValidSizes); + NewSeedCommand = new RelayCommand(NewSeed); RandomSeedCommand = new RelayCommand(RandomSeed); ResetParametersCommand = new RelayCommand(ResetParameters); InitializeComponent(); } public ICommand ResetParametersCommand { get; } + public ICommand NewSeedCommand { get; } public ICommand RandomSeedCommand { get; } public ObservableCollection ValidSizes { get; } @@ -91,6 +94,23 @@ public BatchOptionsModel BatchOptions DependencyProperty.Register("BatchOptions", typeof(BatchOptionsModel), typeof(SchedulerControl)); + public bool IsAutomationEnabled + { + get { return (bool)GetValue(IsAutomationEnabledProperty); } + set { SetValue(IsAutomationEnabledProperty, value); } + } + public static readonly DependencyProperty IsAutomationEnabledProperty = + DependencyProperty.Register("IsAutomationEnabled", typeof(bool), typeof(SchedulerControl)); + + + public bool IsGenerating + { + get { return (bool)GetValue(IsGeneratingProperty); } + set { SetValue(IsGeneratingProperty, value); } + } + public static readonly DependencyProperty IsGeneratingProperty = + DependencyProperty.Register("IsGenerating", typeof(bool), typeof(SchedulerControl)); + @@ -149,14 +169,22 @@ private void OnModelChanged(ModelOptionsModel model) /// private void ResetParameters() { - SchedulerOptions = new SchedulerOptionsModel(); + SchedulerOptions = new SchedulerOptionsModel + { + SchedulerType = SelectedModel.ModelOptions.PipelineType.GetSchedulerTypes().First() + }; } - private void RandomSeed() + private void NewSeed() { SchedulerOptions.Seed = Random.Shared.Next(); } + private void RandomSeed() + { + SchedulerOptions.Seed = 0; + } + #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; public void NotifyPropertyChanged([CallerMemberName] string property = "") diff --git a/OnnxStack.UI/Utils.cs b/OnnxStack.UI/Utils.cs index 832f930b..2f9a4999 100644 --- a/OnnxStack.UI/Utils.cs +++ b/OnnxStack.UI/Utils.cs @@ -3,9 +3,12 @@ using OnnxStack.StableDiffusion.Enums; using OnnxStack.UI.Models; using System; +using System.Diagnostics; using System.IO; +using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; +using System.Threading; using System.Threading.Tasks; using System.Windows.Media.Imaging; using System.Windows.Threading; @@ -193,7 +196,13 @@ public static BatchOptions ToBatchOptions(this BatchOptionsModel batchOptionsMod }; } - + internal static async Task RefreshDelay(long startTime, int refreshRate, CancellationToken cancellationToken) + { + var endTime = Stopwatch.GetTimestamp(); + var elapsedMilliseconds = (endTime - startTime) * 1000.0 / Stopwatch.Frequency; + int adjustedDelay = Math.Max(0, refreshRate - (int)elapsedMilliseconds); + await Task.Delay(adjustedDelay, cancellationToken).ConfigureAwait(false); + } public static void LogToWindow(string message) { diff --git a/OnnxStack.UI/Views/ImageInpaint.xaml b/OnnxStack.UI/Views/ImageInpaintView.xaml similarity index 77% rename from OnnxStack.UI/Views/ImageInpaint.xaml rename to OnnxStack.UI/Views/ImageInpaintView.xaml index 025e388b..1dce3887 100644 --- a/OnnxStack.UI/Views/ImageInpaint.xaml +++ b/OnnxStack.UI/Views/ImageInpaintView.xaml @@ -1,4 +1,4 @@ - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OnnxStack.UI/Views/PaintToImageView.xaml.cs b/OnnxStack.UI/Views/PaintToImageView.xaml.cs new file mode 100644 index 00000000..cbb508ad --- /dev/null +++ b/OnnxStack.UI/Views/PaintToImageView.xaml.cs @@ -0,0 +1,496 @@ +using Microsoft.Extensions.Logging; +using Models; +using OnnxStack.StableDiffusion.Common; +using OnnxStack.StableDiffusion.Config; +using OnnxStack.StableDiffusion.Enums; +using OnnxStack.StableDiffusion.Helpers; +using OnnxStack.UI.Commands; +using OnnxStack.UI.Models; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; + +namespace OnnxStack.UI.Views +{ + /// + /// Interaction logic for PaintToImageView.xaml + /// + public partial class PaintToImageView : UserControl, INavigatable, INotifyPropertyChanged + { + private readonly ILogger _logger; + private readonly IStableDiffusionService _stableDiffusionService; + + private bool _hasResult; + private int _progressMax; + private int _progressValue; + private bool _isGenerating; + private int _selectedTabIndex; + private bool _hasInputResult; + private bool _isControlsEnabled; + private ImageInput _inputImage; + private ImageInput _canvasImage; + private ImageResult _resultImage; + private ModelOptionsModel _selectedModel; + private PromptOptionsModel _promptOptionsModel; + private SchedulerOptionsModel _schedulerOptions; + private BatchOptionsModel _batchOptions; + private CancellationTokenSource _cancelationTokenSource; + + + /// + /// Initializes a new instance of the class. + /// + public PaintToImageView() + { + if (!DesignerProperties.GetIsInDesignMode(this)) + { + _logger = App.GetService>(); + _stableDiffusionService = App.GetService(); + } + + SupportedDiffusers = new() { DiffuserType.ImageToImage }; + CancelCommand = new AsyncRelayCommand(Cancel, CanExecuteCancel); + GenerateCommand = new AsyncRelayCommand(Generate, CanExecuteGenerate); + ClearHistoryCommand = new AsyncRelayCommand(ClearHistory, CanExecuteClearHistory); + PromptOptions = new PromptOptionsModel(); + SchedulerOptions = new SchedulerOptionsModel { SchedulerType = SchedulerType.DDPM }; + BatchOptions = new BatchOptionsModel(); + ImageResults = new ObservableCollection(); + ProgressMax = SchedulerOptions.InferenceSteps; + IsControlsEnabled = true; + HasCanvasChanged = true; + InitializeComponent(); + } + + public OnnxStackUIConfig UISettings + { + get { return (OnnxStackUIConfig)GetValue(UISettingsProperty); } + set { SetValue(UISettingsProperty, value); } + } + public static readonly DependencyProperty UISettingsProperty = + DependencyProperty.Register("UISettings", typeof(OnnxStackUIConfig), typeof(PaintToImageView)); + + public List SupportedDiffusers { get; } + public AsyncRelayCommand CancelCommand { get; } + public AsyncRelayCommand GenerateCommand { get; } + public AsyncRelayCommand ClearHistoryCommand { get; set; } + public ObservableCollection ImageResults { get; } + + public ModelOptionsModel SelectedModel + { + get { return _selectedModel; } + set { _selectedModel = value; NotifyPropertyChanged(); } + } + + public PromptOptionsModel PromptOptions + { + get { return _promptOptionsModel; } + set { _promptOptionsModel = value; NotifyPropertyChanged(); } + } + + public SchedulerOptionsModel SchedulerOptions + { + get { return _schedulerOptions; } + set { _schedulerOptions = value; NotifyPropertyChanged(); } + } + + public BatchOptionsModel BatchOptions + { + get { return _batchOptions; } + set { _batchOptions = value; NotifyPropertyChanged(); } + } + + public ImageResult ResultImage + { + get { return _resultImage; } + set { _resultImage = value; NotifyPropertyChanged(); } + } + + public ImageInput InputImage + { + get { return _inputImage; } + set { _inputImage = value; NotifyPropertyChanged(); } + } + + + + public ImageInput CanvasImage + { + get { return _canvasImage; } + set { _canvasImage = value; NotifyPropertyChanged(); } + } + + + public int ProgressValue + { + get { return _progressValue; } + set { _progressValue = value; NotifyPropertyChanged(); } + } + + public int ProgressMax + { + get { return _progressMax; } + set { _progressMax = value; NotifyPropertyChanged(); } + } + + public bool IsGenerating + { + get { return _isGenerating; } + set { _isGenerating = value; NotifyPropertyChanged(); } + } + + public bool HasResult + { + get { return _hasResult; } + set { _hasResult = value; NotifyPropertyChanged(); } + } + + public bool HasCanvasChanged + { + get { return _hasInputResult; } + set { _hasInputResult = value; NotifyPropertyChanged(); } + } + + public int SelectedTabIndex + { + get { return _selectedTabIndex; } + set { _selectedTabIndex = value; NotifyPropertyChanged(); } + } + + public bool IsControlsEnabled + { + get { return _isControlsEnabled; } + set { _isControlsEnabled = value; NotifyPropertyChanged(); } + } + + + /// + /// Called on Navigate + /// + /// The image result. + /// + public Task NavigateAsync(ImageResult imageResult) + { + Reset(); + HasResult = false; + ResultImage = null; + HasCanvasChanged = true; + if (imageResult.Model.ModelOptions.Diffusers.Contains(DiffuserType.ImageToImage)) + { + SelectedModel = imageResult.Model; + } + InputImage = new ImageInput + { + Image = imageResult.Image, + FileName = "Generated Image" + }; + PromptOptions = new PromptOptionsModel + { + Prompt = imageResult.Prompt, + NegativePrompt = imageResult.NegativePrompt + }; + SchedulerOptions = imageResult.SchedulerOptions.ToSchedulerOptionsModel(); + SelectedTabIndex = 0; + return Task.CompletedTask; + } + + + /// + /// Generates this image result. + /// + private async Task Generate() + { + HasResult = false; + IsGenerating = true; + IsControlsEnabled = false; + ResultImage = null; + var promptOptions = GetPromptOptions(PromptOptions, CanvasImage); + var batchOptions = BatchOptions.ToBatchOptions(); + var schedulerOptions = SchedulerOptions.ToSchedulerOptions(); + + try + { + await foreach (var resultImage in ExecuteStableDiffusion(_selectedModel.ModelOptions, promptOptions, schedulerOptions, batchOptions)) + { + if (resultImage != null) + { + ResultImage = resultImage; + HasResult = true; + if (BatchOptions.IsAutomationEnabled && BatchOptions.DisableHistory) + continue; + if (BatchOptions.IsRealtimeEnabled && !UISettings.RealtimeHistoryEnabled) + continue; + + ImageResults.Add(resultImage); + } + } + } + catch (OperationCanceledException) + { + _logger.LogInformation($"Generate was canceled."); + } + catch (Exception ex) + { + _logger.LogError($"Error during Generate\n{ex}"); + } + + Reset(); + } + + + /// + /// Determines whether this instance can execute Generate. + /// + /// + /// true if this instance can execute Generate; otherwise, false. + /// + private bool CanExecuteGenerate() + { + return !IsGenerating + // && !string.IsNullOrEmpty(PromptOptions.Prompt) + && HasCanvasChanged; + } + + + /// + /// Cancels this generation. + /// + /// + private Task Cancel() + { + _cancelationTokenSource?.Cancel(); + HasCanvasChanged = true; + return Task.CompletedTask; + } + + + /// + /// Determines whether this instance can execute Cancel. + /// + /// + /// true if this instance can execute Cancel; otherwise, false. + /// + private bool CanExecuteCancel() + { + return IsGenerating; + } + + + /// + /// Clears the history. + /// + /// + private Task ClearHistory() + { + ImageResults.Clear(); + return Task.CompletedTask; + } + + + /// + /// Determines whether this instance can execute ClearHistory. + /// + /// + /// true if this instance can execute ClearHistory; otherwise, false. + /// + private bool CanExecuteClearHistory() + { + return ImageResults.Count > 0; + } + + + /// + /// Resets this instance. + /// + private void Reset() + { + IsGenerating = false; + IsControlsEnabled = true; + ProgressValue = 0; + } + + + /// + /// Executes the stable diffusion process. + /// + /// The model options. + /// The prompt options. + /// The scheduler options. + /// The batch options. + /// + private async IAsyncEnumerable ExecuteStableDiffusion(IModelOptions modelOptions, PromptOptions promptOptions, SchedulerOptions schedulerOptions, BatchOptions batchOptions) + { + _cancelationTokenSource = new CancellationTokenSource(); + + if (!BatchOptions.IsRealtimeEnabled) + { + if (!BatchOptions.IsAutomationEnabled) + { + var timestamp = Stopwatch.GetTimestamp(); + var result = await _stableDiffusionService.GenerateAsBytesAsync(modelOptions, promptOptions, schedulerOptions, ProgressCallback(), _cancelationTokenSource.Token); + yield return await GenerateResultAsync(result, promptOptions, schedulerOptions, timestamp); + } + else + { + if (!BatchOptions.IsRealtimeEnabled) + { + var timestamp = Stopwatch.GetTimestamp(); + await foreach (var batchResult in _stableDiffusionService.GenerateBatchAsync(modelOptions, promptOptions, schedulerOptions, batchOptions, ProgressBatchCallback(), _cancelationTokenSource.Token)) + { + yield return await GenerateResultAsync(batchResult.ImageResult.ToImageBytes(), promptOptions, batchResult.SchedulerOptions, timestamp); + timestamp = Stopwatch.GetTimestamp(); + } + } + } + } + else + { + // Realtime Diffusion + IsControlsEnabled = true; + SchedulerOptions.Seed = SchedulerOptions.Seed == 0 ? Random.Shared.Next() : SchedulerOptions.Seed; + while (!_cancelationTokenSource.IsCancellationRequested) + { + var refreshTimestamp = Stopwatch.GetTimestamp(); + if (SchedulerOptions.HasChanged || PromptOptions.HasChanged || HasCanvasChanged) + { + HasCanvasChanged = false; + PromptOptions.HasChanged = false; + SchedulerOptions.HasChanged = false; + var realtimePromptOptions = GetPromptOptions(PromptOptions, CanvasImage); + var realtimeSchedulerOptions = SchedulerOptions.ToSchedulerOptions(); + + var timestamp = Stopwatch.GetTimestamp(); + var result = await _stableDiffusionService.GenerateAsBytesAsync(modelOptions, realtimePromptOptions, realtimeSchedulerOptions, RealtimeProgressCallback(), _cancelationTokenSource.Token); + yield return await GenerateResultAsync(result, promptOptions, schedulerOptions, timestamp); + } + await Utils.RefreshDelay(refreshTimestamp, UISettings.RealtimeRefreshRate, _cancelationTokenSource.Token); + } + } + } + + + private PromptOptions GetPromptOptions(PromptOptionsModel promptOptionsModel, ImageInput imageInput) + { + return new PromptOptions + { + Prompt = promptOptionsModel.Prompt, + NegativePrompt = promptOptionsModel.NegativePrompt, + DiffuserType = DiffuserType.ImageToImage, + InputImage = new StableDiffusion.Models.InputImage + { + ImageBytes = imageInput.Image?.GetImageBytes() + } + }; + } + + + /// + /// Generates the result. + /// + /// The image bytes. + /// The prompt options. + /// The scheduler options. + /// The timestamp. + /// + private async Task GenerateResultAsync(byte[] imageBytes, PromptOptions promptOptions, SchedulerOptions schedulerOptions, long timestamp) + { + var image = Utils.CreateBitmap(imageBytes); + + var imageResult = new ImageResult + { + Image = image, + Model = _selectedModel, + Prompt = promptOptions.Prompt, + NegativePrompt = promptOptions.NegativePrompt, + PipelineType = _selectedModel.ModelOptions.PipelineType, + DiffuserType = promptOptions.DiffuserType, + SchedulerType = schedulerOptions.SchedulerType, + SchedulerOptions = schedulerOptions, + Elapsed = Stopwatch.GetElapsedTime(timestamp).TotalSeconds + }; + + if (UISettings.ImageAutoSave) + await imageResult.AutoSaveAsync(Path.Combine(UISettings.ImageAutoSaveDirectory, "PaintToImage"), UISettings.ImageAutoSaveBlueprint); + return imageResult; + } + + + /// + /// StableDiffusion progress callback. + /// + /// + private Action ProgressCallback() + { + return (value, maximum) => + { + App.UIInvoke(() => + { + if (_cancelationTokenSource.IsCancellationRequested) + return; + + if (ProgressMax != maximum) + ProgressMax = maximum; + + ProgressValue = value; + }); + }; + } + + private Action ProgressBatchCallback() + { + return (batchIndex, batchCount, step, steps) => + { + App.UIInvoke(() => + { + if (_cancelationTokenSource.IsCancellationRequested) + return; + + if (BatchOptions.BatchsValue != batchCount) + BatchOptions.BatchsValue = batchCount; + if (BatchOptions.BatchValue != batchIndex) + BatchOptions.BatchValue = batchIndex; + if (BatchOptions.StepValue != step) + BatchOptions.StepValue = step; + if (BatchOptions.StepsValue != steps) + BatchOptions.StepsValue = steps; + }); + }; + } + + private Action RealtimeProgressCallback() + { + return (value, maximum) => + { + App.UIInvoke(() => + { + if (_cancelationTokenSource.IsCancellationRequested) + return; + + if (BatchOptions.StepValue != value) + BatchOptions.StepValue = value; + if (BatchOptions.StepsValue != maximum) + BatchOptions.StepsValue = maximum; + }); + }; + } + + + #region INotifyPropertyChanged + public event PropertyChangedEventHandler PropertyChanged; + public void NotifyPropertyChanged([CallerMemberName] string property = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); + } + + #endregion + } + +} \ No newline at end of file diff --git a/OnnxStack.UI/Views/Settings.xaml b/OnnxStack.UI/Views/SettingsView.xaml similarity index 88% rename from OnnxStack.UI/Views/Settings.xaml rename to OnnxStack.UI/Views/SettingsView.xaml index 72db5c1b..4f0122b8 100644 --- a/OnnxStack.UI/Views/Settings.xaml +++ b/OnnxStack.UI/Views/SettingsView.xaml @@ -1,4 +1,4 @@ - + + + + + + + + + @@ -50,7 +59,7 @@ - + diff --git a/OnnxStack.UI/Views/Settings.xaml.cs b/OnnxStack.UI/Views/SettingsView.xaml.cs similarity index 83% rename from OnnxStack.UI/Views/Settings.xaml.cs rename to OnnxStack.UI/Views/SettingsView.xaml.cs index 18347682..74a79be4 100644 --- a/OnnxStack.UI/Views/Settings.xaml.cs +++ b/OnnxStack.UI/Views/SettingsView.xaml.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.Logging; using OnnxStack.Core.Config; -using OnnxStack.StableDiffusion.Config; using OnnxStack.UI.Commands; using OnnxStack.UI.Models; using System; @@ -13,19 +12,19 @@ namespace OnnxStack.UI.Views { /// - /// Interaction logic for Settings.xaml + /// Interaction logic for SettingsView.xaml /// - public partial class Settings : UserControl, INavigatable, INotifyPropertyChanged + public partial class SettingsView : UserControl, INavigatable, INotifyPropertyChanged { - private readonly ILogger _logger; + private readonly ILogger _logger; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Settings() + public SettingsView() { if (!DesignerProperties.GetIsInDesignMode(this)) - _logger = App.GetService>(); + _logger = App.GetService>(); SaveCommand = new AsyncRelayCommand(Save); InitializeComponent(); @@ -39,7 +38,7 @@ public OnnxStackUIConfig UISettings set { SetValue(UISettingsProperty, value); } } public static readonly DependencyProperty UISettingsProperty = - DependencyProperty.Register("UISettings", typeof(OnnxStackUIConfig), typeof(Settings)); + DependencyProperty.Register("UISettings", typeof(OnnxStackUIConfig), typeof(SettingsView)); diff --git a/OnnxStack.UI/Views/TextToImage.xaml b/OnnxStack.UI/Views/TextToImageView.xaml similarity index 72% rename from OnnxStack.UI/Views/TextToImage.xaml rename to OnnxStack.UI/Views/TextToImageView.xaml index f9d79c6c..7b248620 100644 --- a/OnnxStack.UI/Views/TextToImage.xaml +++ b/OnnxStack.UI/Views/TextToImageView.xaml @@ -16,27 +16,28 @@ -