forked from gerardo-lijs/MachineLearning-ObjectDetect-WPF
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: add router to mainwindow and extract folder view
- Loading branch information
Gerardo Lijs
committed
Mar 15, 2021
1 parent
8b83c18
commit b743f46
Showing
9 changed files
with
396 additions
and
243 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,61 @@ | ||
using System.Windows; | ||
using System; | ||
using System.Windows; | ||
|
||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Hosting; | ||
|
||
using ReactiveUI; | ||
using Splat; | ||
using Splat.Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace MachineLearning.ObjectDetect.WPF | ||
{ | ||
/// <summary> | ||
/// Interaction logic for App.xaml | ||
/// </summary> | ||
public partial class App : Application | ||
{ | ||
public IHost AppHost { get; } | ||
|
||
public App() | ||
{ | ||
// Build AppHost | ||
AppHost = Host | ||
.CreateDefaultBuilder() | ||
.ConfigureServices(ConfigureServices) | ||
.Build(); | ||
} | ||
|
||
private async void Application_Startup(object sender, StartupEventArgs e) | ||
{ | ||
await AppHost.StartAsync(); | ||
|
||
// Show main window | ||
StartupUri = new Uri("Views/MainWindow.xaml", UriKind.Relative); | ||
} | ||
|
||
private async void Application_Exit(object sender, ExitEventArgs e) | ||
{ | ||
using (AppHost) | ||
{ | ||
await AppHost.StopAsync(TimeSpan.FromSeconds(5)); | ||
} | ||
} | ||
|
||
private void ConfigureServices(IServiceCollection services) | ||
{ | ||
// RxUI uses Splat as its default DI engine but we can instruct it to use Microsoft DI instead | ||
services.UseMicrosoftDependencyResolver(); | ||
var resolver = Locator.CurrentMutable; | ||
resolver.InitializeSplat(); | ||
resolver.InitializeReactiveUI(); | ||
|
||
// Manual register ViewModels and Windows | ||
services.AddTransient<Views.MainWindow>(); | ||
services.AddTransient<ViewModels.MainWindowViewModel>(); | ||
|
||
services.AddTransient<ViewModels.FolderViewModel>(); | ||
//services.AddTransient<ViewModels.VideoViewModel>(); | ||
|
||
// Manual register views | ||
services.AddTransient(typeof(IViewFor<ViewModels.FolderViewModel>), typeof(Views.FolderView)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
121 changes: 121 additions & 0 deletions
121
src/MachineLearning.ObjectDetect.WPF/ViewModels/FolderViewModel.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reactive; | ||
using System.Reactive.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
using System.Drawing; | ||
using System.Windows.Media.Imaging; | ||
|
||
using ReactiveUI; | ||
using ReactiveUI.Fody.Helpers; | ||
using Splat; | ||
|
||
using OnnxObjectDetection; | ||
|
||
namespace MachineLearning.ObjectDetect.WPF.ViewModels | ||
{ | ||
public class FolderViewModel : ReactiveObject, IRoutableViewModel | ||
{ | ||
public string UrlPathSegment => "FolderView"; | ||
public IScreen HostScreen { get; } | ||
private readonly MainWindowViewModel _mainViewModel; | ||
|
||
// Commands | ||
public ReactiveCommand<Unit, Unit> PrevImage { get; } | ||
public ReactiveCommand<Unit, Unit> NextImage { get; } | ||
public ReactiveCommand<Unit, Unit> SelectImageFolder { get; } | ||
|
||
[Reactive] public string ImageFolderPath { get; private set; } = string.Empty; | ||
[Reactive] public List<string> ImageList { get; private set; } = new List<string>(); | ||
[Reactive] public int ImageCurrentIndex { get; private set; } | ||
|
||
[Reactive] public long DetectMilliseconds { get; private set; } | ||
[Reactive] public BitmapSource? DetectImageSource { get; private set; } | ||
public List<BoundingBox> FilteredBoundingBoxes { get; private set; } = new List<BoundingBox>(); | ||
|
||
// Interactions | ||
public readonly Interaction<Unit, Unit> DrawOverlays = new Interaction<Unit, Unit>(); | ||
|
||
public FolderViewModel(IScreen? screen = null) | ||
{ | ||
HostScreen = screen ?? Locator.Current.GetService<IScreen>(); | ||
_mainViewModel = HostScreen as MainWindowViewModel ?? throw new Exception("IScreen must be of type MainWindowViewModel"); | ||
|
||
// Create command | ||
PrevImage = ReactiveCommand.CreateFromTask(PrevImageImpl); | ||
NextImage = ReactiveCommand.CreateFromTask(NextImageImpl); | ||
SelectImageFolder = ReactiveCommand.CreateFromTask(SelectImageFolderImpl); | ||
|
||
// Observables | ||
this.WhenAnyValue(x => x.ImageFolderPath) | ||
.Skip(1) | ||
.Subscribe(folder => | ||
{ | ||
if (string.IsNullOrWhiteSpace(folder)) return; | ||
ImageList = System.IO.Directory.GetFiles(folder).Where(x => x.ToLowerInvariant().EndsWith(".png") || x.ToLowerInvariant().EndsWith(".jpg") || x.ToLowerInvariant().EndsWith(".jpeg") || x.ToLowerInvariant().EndsWith(".bmp")).ToList(); | ||
}); | ||
|
||
// Load image list | ||
ImageFolderPath = System.IO.Path.Combine(Environment.CurrentDirectory, "TestImages"); | ||
} | ||
|
||
private async Task SelectImageFolderImpl() | ||
{ | ||
using var dialog = new System.Windows.Forms.FolderBrowserDialog | ||
{ | ||
ShowNewFolderButton = false, | ||
SelectedPath = ImageFolderPath | ||
}; | ||
|
||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) | ||
{ | ||
ImageFolderPath = dialog.SelectedPath; | ||
ImageCurrentIndex = 0; | ||
if (ImageList.Count > 0) | ||
{ | ||
await NextImage.Execute(); | ||
} | ||
} | ||
} | ||
|
||
private async Task PrevImageImpl() | ||
{ | ||
if (ImageCurrentIndex <= 1) return; | ||
ImageCurrentIndex--; | ||
await LoadAndDetectImage(ImageList[ImageCurrentIndex - 1]); | ||
} | ||
|
||
private async Task NextImageImpl() | ||
{ | ||
if (ImageList.Count == ImageCurrentIndex) return; | ||
ImageCurrentIndex++; | ||
await LoadAndDetectImage(ImageList[ImageCurrentIndex - 1]); | ||
} | ||
|
||
private async Task DetectImage(Bitmap bitmap) | ||
{ | ||
var imageInputData = new ImageInputData { Image = bitmap }; | ||
|
||
var sw = System.Diagnostics.Stopwatch.StartNew(); | ||
|
||
var labels = _mainViewModel.CustomVisionPredictionEngine.Predict(imageInputData).PredictedLabels; | ||
var boundingBoxes = _mainViewModel.OutputParser.ParseOutputs(labels); | ||
FilteredBoundingBoxes = _mainViewModel.OutputParser.FilterBoundingBoxes(boundingBoxes, 5, 0.5f); | ||
|
||
// Time spent for detection by ML.NET | ||
DetectMilliseconds = sw.ElapsedMilliseconds; | ||
|
||
await DrawOverlays.Handle(Unit.Default); | ||
} | ||
|
||
private async Task LoadAndDetectImage(string filename) | ||
{ | ||
var bitmapImage = new Bitmap(filename); | ||
DetectImageSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmapImage.GetHbitmap(), IntPtr.Zero, System.Windows.Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); | ||
await DetectImage(bitmapImage); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.