From 4145867137323304c14efa05c6c01a1122c5123e Mon Sep 17 00:00:00 2001 From: gosha20777 Date: Mon, 11 Nov 2019 22:07:14 +0300 Subject: [PATCH 1/3] feat: add hide and show boubd boxes function --- DataUtils/LaddGenerator/Program.cs | 11 ++- RescuerLaApp/Models/BoundBox.cs | 9 +++ .../ViewModels/MainWindowViewModel.cs | 68 +++++++++++++++++-- RescuerLaApp/Views/MainWindow.xaml | 4 +- 4 files changed, 84 insertions(+), 8 deletions(-) diff --git a/DataUtils/LaddGenerator/Program.cs b/DataUtils/LaddGenerator/Program.cs index 3a5b9a4..fa66ab6 100644 --- a/DataUtils/LaddGenerator/Program.cs +++ b/DataUtils/LaddGenerator/Program.cs @@ -44,12 +44,17 @@ static void Main(string[] args) { Directory.CreateDirectory(annDstPatch); } + if (!Directory.Exists(spltDstPatch)) + { + Directory.CreateDirectory(spltDstPatch); + } var srcFiles = Directory.GetFiles(annSrcPatch); var dstImgFileNames = Directory.GetFiles(imgDstPatch); int count = 0; //420; + int beginCount = 769; if (dstImgFileNames == null || dstImgFileNames.Length == 0) - count = 0; + count = beginCount; else { count = dstImgFileNames.Length; @@ -73,7 +78,7 @@ static void Main(string[] args) if (dstAnnotation.Objects == null || dstAnnotation.Objects.Count <= 0) { dstAnnotation.Objects = new List(); - throw new Exception("no objects in the image!"); + //throw new Exception("no objects in the image!"); } foreach (var obj in dstAnnotation.Objects) { @@ -93,7 +98,7 @@ static void Main(string[] args) Console.Write($"Shuffling {count-1} files"); List files = new List(); - for (int i = 0; i < count; i++) + for (int i = beginCount; i < count; i++) { files.Add(i); } diff --git a/RescuerLaApp/Models/BoundBox.cs b/RescuerLaApp/Models/BoundBox.cs index 4ac2a69..5b1d91a 100755 --- a/RescuerLaApp/Models/BoundBox.cs +++ b/RescuerLaApp/Models/BoundBox.cs @@ -10,6 +10,7 @@ public class BoundBox private int _y; private int _height; private int _width; + private bool _isVisible; private readonly int _xBase; private readonly int _yBase; private readonly int _heightBase; @@ -21,6 +22,7 @@ public BoundBox(int x, int y, int height, int width) _y = _yBase = y; _width = _widthBase = width; _height = _heightBase = height; + _isVisible = true; } public List Points @@ -34,6 +36,13 @@ public List Points return new List() {p1, p3, p4, p2}; } } + + public bool IsVisible + { + get => _isVisible; + set => _isVisible = value; + } + public int X { get => _x; diff --git a/RescuerLaApp/ViewModels/MainWindowViewModel.cs b/RescuerLaApp/ViewModels/MainWindowViewModel.cs index 1ff81df..cc9d7c2 100755 --- a/RescuerLaApp/ViewModels/MainWindowViewModel.cs +++ b/RescuerLaApp/ViewModels/MainWindowViewModel.cs @@ -13,6 +13,7 @@ using MessageBox.Avalonia.DTO; using MessageBox.Avalonia.Enums; using MessageBox.Avalonia.Models; +using MessageBox.Avalonia.Views; using MetadataExtractor; using ReactiveUI; using ReactiveUI.Fody.Helpers; @@ -48,6 +49,10 @@ public MainWindowViewModel(Window window) .WhenAnyValue(x => x.Status) .Select(status => status.Status != Enums.Status.Working && status.Status != Enums.Status.Unauthenticated); + var canSwitchBoundBox = this + .WhenAnyValue(x => x.BoundBoxes) + .Select(count => BoundBoxes?.Count > 0); + var canAuth = this .WhenAnyValue(x => x.Status) .Select(status => status.Status == Enums.Status.Unauthenticated); @@ -75,6 +80,7 @@ public MainWindowViewModel(Window window) SaveAllImagesWithObjectsCommand = ReactiveCommand.Create(SaveAllImagesWithObjects, canExecute); ShowAllMetadataCommand = ReactiveCommand.Create(ShowAllMetadata, canExecute); ShowGeoDataCommand = ReactiveCommand.Create(ShowGeoData, canExecute); + SwitchBoundBoxesVisibilityCommand = ReactiveCommand.Create(SwitchBoundBoxesVisibility, canSwitchBoundBox); HelpCommand = ReactiveCommand.Create(Help); AboutCommand = ReactiveCommand.Create(About); SignUpCommand = ReactiveCommand.Create(SignUp, canAuth); @@ -94,6 +100,7 @@ public void UpdateFramesRepo() Status = Enums.Status.Ready, StringStatus = $"{Enums.Status.Ready.ToString()} | {Frames[SelectedIndex].Patch}" }; + SwitchBoundBoxesVisibilityToTrue(); UpdateUi(); }); } @@ -101,11 +108,13 @@ public void UpdateFramesRepo() #region Public API [Reactive] public List BoundBoxes { get; set; } = new List(); + // TODO: update with locales + [Reactive] public string BoundBoxesStateString { get; set; } = "Hide bound boxes"; [Reactive] public double CanvasWidth { get; set; } = 500; [Reactive] public double CanvasHeight { get; set; } = 500; - + [Reactive] public int SelectedIndex { get; set; } = 0; [Reactive] public List Frames { get; set; } = new List(); @@ -142,6 +151,7 @@ public void UpdateFramesRepo() public ReactiveCommand ShowAllMetadataCommand { get; } public ReactiveCommand ShowGeoDataCommand { get; } + public ReactiveCommand SwitchBoundBoxesVisibilityCommand { get; } public ReactiveCommand HelpCommand { get; } public ReactiveCommand AboutCommand { get; } public ReactiveCommand SignUpCommand { get; } @@ -586,7 +596,13 @@ public void ShowGeoData() ContentMessage = msg, Icon = Icon.Info, Style = Style.None, - ShowInCenter = true + ShowInCenter = true, + Window = new MsBoxStandardWindow + { + Height = 300, + Width = 500, + CanResize = true + } }); msgbox.Show(); } @@ -610,11 +626,49 @@ public void ShowAllMetadata() ContentMessage = tb.Output(), Icon = Icon.Info, Style = Style.None, - ShowInCenter = true + ShowInCenter = true, + Window = new MsBoxStandardWindow + { + Height = 600, + Width = 1300, + CanResize = true + } }); msgbox.Show(); } + public void SwitchBoundBoxesVisibility() + { + var isVisible = true; + + if (BoundBoxes == null) return; + if (BoundBoxes.Count > 0) + isVisible = BoundBoxes[0].IsVisible; + + foreach (var rectangle in BoundBoxes) + { + rectangle.IsVisible = !isVisible; + } + + if (BoundBoxes[0].IsVisible) + BoundBoxesStateString = "Hide bound boxes"; + else + BoundBoxesStateString = "Show bound boxes"; + + UpdateUi(); + } + + private void SwitchBoundBoxesVisibilityToTrue() + { + if (BoundBoxes == null || BoundBoxes[0].IsVisible) return; + + foreach (var rectangle in BoundBoxes) + { + rectangle.IsVisible = true; + } + BoundBoxesStateString = "Hide bound boxes"; + } + public async void About() { var message = @@ -635,7 +689,13 @@ public async void About() ContentMessage = message, Icon = Icon.Avalonia, Style = Style.None, - ShowInCenter = true + ShowInCenter = true, + Window = new MsBoxCustomWindow + { + Height = 400, + Width = 1000, + CanResize = true + } }; var msgbox = MessageBoxManager.GetMessageBoxCustomWindow(msgBoxCustomParams); var result = await msgbox.Show(); diff --git a/RescuerLaApp/Views/MainWindow.xaml b/RescuerLaApp/Views/MainWindow.xaml index c0d06c9..4198c59 100755 --- a/RescuerLaApp/Views/MainWindow.xaml +++ b/RescuerLaApp/Views/MainWindow.xaml @@ -124,6 +124,7 @@ + @@ -133,7 +134,8 @@ + Stroke="Red" StrokeThickness="10" + IsVisible="{Binding IsVisible}"/> From 0ca68be02bbd24889e6568b154dd4d51847833b9 Mon Sep 17 00:00:00 2001 From: gosha20777 Date: Tue, 12 Nov 2019 01:54:29 +0300 Subject: [PATCH 2/3] dirty: redesign app, add tab control --- RescuerLaApp/App.xaml | 4 +++ RescuerLaApp/RescuerLaApp.csproj | 1 + RescuerLaApp/Views/MainWindow.xaml | 56 ++++++++++++++++++++++-------- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/RescuerLaApp/App.xaml b/RescuerLaApp/App.xaml index 2cf943a..955a002 100755 --- a/RescuerLaApp/App.xaml +++ b/RescuerLaApp/App.xaml @@ -2,7 +2,11 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="RescuerLaApp.App"> + + + \ No newline at end of file diff --git a/RescuerLaApp/RescuerLaApp.csproj b/RescuerLaApp/RescuerLaApp.csproj index 48b49df..6638834 100755 --- a/RescuerLaApp/RescuerLaApp.csproj +++ b/RescuerLaApp/RescuerLaApp.csproj @@ -21,6 +21,7 @@ + diff --git a/RescuerLaApp/Views/MainWindow.xaml b/RescuerLaApp/Views/MainWindow.xaml index 4198c59..f7939fb 100755 --- a/RescuerLaApp/Views/MainWindow.xaml +++ b/RescuerLaApp/Views/MainWindow.xaml @@ -80,20 +80,48 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c28acd64c2d2215b49e6caacca0850b468bc7b0f Mon Sep 17 00:00:00 2001 From: gosha20777 Date: Wed, 13 Nov 2019 03:08:07 +0300 Subject: [PATCH 3/3] feat: geo tags transform, favorites, new design --- RescuerLaApp/Models/Frame.cs | 1 + RescuerLaApp/Program.cs | 2 +- .../ViewModels/MainWindowViewModel.cs | 184 +++++++++++++++++- RescuerLaApp/Views/MainWindow.xaml | 31 ++- 4 files changed, 207 insertions(+), 11 deletions(-) diff --git a/RescuerLaApp/Models/Frame.cs b/RescuerLaApp/Models/Frame.cs index 72a914f..c4f9cd3 100755 --- a/RescuerLaApp/Models/Frame.cs +++ b/RescuerLaApp/Models/Frame.cs @@ -52,6 +52,7 @@ public List Rectangles public int Height { get; private set; } public bool IsVisible { get; set; } = false; + public bool IsFavorite { get; set; } = false; public delegate void MethodContainer(); diff --git a/RescuerLaApp/Program.cs b/RescuerLaApp/Program.cs index 278ce1f..03282fc 100755 --- a/RescuerLaApp/Program.cs +++ b/RescuerLaApp/Program.cs @@ -12,7 +12,7 @@ internal static class Program { private static void Main(string[] args) { - Console.WriteLine("Lacmus desktop application. Version 0.3.2-preview alpha. \nCopyright (c) 2019 Georgy Perevozghikov \nGithub page: https://github.com/lizaalert/lacmus/.\nProvided by Yandex Cloud: https://cloud.yandex.com/."); + Console.WriteLine("Lacmus desktop application. Version 0.3.2 alpha. \nCopyright (c) 2019 Georgy Perevozghikov \nGithub page: https://github.com/lizaalert/lacmus/.\nProvided by Yandex Cloud: https://cloud.yandex.com/."); Console.WriteLine("This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'."); Console.WriteLine("This is free software, and you are welcome to redistribute it\nunder certain conditions; type `show c' for details."); Console.WriteLine("------------------------------------"); diff --git a/RescuerLaApp/ViewModels/MainWindowViewModel.cs b/RescuerLaApp/ViewModels/MainWindowViewModel.cs index cc9d7c2..df8e571 100755 --- a/RescuerLaApp/ViewModels/MainWindowViewModel.cs +++ b/RescuerLaApp/ViewModels/MainWindowViewModel.cs @@ -76,10 +76,13 @@ public MainWindowViewModel(Window window) LoadModelCommand = ReactiveCommand.Create(LoadModel, canExecute); UpdateModelCommand = ReactiveCommand.Create(UpdateModel, canExecute); ShowPerestriansCommand = ReactiveCommand.Create(ShowPedestrians, canExecute); + ShowFavoritesCommand = ReactiveCommand.Create(ShowFavorites, canExecute); ImportAllCommand = ReactiveCommand.Create(ImportAll, canExecute); SaveAllImagesWithObjectsCommand = ReactiveCommand.Create(SaveAllImagesWithObjects, canExecute); + SaveFavoritesImagesCommand = ReactiveCommand.Create(SaveFavoritesImages, canExecute); ShowAllMetadataCommand = ReactiveCommand.Create(ShowAllMetadata, canExecute); ShowGeoDataCommand = ReactiveCommand.Create(ShowGeoData, canExecute); + AddToFavoritesCommand = ReactiveCommand.Create(AddToFavorites, canExecute); SwitchBoundBoxesVisibilityCommand = ReactiveCommand.Create(SwitchBoundBoxesVisibility, canSwitchBoundBox); HelpCommand = ReactiveCommand.Create(Help); AboutCommand = ReactiveCommand.Create(About); @@ -101,6 +104,10 @@ public void UpdateFramesRepo() StringStatus = $"{Enums.Status.Ready.ToString()} | {Frames[SelectedIndex].Patch}" }; SwitchBoundBoxesVisibilityToTrue(); + if (Frames[SelectedIndex].IsFavorite) + FavoritesStateString = "Remove from favorites"; + else + FavoritesStateString = "Add to favorites"; UpdateUi(); }); } @@ -110,6 +117,7 @@ public void UpdateFramesRepo() [Reactive] public List BoundBoxes { get; set; } = new List(); // TODO: update with locales [Reactive] public string BoundBoxesStateString { get; set; } = "Hide bound boxes"; + [Reactive] public string FavoritesStateString { get; set; } = "Add to favorites"; [Reactive] public double CanvasWidth { get; set; } = 500; @@ -124,6 +132,7 @@ public void UpdateFramesRepo() [Reactive] public ImageBrush ImageBrush { get; set; } = new ImageBrush { Stretch = Stretch.Uniform }; [Reactive] public bool IsShowPedestrians { get; set; } = false; + [Reactive] public bool IsShowFavorites { get; set; } = false; public ReactiveCommand PredictAllCommand { get; } @@ -146,11 +155,12 @@ public void UpdateFramesRepo() public ReactiveCommand UpdateModelCommand { get; } public ReactiveCommand ShowPerestriansCommand { get; } - + public ReactiveCommand ShowFavoritesCommand { get; } public ReactiveCommand SaveAllImagesWithObjectsCommand { get; } - + public ReactiveCommand SaveFavoritesImagesCommand { get; } public ReactiveCommand ShowAllMetadataCommand { get; } public ReactiveCommand ShowGeoDataCommand { get; } + public ReactiveCommand AddToFavoritesCommand { get; } public ReactiveCommand SwitchBoundBoxesVisibilityCommand { get; } public ReactiveCommand HelpCommand { get; } public ReactiveCommand AboutCommand { get; } @@ -166,11 +176,19 @@ private void ShowPedestrians() { if (IsShowPedestrians) { - //fix bug when application stop if focus was set on image without object + // fix bug when application stop if focus was set on image without object if (!_frames.Any(x => x.IsVisible)) + { + IsShowPedestrians = false; + if (IsShowFavorites) + { + IsShowFavorites = false; + ShowFavorites(); + } return; + } + IsShowFavorites = false; SelectedIndex = Frames.FindIndex(x => x.IsVisible); - Console.WriteLine(SelectedIndex); Frames = _frames.FindAll(x => x.IsVisible); UpdateUi(); } @@ -180,6 +198,30 @@ private void ShowPedestrians() UpdateUi(); } } + + private void ShowFavorites() + { + if (IsShowFavorites) + { + //fix bug when application stop if focus was set on image without object + if (!_frames.Any(x => x.IsFavorite)) + { + IsShowFavorites = false; + ShowPedestrians(); + return; + } + + IsShowPedestrians = false; + SelectedIndex = Frames.FindIndex(x => x.IsFavorite); + Frames = _frames.FindAll(x => x.IsFavorite); + UpdateUi(); + } + else + { + Frames = new List(_frames); + UpdateUi(); + } + } private async void LoadModel() { @@ -453,6 +495,85 @@ private async void SaveAllImagesWithObjects() } } + private async void SaveFavoritesImages() + { + try + { + if (Frames == null || Frames.Count < 1) + { + Status = new AppStatusInfo() {Status = Enums.Status.Ready}; + return; + } + Status = new AppStatusInfo() {Status = Enums.Status.Working}; + + var openDig = new OpenFolderDialog() + { + Title = "Choose a directory to save images with objects" + }; + var dirName = await openDig.ShowAsync(new Window()); + + + if (string.IsNullOrEmpty(dirName) || !Directory.Exists(dirName)) + { + Status = new AppStatusInfo() {Status = Enums.Status.Ready}; + return; + } + + foreach (var frame in Frames) + { + if (!frame.IsFavorite) + continue; + + var annotation = new Annotation(); + annotation.Filename = Path.GetFileName(frame.Patch); + annotation.Folder = Path.GetRelativePath(dirName, Path.GetDirectoryName(frame.Patch)); + annotation.Segmented = 0; + annotation.Size = new Models.Size() + { + Depth = 3, + Height = frame.Height, + Width = frame.Width + }; + if (frame.Rectangles == null) + { + frame.Rectangles = new List(); + } + foreach (var rectangle in frame.Rectangles) + { + var o = new Models.Object(); + o.Name = "Pedestrian"; + o.Box = new Box() + { + Xmax = rectangle.XBase + rectangle.WidthBase, + Ymax = rectangle.YBase + rectangle.HeightBase, + Xmin = rectangle.XBase, + Ymin = rectangle.YBase + }; + annotation.Objects.Add(o); + } + + annotation.SaveToXml(Path.Join(dirName,$"{annotation.Filename}.xml")); + + if (frame.Rectangles.Count == 0) + { + frame.Rectangles = null; + } + + File.Copy(frame.Patch, Path.Combine(dirName, Path.GetFileName(frame.Patch))); + } + Console.WriteLine($"Saved to {dirName}"); + Status = new AppStatusInfo() {Status = Enums.Status.Ready, StringStatus = $"Success | saved to {dirName}"}; + } + catch (Exception ex) + { + Status = new AppStatusInfo() + { + Status = Enums.Status.Error, + StringStatus = $"Error | {ex.Message.Replace('\n', ' ')}" + }; + } + } + private async void ImportAll() { Status = new AppStatusInfo() {Status = Enums.Status.Working}; @@ -582,7 +703,7 @@ public void ShowGeoData() tag.Name.ToLower() == "gps altitude") { rows++; - msg += $"{tag.Name}: {tag.Description}\n"; + msg += $"{tag.Name}: {TranslateGeoTag(tag.Description)}\n"; } } } @@ -607,6 +728,36 @@ public void ShowGeoData() msgbox.Show(); } + private string TranslateGeoTag(string tag) + { + /* + GPS Latitude: 55° 11' 51,44" + GPS Longitude: 37° 41' 39,88" + GPS Altitude: 124 metres + */ + try + { + if (!tag.Contains('°')) + return tag; + tag = tag.Replace('°', ';'); + tag = tag.Replace('\'', ';'); + tag = tag.Replace('"', ';'); + tag = tag.Replace(" ", ""); + + var splitTag = tag.Split(';'); + var grad = float.Parse(splitTag[0]); + var min = float.Parse(splitTag[1]); + var sec = float.Parse(splitTag[2]); + + float result = grad + min / 60 + sec / 3600; + return $"{result}"; + } + catch (Exception e) + { + return tag; + } + } + public void ShowAllMetadata() { var tb = new TextTableBuilder(); @@ -637,6 +788,27 @@ public void ShowAllMetadata() msgbox.Show(); } + public void AddToFavorites() + { + Frames[SelectedIndex].IsFavorite = !Frames[SelectedIndex].IsFavorite; + + if (IsShowFavorites) + { + IsShowPedestrians = false; + //fix bug when application stop if focus was set on image without object + if (!_frames.Any(x => x.IsFavorite)) + { + IsShowFavorites = false; + ShowFavorites(); + return; + } + + SelectedIndex = Frames.FindIndex(x => x.IsFavorite); + Frames = _frames.FindAll(x => x.IsFavorite); + UpdateUi(); + } + } + public void SwitchBoundBoxesVisibility() { var isVisible = true; @@ -685,7 +857,7 @@ public async void About() new ButtonDefinition{Name = "Github"} }, ContentTitle = "About", - ContentHeader = "Lacmus desktop application. Version 0.3.2-preview alpha.", + ContentHeader = "Lacmus desktop application. Version 0.3.2 alpha.", ContentMessage = message, Icon = Icon.Avalonia, Style = Style.None, diff --git a/RescuerLaApp/Views/MainWindow.xaml b/RescuerLaApp/Views/MainWindow.xaml index f7939fb..6db724c 100755 --- a/RescuerLaApp/Views/MainWindow.xaml +++ b/RescuerLaApp/Views/MainWindow.xaml @@ -25,6 +25,7 @@ + @@ -77,9 +78,24 @@ - + + + + + + + + + + + + + + + + + @@ -149,10 +169,13 @@ Width="{Binding CanvasWidth, Mode=TwoWay}"> + + - + +