Skip to content
This repository was archived by the owner on Nov 27, 2024. It is now read-only.

Commit 61b13a3

Browse files
committed
Add support for Mask drawing
1 parent eb3e381 commit 61b13a3

File tree

4 files changed

+207
-11
lines changed

4 files changed

+207
-11
lines changed

OnnxStack.UI/Models/SchedulerOptionsModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class SchedulerOptionsModel : INotifyPropertyChanged
1313
private int _seed;
1414
private int _inferenceSteps = 30;
1515
private float _guidanceScale = 7.5f;
16-
private float _strength = 0.6f;
16+
private float _strength = 0.7f;
1717
private float _initialNoiseLevel = 0f;
1818
private int _trainTimesteps = 1000;
1919
private float _betaStart = 0.00085f;

OnnxStack.UI/UserControls/ImageInputControl.xaml

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
44
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
55
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6+
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
67
xmlns:local="clr-namespace:OnnxStack.UI.UserControls"
78
xmlns:userControls="clr-namespace:OnnxStack.UI.UserControls"
89
xmlns:behaviors="clr-namespace:OnnxStack.UI.Behaviors"
@@ -17,27 +18,67 @@
1718

1819
<Border DataContext="{Binding ElementName=UI}" BorderBrush="LightGray" BorderThickness="2" >
1920
<StackPanel Margin="2" >
20-
<Image Source="{Binding Result.Image, FallbackValue={StaticResource PlaceholderImage}, TargetNullValue={StaticResource PlaceholderImage}}" Width="{Binding SchedulerOptions.Width}" Height="{Binding SchedulerOptions.Height}" MinHeight="512" MinWidth="512"/>
21+
<Canvas MinHeight="512" MinWidth="512">
22+
<Image Source="{Binding Result.Image, FallbackValue={StaticResource PlaceholderImage}, TargetNullValue={StaticResource PlaceholderImage}}" Width="{Binding SchedulerOptions.Width}" Height="{Binding SchedulerOptions.Height}" MinHeight="512" MinWidth="512"/>
23+
<InkCanvas x:Name="MaskCanvas" Background="Transparent" ForceCursor="True" Cursor="Pen" MinHeight="512" MinWidth="512"
24+
IsEnabled="{Binding HasResult}"
25+
EditingMode="{Binding MaskEditingMode}"
26+
Width="{Binding SchedulerOptions.Width}"
27+
Height="{Binding SchedulerOptions.Height}"
28+
DefaultDrawingAttributes="{Binding MaskAttributes}"
29+
Visibility="{Binding IsMaskEnabled, Converter={StaticResource BooleanToVisibilityConverter}}"/>
30+
</Canvas>
31+
2132
<UniformGrid Columns="1" Height="30" Visibility="{Binding HasResult, Converter={StaticResource BooleanToHiddenConverter}}">
2233
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,3">
2334
<TextBlock Text="Image File:" Margin="5,0,0,0" FontSize="10" Opacity=".6" VerticalAlignment="Center" FontStyle="Italic"/>
2435
<TextBlock Text="{Binding Result.FileName}" Margin="5,0,0,0" VerticalAlignment="Center"/>
2536
</StackPanel>
26-
<!--<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,3">
27-
<TextBlock Text="Size:" Margin="5,0,0,0" FontSize="10" Opacity=".6" VerticalAlignment="Center" FontStyle="Italic"/>
28-
<TextBlock Text="{Binding Result.FileSize}" Margin="5,0,0,0"/>
29-
</StackPanel>-->
3037
</UniformGrid>
3138
<DockPanel Height="30">
32-
<Button DockPanel.Dock="Right" Command="{Binding ClearImageCommand}" BorderThickness="0,1,1,1" Width="50">
39+
<Button DockPanel.Dock="Right" Command="{Binding ClearImageCommand}" BorderThickness="0,1,1,1" Width="50" IsEnabled="{Binding HasResult}">
3340
<userControls:FontAwesome Icon="&#xf2ed;" IconStyle="Light" />
3441
</Button>
35-
<Button Command="{Binding LoadImageCommand}" ToolTip="Load Image File" >
42+
<Button DockPanel.Dock="Left" Command="{Binding LoadImageCommand}" ToolTip="Load Image File" Width="150" Visibility="{Binding IsMaskEnabled, Converter={StaticResource BooleanToVisibilityConverter}}">
3643
<StackPanel Orientation="Horizontal">
3744
<userControls:FontAwesome Icon="&#xf1c5;" IconStyle="Light" Size="13" />
3845
<TextBlock Text="Load Image" Margin="5,0,0,0"/>
3946
</StackPanel>
4047
</Button>
48+
49+
<DockPanel >
50+
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" IsEnabled="{Binding HasResult}" Visibility="{Binding IsMaskEnabled, Converter={StaticResource BooleanToVisibilityConverter}}">
51+
<Button DockPanel.Dock="Right" Command="{Binding SaveMaskCommand}" Width="50" BorderThickness="1,1,0,1">
52+
<userControls:FontAwesome Icon="&#xf0c7;" IconStyle="Light" />
53+
</Button>
54+
<Button DockPanel.Dock="Right" Command="{Binding MaskModeCommand}" Width="50" Visibility="{Binding IsMaskEraserEnabled, Converter={StaticResource BooleanToVisibilityConverter}}">
55+
<userControls:FontAwesome Icon="&#xf304;" IconStyle="Light" />
56+
</Button>
57+
<Button DockPanel.Dock="Right" Command="{Binding MaskModeCommand}" Width="50" Visibility="{Binding IsMaskEraserEnabled, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
58+
<userControls:FontAwesome Icon="&#xf12d;" IconStyle="Light" />
59+
</Button>
60+
</StackPanel>
61+
62+
<Button DockPanel.Dock="Top" Command="{Binding LoadImageCommand}" Visibility="{Binding IsMaskEnabled, Converter={StaticResource InverseBooleanToVisibilityConverter}}" ToolTip="Load Image File" Height="30" >
63+
<StackPanel Orientation="Horizontal">
64+
<userControls:FontAwesome Icon="&#xf1c5;" IconStyle="Light" Size="13" />
65+
<TextBlock Text="Load Image" Margin="5,0,0,0"/>
66+
</StackPanel>
67+
</Button>
68+
69+
<StackPanel Margin="0,0,5,0" Visibility="{Binding IsMaskEnabled, Converter={StaticResource BooleanToVisibilityConverter}}">
70+
<UniformGrid Columns="2" >
71+
<TextBlock Text="Pen Size" VerticalAlignment="Bottom" FontSize="8" Margin="5,-4,6,0" Opacity=".6" FontStyle="Italic" />
72+
<TextBlock Text="{Binding ElementName=SliderMaskDrawSize, Path=Value, StringFormat={}{0}}" VerticalAlignment="Bottom" FontSize="8" Margin="0,-4,6,0" FontWeight="Medium" HorizontalAlignment="Right" />
73+
</UniformGrid>
74+
<Slider Name="SliderMaskDrawSize" Value="{Binding MaskDrawSize}" IsEnabled="{Binding HasResult}" Minimum="2" Maximum="40" TickFrequency="1" IsSnapToTickEnabled="True" SmallChange="1" LargeChange="1" >
75+
<i:Interaction.Behaviors>
76+
<behaviors:SliderMouseWheelBehavior />
77+
</i:Interaction.Behaviors>
78+
</Slider>
79+
</StackPanel>
80+
</DockPanel>
81+
4182
</DockPanel>
4283
</StackPanel>
4384
</Border>

