mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 09:36:30 +00:00
lot of small fixes for dataset explorer
This commit is contained in:
@@ -14,5 +14,6 @@ public enum PlaybackControlEnum
|
|||||||
TurnOffVolume = 9,
|
TurnOffVolume = 9,
|
||||||
TurnOnVolume = 10,
|
TurnOnVolume = 10,
|
||||||
Previous = 11,
|
Previous = 11,
|
||||||
Next = 12
|
Next = 12,
|
||||||
|
Close = 13
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
using Azaion.Annotator.Extensions;
|
||||||
|
|
||||||
namespace Azaion.Annotator.DTO;
|
namespace Azaion.Annotator.DTO;
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ public class ThumbnailDto : INotifyPropertyChanged
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_image == null)
|
if (_image == null)
|
||||||
LoadImageAsync();
|
Task.Run(async () => Image = await ThumbnailPath.OpenImage());
|
||||||
return _image;
|
return _image;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
@@ -26,28 +27,7 @@ public class ThumbnailDto : INotifyPropertyChanged
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void LoadImageAsync()
|
public void UpdateImage() => _image = null;
|
||||||
{
|
|
||||||
await Task.Run(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var bitmap = new BitmapImage();
|
|
||||||
bitmap.BeginInit();
|
|
||||||
bitmap.UriSource = new Uri(ThumbnailPath);
|
|
||||||
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
|
||||||
bitmap.DecodePixelWidth = 480;
|
|
||||||
bitmap.DecodePixelHeight = 270;
|
|
||||||
bitmap.EndInit();
|
|
||||||
bitmap.Freeze(); // Freeze to make it cross-thread accessible
|
|
||||||
Image = bitmap;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.WriteLine(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||||
|
|||||||
@@ -103,7 +103,7 @@
|
|||||||
</StatusBarItem>
|
</StatusBarItem>
|
||||||
<Separator Grid.Column="2"/>
|
<Separator Grid.Column="2"/>
|
||||||
<StatusBarItem Grid.Column="3" Background="Black">
|
<StatusBarItem Grid.Column="3" Background="Black">
|
||||||
<TextBlock Text="{Binding AnnotationCount}" />
|
<TextBlock Name="StatusText" Text="Loading..."/>
|
||||||
</StatusBarItem>
|
</StatusBarItem>
|
||||||
</StatusBar>
|
</StatusBar>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -23,25 +23,24 @@ public partial class DatasetExplorer
|
|||||||
|
|
||||||
private int _tempSelectedClassIdx = 0;
|
private int _tempSelectedClassIdx = 0;
|
||||||
private readonly string _thumbnailsCacheFile;
|
private readonly string _thumbnailsCacheFile;
|
||||||
private IConfigRepository _configRepository;
|
private readonly IConfigRepository _configRepository;
|
||||||
private readonly FormState _formState;
|
private readonly FormState _formState;
|
||||||
private readonly IGalleryManager _galleryManager;
|
|
||||||
private static Dictionary<string, List<int>> LabelsCache { get; set; } = new();
|
private static Dictionary<string, List<int>> LabelsCache { get; set; } = new();
|
||||||
|
|
||||||
|
public bool ThumbnailLoading { get; set; }
|
||||||
|
|
||||||
public ThumbnailDto? CurrentThumbnail { get; set; }
|
public ThumbnailDto? CurrentThumbnail { get; set; }
|
||||||
|
|
||||||
public DatasetExplorer(
|
public DatasetExplorer(
|
||||||
Config config,
|
Config config,
|
||||||
ILogger<DatasetExplorer> logger,
|
ILogger<DatasetExplorer> logger,
|
||||||
IConfigRepository configRepository,
|
IConfigRepository configRepository,
|
||||||
FormState formState,
|
FormState formState)
|
||||||
IGalleryManager galleryManager)
|
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_configRepository = configRepository;
|
_configRepository = configRepository;
|
||||||
_formState = formState;
|
_formState = formState;
|
||||||
_galleryManager = galleryManager;
|
|
||||||
_thumbnailsCacheFile = Path.Combine(config.ThumbnailsDirectory, Config.ThumbnailsCacheFile);
|
_thumbnailsCacheFile = Path.Combine(config.ThumbnailsDirectory, Config.ThumbnailsCacheFile);
|
||||||
if (File.Exists(_thumbnailsCacheFile))
|
if (File.Exists(_thumbnailsCacheFile))
|
||||||
{
|
{
|
||||||
@@ -116,33 +115,12 @@ public partial class DatasetExplorer
|
|||||||
};
|
};
|
||||||
ThumbnailsView.MouseDoubleClick += async (_, _) => await EditAnnotation();
|
ThumbnailsView.MouseDoubleClick += async (_, _) => await EditAnnotation();
|
||||||
|
|
||||||
Activated += (_, _) => { _formState.ActiveWindow = WindowsEnum.DatasetExplorer; };
|
ThumbnailsView.SelectionChanged += (_, _) =>
|
||||||
|
|
||||||
ExplorerEditor.GetTimeFunc = () => _formState.GetTime(CurrentThumbnail!.ImagePath)!.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task EditAnnotation()
|
|
||||||
{
|
|
||||||
if (ThumbnailsView.SelectedItem == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var dto = (ThumbnailsView.SelectedItem as ThumbnailDto)!;
|
|
||||||
ExplorerEditor.Background = new ImageBrush
|
|
||||||
{
|
{
|
||||||
ImageSource = new BitmapImage(new Uri(dto.ImagePath))
|
StatusText.Text = $"Обрано: {ThumbnailsView.SelectedItems.Count} | {ThumbnailsView.SelectedIndex} / {ThumbnailsDtos.Count}";
|
||||||
};
|
};
|
||||||
CurrentThumbnail = dto;
|
|
||||||
|
|
||||||
Switcher.SelectedIndex = 1;
|
Activated += (_, _) => { _formState.ActiveWindow = WindowsEnum.DatasetExplorer; };
|
||||||
LvClasses.SelectedIndex = 1;
|
|
||||||
|
|
||||||
var time = _formState.GetTime(dto.ImagePath)!.Value;
|
|
||||||
foreach (var ann in await YoloLabel.ReadFromFile(dto.LabelPath))
|
|
||||||
{
|
|
||||||
var annClass = _config.AnnotationClasses[ann.ClassNumber];
|
|
||||||
var annInfo = new CanvasLabel(ann, ExplorerEditor.RenderSize, ExplorerEditor.RenderSize);
|
|
||||||
Dispatcher.Invoke(() => ExplorerEditor.CreateAnnotation(annClass, time, annInfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
Switcher.SelectionChanged += (sender, args) =>
|
Switcher.SelectionChanged += (sender, args) =>
|
||||||
{
|
{
|
||||||
@@ -157,8 +135,53 @@ public partial class DatasetExplorer
|
|||||||
//Explorer
|
//Explorer
|
||||||
LvClasses.ItemsSource = AllAnnotationClasses;
|
LvClasses.ItemsSource = AllAnnotationClasses;
|
||||||
LvClasses.SelectedIndex = _tempSelectedClassIdx;
|
LvClasses.SelectedIndex = _tempSelectedClassIdx;
|
||||||
|
ExplorerEditor.Background = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ExplorerEditor.GetTimeFunc = () => _formState.GetTime(CurrentThumbnail!.ImagePath)!.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EditAnnotation()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ThumbnailLoading = true;
|
||||||
|
|
||||||
|
if (ThumbnailsView.SelectedItem == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var dto = (ThumbnailsView.SelectedItem as ThumbnailDto)!;
|
||||||
|
CurrentThumbnail = dto;
|
||||||
|
ExplorerEditor.Background = new ImageBrush
|
||||||
|
{
|
||||||
|
ImageSource = await dto.ImagePath.OpenImage()
|
||||||
|
};
|
||||||
|
|
||||||
|
Switcher.SelectedIndex = 1;
|
||||||
|
LvClasses.SelectedIndex = 1;
|
||||||
|
|
||||||
|
var time = _formState.GetTime(dto.ImagePath)!.Value;
|
||||||
|
ExplorerEditor.RemoveAllAnns();
|
||||||
|
foreach (var ann in await YoloLabel.ReadFromFile(dto.LabelPath))
|
||||||
|
{
|
||||||
|
var annClass = _config.AnnotationClasses[ann.ClassNumber];
|
||||||
|
var annInfo = new CanvasLabel(ann, ExplorerEditor.RenderSize, ExplorerEditor.RenderSize);
|
||||||
|
ExplorerEditor.CreateAnnotation(annClass, time, annInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThumbnailLoading = false;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, e.Message);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ThumbnailLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveUserSettings()
|
private async Task SaveUserSettings()
|
||||||
@@ -173,6 +196,7 @@ public partial class DatasetExplorer
|
|||||||
|
|
||||||
private void DeleteAnnotations()
|
private void DeleteAnnotations()
|
||||||
{
|
{
|
||||||
|
var tempSelected = ThumbnailsView.SelectedIndex;
|
||||||
var result = MessageBox.Show("Чи дійсно видалити аннотації?","Підтвердження видалення", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
var result = MessageBox.Show("Чи дійсно видалити аннотації?","Підтвердження видалення", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||||
if (result != MessageBoxResult.Yes)
|
if (result != MessageBoxResult.Yes)
|
||||||
return;
|
return;
|
||||||
@@ -186,6 +210,7 @@ public partial class DatasetExplorer
|
|||||||
File.Delete(dto.ThumbnailPath);
|
File.Delete(dto.ThumbnailPath);
|
||||||
ThumbnailsDtos.Remove(dto);
|
ThumbnailsDtos.Remove(dto);
|
||||||
}
|
}
|
||||||
|
ThumbnailsView.SelectedIndex = Math.Min(ThumbnailsDtos.Count, tempSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReloadThumbnails()
|
private async Task ReloadThumbnails()
|
||||||
@@ -212,11 +237,14 @@ public partial class DatasetExplorer
|
|||||||
{
|
{
|
||||||
var name = Path.GetFileNameWithoutExtension(thumbnail)[..^Config.ThumbnailPrefix.Length];
|
var name = Path.GetFileNameWithoutExtension(thumbnail)[..^Config.ThumbnailPrefix.Length];
|
||||||
var imageName = Path.Combine(_config.ImagesDirectory, name);
|
var imageName = Path.Combine(_config.ImagesDirectory, name);
|
||||||
foreach (var imageFormat in _config.ImageFormats)
|
foreach (var f in _config.ImageFormats)
|
||||||
{
|
{
|
||||||
imageName = $"{imageName}.{imageFormat}";
|
var curName = $"{imageName}.{f}";
|
||||||
if (File.Exists(imageName))
|
if (File.Exists(curName))
|
||||||
|
{
|
||||||
|
imageName = curName;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var labelPath = Path.Combine(_config.LabelsDirectory, $"{name}.txt");
|
var labelPath = Path.Combine(_config.LabelsDirectory, $"{name}.txt");
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ public class DatasetExplorerEventHandler(DatasetExplorer datasetExplorer,
|
|||||||
{
|
{
|
||||||
{ Key.Enter, PlaybackControlEnum.SaveAnnotations },
|
{ Key.Enter, PlaybackControlEnum.SaveAnnotations },
|
||||||
{ Key.Delete, PlaybackControlEnum.RemoveSelectedAnns },
|
{ Key.Delete, PlaybackControlEnum.RemoveSelectedAnns },
|
||||||
{ Key.X, PlaybackControlEnum.RemoveAllAnns }
|
{ Key.X, PlaybackControlEnum.RemoveAllAnns },
|
||||||
|
{ Key.Escape, PlaybackControlEnum.Close }
|
||||||
};
|
};
|
||||||
|
|
||||||
public async Task Handle(KeyEvent keyEvent, CancellationToken cancellationToken)
|
public async Task Handle(KeyEvent keyEvent, CancellationToken cancellationToken)
|
||||||
@@ -42,13 +43,16 @@ public class DatasetExplorerEventHandler(DatasetExplorer datasetExplorer,
|
|||||||
switch (controlEnum)
|
switch (controlEnum)
|
||||||
{
|
{
|
||||||
case PlaybackControlEnum.SaveAnnotations:
|
case PlaybackControlEnum.SaveAnnotations:
|
||||||
|
if (datasetExplorer.ThumbnailLoading)
|
||||||
|
return;
|
||||||
|
|
||||||
var currentAnns = datasetExplorer.ExplorerEditor.CurrentAnns
|
var currentAnns = datasetExplorer.ExplorerEditor.CurrentAnns
|
||||||
.Select(x => new YoloLabel(x.Info, datasetExplorer.ExplorerEditor.RenderSize, datasetExplorer.ExplorerEditor.RenderSize))
|
.Select(x => new YoloLabel(x.Info, datasetExplorer.ExplorerEditor.RenderSize, datasetExplorer.ExplorerEditor.RenderSize))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
await YoloLabel.WriteToFile(currentAnns, Path.Combine(config.LabelsDirectory, datasetExplorer.CurrentThumbnail!.LabelPath));
|
await YoloLabel.WriteToFile(currentAnns, Path.Combine(config.LabelsDirectory, datasetExplorer.CurrentThumbnail!.LabelPath));
|
||||||
await galleryManager.CreateThumbnail(datasetExplorer.CurrentThumbnail.ImagePath);
|
await galleryManager.CreateThumbnail(datasetExplorer.CurrentThumbnail.ImagePath);
|
||||||
|
datasetExplorer.CurrentThumbnail.UpdateImage();
|
||||||
datasetExplorer.Switcher.SelectedIndex = 0;
|
datasetExplorer.Switcher.SelectedIndex = 0;
|
||||||
break;
|
break;
|
||||||
case PlaybackControlEnum.RemoveSelectedAnns:
|
case PlaybackControlEnum.RemoveSelectedAnns:
|
||||||
@@ -57,6 +61,9 @@ public class DatasetExplorerEventHandler(DatasetExplorer datasetExplorer,
|
|||||||
case PlaybackControlEnum.RemoveAllAnns:
|
case PlaybackControlEnum.RemoveAllAnns:
|
||||||
datasetExplorer.ExplorerEditor.RemoveAllAnns();
|
datasetExplorer.ExplorerEditor.RemoveAllAnns();
|
||||||
break;
|
break;
|
||||||
|
case PlaybackControlEnum.Close:
|
||||||
|
datasetExplorer.Switcher.SelectedIndex = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
|
||||||
|
namespace Azaion.Annotator.Extensions;
|
||||||
|
|
||||||
|
public static class BitmapExtensions
|
||||||
|
{
|
||||||
|
public static async Task<BitmapImage> OpenImage(this string imagePath)
|
||||||
|
{
|
||||||
|
var image = new BitmapImage();
|
||||||
|
await using var stream = File.OpenRead(imagePath);
|
||||||
|
image.BeginInit();
|
||||||
|
image.CacheOption = BitmapCacheOption.OnLoad;
|
||||||
|
image.StreamSource = stream;
|
||||||
|
image.EndInit();
|
||||||
|
image.Freeze();
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user