OnnxStack.UI/UserControls/ImageInputControl.xaml.cs

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,40 @@
77
using System.Threading.Tasks;
88
using System.Windows;
99
using System.Windows.Controls;
10+
using System.Windows.Ink;
11+
using System.Windows.Media;
12+
using System.Windows.Media.Imaging;
1013

1114
namespace OnnxStack.UI.UserControls
1215
{
1316
public partial class ImageInputControl : UserControl, INotifyPropertyChanged
1417
{
1518
private readonly IDialogService _dialogService;
1619

17-
/// <summary>Initializes a new instance of the <see cref="ImageInputControl" /> class.</summary>
20+
private int _maskDrawSize = 20;
21+
private bool _isMaskEraserEnabled;
22+
private DrawingAttributes _maskAttributes;
23+
private InkCanvasEditingMode _maskEditingMode = InkCanvasEditingMode.Ink;
24+
25+
/// <summary>
26+
/// Initializes a new instance of the <see cref="ImageInputControl" /> class.
27+
/// </summary>
1828
public ImageInputControl()
1929
{
2030
if (!DesignerProperties.GetIsInDesignMode(this))
2131
_dialogService = App.GetService<IDialogService>();
2232

2333
LoadImageCommand = new AsyncRelayCommand(LoadImage);
2434
ClearImageCommand = new AsyncRelayCommand(ClearImage);
35+
MaskModeCommand = new AsyncRelayCommand(MaskMode);
36+
SaveMaskCommand = new AsyncRelayCommand(SaveMask);
2537
InitializeComponent();
2638
}
2739

2840
public AsyncRelayCommand LoadImageCommand { get; }
2941
public AsyncRelayCommand ClearImageCommand { get; }
42+
public AsyncRelayCommand MaskModeCommand { get; }
43+
public AsyncRelayCommand SaveMaskCommand { get; }
3044

3145
public ImageInput Result
3246
{
@@ -37,6 +51,14 @@ public ImageInput Result
3751
public static readonly DependencyProperty ResultProperty =
3852
DependencyProperty.Register("Result", typeof(ImageInput), typeof(ImageInputControl));
3953

54+
public ImageInput MaskResult
55+
{
56+
get { return (ImageInput)GetValue(MaskResultProperty); }
57+
set { SetValue(MaskResultProperty, value); }
58+
}
59+
60+
public static readonly DependencyProperty MaskResultProperty =
61+
DependencyProperty.Register("MaskResult", typeof(ImageInput), typeof(ImageInputControl));
4062

4163
public SchedulerOptionsModel SchedulerOptions
4264
{
@@ -47,7 +69,6 @@ public SchedulerOptionsModel SchedulerOptions
4769
public static readonly DependencyProperty SchedulerOptionsProperty =
4870
DependencyProperty.Register("SchedulerOptions", typeof(SchedulerOptionsModel), typeof(ImageInputControl));
4971

50-
5172
public bool HasResult
5273
{
5374
get { return (bool)GetValue(HasResultProperty); }
@@ -57,7 +78,60 @@ public bool HasResult
5778
public static readonly DependencyProperty HasResultProperty =
5879
DependencyProperty.Register("HasResult", typeof(bool), typeof(ImageInputControl));
5980

81+
public bool HasMaskResult
82+
{
83+
get { return (bool)GetValue(HasMaskResultProperty); }
84+
set { SetValue(HasMaskResultProperty, value); }
85+
}
86+
87+
public static readonly DependencyProperty HasMaskResultProperty =
88+
DependencyProperty.Register("HasMaskResult", typeof(bool), typeof(ImageInputControl));
89+
90+
public bool IsMaskEnabled
91+
{
92+
get { return (bool)GetValue(IsMaskEnabledProperty); }
93+
set { SetValue(IsMaskEnabledProperty, value); }
94+
}
95+
96+
// Using a DependencyProperty as the backing store for IsMaskEnabled. This enables animation, styling, binding, etc...
97+
public static readonly DependencyProperty IsMaskEnabledProperty =
98+
DependencyProperty.Register("IsMaskEnabled", typeof(bool), typeof(ImageInputControl));
99+
100+
101+
public InkCanvasEditingMode MaskEditingMode
102+
{
103+
get { return _maskEditingMode; }
104+
set { _maskEditingMode = value; NotifyPropertyChanged(); }
105+
}
106+
107+
public DrawingAttributes MaskAttributes
108+
{
109+
get { return _maskAttributes; }
110+
set { _maskAttributes = value; NotifyPropertyChanged(); }
111+
}
112+
113+
public bool IsMaskEraserEnabled
114+
{
115+
get { return _isMaskEraserEnabled; }
116+
set { _isMaskEraserEnabled = value; NotifyPropertyChanged(); }
117+
}
118+
119+
public int MaskDrawSize
120+
{
121+
get { return _maskDrawSize; }
122+
set
123+
{
124+
_maskDrawSize = value;
125+
NotifyPropertyChanged();
126+
UpdateMaskAttributes();
127+
}
128+
}
129+
60130

131+
/// <summary>
132+
/// Loads the image.
133+
/// </summary>
134+
/// <returns></returns>
61135
private Task LoadImage()
62136
{
63137
var loadImageDialog = _dialogService.GetDialog<CropImageDialog>();
@@ -75,13 +149,93 @@ private Task LoadImage()
75149
}
76150

77151

152+
/// <summary>
153+
/// Clears the image.
154+
/// </summary>
155+
/// <returns></returns>
78156
private Task ClearImage()
79157
{
80158
Result = null;
159+
MaskResult = null;
81160
HasResult = false;
161+
HasMaskResult = false;
162+
MaskCanvas.Strokes.Clear();
163+
IsMaskEraserEnabled = false;
164+
MaskEditingMode = InkCanvasEditingMode.Ink;
82165
return Task.CompletedTask;
83166
}
84167

168+
169+
/// <summary>
170+
/// Saves the mask.
171+
/// </summary>
172+
/// <returns></returns>
173+
private Task SaveMask()
174+
{
175+
MaskResult = new ImageInput
176+
{
177+
Image = CreateMaskImage(),
178+
FileName = "OnnxStack Generated Mask",
179+
};
180+
HasMaskResult = true;
181+
return Task.CompletedTask;
182+
}
183+
184+
185+
/// <summary>
186+
/// Change Masks mode.
187+
/// </summary>
188+
/// <returns></returns>
189+
private Task MaskMode()
190+
{
191+
if (_isMaskEraserEnabled)
192+
{
193+
IsMaskEraserEnabled = false;
194+
MaskEditingMode = InkCanvasEditingMode.Ink;
195+
}
196+
else
197+
{
198+
IsMaskEraserEnabled = true;
199+
MaskEditingMode = InkCanvasEditingMode.EraseByPoint;
200+
}
201+
return Task.CompletedTask;
202+
}
203+
204+
205+
/// <summary>
206+
/// Updates the mask attributes.
207+
/// </summary>
208+
private void UpdateMaskAttributes()
209+
{
210+
MaskAttributes = new DrawingAttributes
211+
{
212+
Color = Colors.Black,
213+
Height = _maskDrawSize,
214+
Width = _maskDrawSize,
215+
};
216+
}
217+
218+
219+
/// <summary>
220+
/// Creates the mask image.
221+
/// </summary>
222+
/// <returns></returns>
223+
public BitmapSource CreateMaskImage()
224+
{
225+
// Create a RenderTargetBitmap to render the Canvas content.
226+
var renderBitmap = new RenderTargetBitmap((int)MaskCanvas.ActualWidth, (int)MaskCanvas.ActualHeight, 96, 96, PixelFormats.Pbgra32);
227+
228+
// Make a drawing visual to render.
229+
var visual = new DrawingVisual();
230+
using (DrawingContext context = visual.RenderOpen())
231+
{
232+
VisualBrush brush = new VisualBrush(MaskCanvas);
233+
context.DrawRectangle(brush, null, new Rect(new Point(0, 0), new Point(MaskCanvas.ActualWidth, MaskCanvas.ActualHeight)));
234+
}
235+
renderBitmap.Render(visual);
236+
return renderBitmap;
237+
}
238+
85239
#region INotifyPropertyChanged
86240
public event PropertyChangedEventHandler PropertyChanged;
87241
public void NotifyPropertyChanged([CallerMemberName] string property = "")

OnnxStack.UI/Views/ImageToImage.xaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
<userControls:ImageInputControl
4747
Result="{Binding InputImage, Mode=TwoWay}"
4848
HasResult="{Binding HasInputResult, Mode=TwoWay}"
49-
SchedulerOptions="{Binding SchedulerOptions}" />
49+
SchedulerOptions="{Binding SchedulerOptions}"
50+
IsMaskEnabled="False"/>
5051
</Viewbox>
5152
<Viewbox Margin="20,40,40,40" >
5253
<userControls:ImageResultControl

0 commit comments

Comments
 (0)