Files
annotations/Azaion.Dataset/DatasetExplorer.xaml.cs
T
Alex Bezdieniezhnykh a5fcb0988b fixed sorting in datasetexplorer, also show date
make annotationstatus more clear
2025-05-20 11:02:24 +03:00

323 lines
11 KiB
C#

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Azaion.Common.Database;
using Azaion.Common.DTO;
using Azaion.Common.DTO.Config;
using Azaion.Common.Events;
using Azaion.Common.Services;
using Azaion.CommonSecurity.DTO;
using Azaion.CommonSecurity.Services;
using LinqToDB;
using MediatR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ScottPlot;
using Color = ScottPlot.Color;
namespace Azaion.Dataset;
public partial class DatasetExplorer
{
private readonly ILogger<DatasetExplorer> _logger;
private readonly AnnotationConfig _annotationConfig;
private readonly DirectoriesConfig _directoriesConfig;
private readonly Dictionary<int, Dictionary<string, Annotation>> _annotationsDict;
private readonly CancellationTokenSource _cts = new();
public List<DetectionClass> AllDetectionClasses { get; set; }
public ObservableCollection<AnnotationThumbnail> SelectedAnnotations { get; set; } = new();
public readonly Dictionary<string, AnnotationThumbnail> SelectedAnnotationDict = new();
private int _tempSelectedClassIdx = 0;
private readonly IGalleryService _galleryService;
private readonly IDbFactory _dbFactory;
private readonly IMediator _mediator;
public readonly List<DetectionClass> AnnotationsClasses;
private IAzaionApi _azaionApi;
public bool ThumbnailLoading { get; set; }
public AnnotationThumbnail? CurrentAnnotation { get; set; }
public DatasetExplorer(
IOptions<DirectoriesConfig> directoriesConfig,
IOptions<AnnotationConfig> annotationConfig,
ILogger<DatasetExplorer> logger,
IGalleryService galleryService,
FormState formState,
IDbFactory dbFactory,
IMediator mediator,
IAzaionApi azaionApi)
{
InitializeComponent();
_directoriesConfig = directoriesConfig.Value;
_annotationConfig = annotationConfig.Value;
_logger = logger;
_galleryService = galleryService;
_dbFactory = dbFactory;
_mediator = mediator;
_azaionApi = azaionApi;
var photoModes = Enum.GetValues(typeof(PhotoMode)).Cast<PhotoMode>().ToList();
_annotationsDict = _annotationConfig.DetectionClasses.SelectMany(cls => photoModes.Select(mode => (int)mode + cls.Id))
.ToDictionary(x => x, _ => new Dictionary<string, Annotation>());
_annotationsDict.Add(-1, []);
AnnotationsClasses = annotationConfig.Value.DetectionClasses;
Loaded += OnLoaded;
Activated += (_, _) => formState.ActiveWindow = WindowEnum.DatasetExplorer;
ThumbnailsView.KeyDown += async (sender, args) =>
{
switch (args.Key)
{
case Key.Delete:
await DeleteAnnotations();
break;
case Key.Enter:
await EditAnnotation(ThumbnailsView.SelectedIndex);
break;
}
};
ThumbnailsView.MouseDoubleClick += async (_, _) => await EditAnnotation(ThumbnailsView.SelectedIndex);
ThumbnailsView.SelectionChanged += (_, _) =>
{
StatusText.Text = $"Обрано: {ThumbnailsView.SelectedItems.Count} | {ThumbnailsView.SelectedIndex} / {SelectedAnnotations.Count}";
ValidateBtn.Visibility = ThumbnailsView.SelectedItems.Cast<AnnotationThumbnail>().Any(x => x.IsSeed)
? Visibility.Visible
: Visibility.Hidden;
};
ExplorerEditor.GetTimeFunc = () => CurrentAnnotation!.Annotation.Time;
_galleryService.ThumbnailsUpdate += thumbnailsPercentage =>
{
Dispatcher.Invoke(() => RefreshThumbBar.Value = thumbnailsPercentage);
};
Closing += (_, _) => _cts.Cancel();
AllDetectionClasses = new List<DetectionClass>(
new List<DetectionClass> { new() {Id = -1, Name = "All", ShortName = "All"}}
.Concat(_annotationConfig.DetectionClasses));
LvClasses.Init(AllDetectionClasses);
_dbFactory.Run(db =>
{
var allAnnotations = db.Annotations
.LoadWith(x => x.Detections)
.OrderBy(x => x.AnnotationStatus)
.ThenByDescending(x => x.CreatedDate)
.ToList();
foreach (var annotation in allAnnotations)
AddAnnotationToDict(annotation);
return null!;
});
DataContext = this;
}
private async void OnLoaded(object sender, RoutedEventArgs e)
{
LvClasses.DetectionClassChanged += async (_, args) =>
{
ExplorerEditor.CurrentAnnClass = args.DetectionClass;
if (Switcher.SelectedIndex == 0)
await ReloadThumbnails();
else
foreach (var ann in ExplorerEditor.CurrentDetections.Where(x => x.IsSelected))
ann.DetectionClass = args.DetectionClass;
};
ExplorerEditor.CurrentAnnClass = LvClasses.CurrentDetectionClass ?? _annotationConfig.DetectionClasses.First();
await ReloadThumbnails();
await LoadClassDistribution();
DataContext = this;
}
public void AddAnnotationToDict(Annotation annotation)
{
foreach (var c in annotation.Classes)
_annotationsDict[c][annotation.Name] = annotation;
_annotationsDict[-1][annotation.Name] = annotation;
}
private async Task LoadClassDistribution()
{
var data = _annotationsDict
.Where(x => x.Key != -1)
.Select(gr => new
{
gr.Key,
_annotationConfig.DetectionClassesDict[gr.Key].ShortName,
_annotationConfig.DetectionClassesDict[gr.Key].Color,
ClassCount = gr.Value.Count
})
.ToList();
var foregroundColor = Color.FromColor(System.Drawing.Color.Black);
var bars = data.Select(x => new Bar
{
Orientation = Orientation.Horizontal,
Position = -1.5 * x.Key + 1,
Label = x.ClassCount > 200 ? x.ClassCount.ToString() : "",
FillColor = new Color(x.Color.R, x.Color.G, x.Color.B, x.Color.A),
Value = x.ClassCount,
CenterLabel = true,
LabelOffset = 10
}).ToList();
ClassDistribution.Plot.Add.Bars(bars);
foreach (var x in data)
{
var label = ClassDistribution.Plot.Add.Text(x.ShortName, 50, -1.5 * x.Key + 1.1);
label.LabelFontColor = foregroundColor;
label.LabelFontSize = 18;
}
ClassDistribution.Plot.Axes.AutoScale();
ClassDistribution.Plot.HideAxesAndGrid();
ClassDistribution.Plot.FigureBackground.Color = new("#888888");
ClassDistribution.Refresh();
await Task.CompletedTask;
}
private async void RefreshThumbnailsBtnClick(object sender, RoutedEventArgs e)
{
RefreshThumbnailsButtonItem.Visibility = Visibility.Hidden;
RefreshProgressBarItem.Visibility = Visibility.Visible;
var result = MessageBox.Show($"Видалити всі іконки та згенерувати нову базу іконок в {_directoriesConfig.ThumbnailsDirectory}?",
"Підтвердження оновлення іконок", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
return;
await _galleryService.ClearThumbnails();
await _galleryService.RefreshThumbnails();
RefreshProgressBarItem.Visibility = Visibility.Hidden;
RefreshThumbnailsButtonItem.Visibility = Visibility.Visible;
}
public async Task EditAnnotation(int index)
{
try
{
ThumbnailLoading = true;
if (index == -1)
return;
CurrentAnnotation = (ThumbnailsView.Items[index] as AnnotationThumbnail)!;
ThumbnailsView.SelectedIndex = index;
var ann = CurrentAnnotation.Annotation;
ExplorerEditor.Background = new ImageBrush
{
ImageSource = await ann.ImagePath.OpenImage()
};
SwitchTab(toEditor: true);
var time = ann.Time;
ExplorerEditor.RemoveAllAnns();
ExplorerEditor.CreateDetections(time, ann.Detections, _annotationConfig.DetectionClasses, ExplorerEditor.RenderSize);
}
catch (Exception e)
{
_logger.LogError(e, e.Message);
throw;
}
finally
{
_ = Task.Run(async () =>
{
await Task.Delay(100);
ThumbnailLoading = false;
});
}
}
public void SwitchTab(bool toEditor)
{
if (toEditor)
{
AnnotationsTab.Visibility = Visibility.Collapsed;
EditorTab.Visibility = Visibility.Visible;
_tempSelectedClassIdx = LvClasses.CurrentClassNumber;
LvClasses.DetectionDataGrid.ItemsSource = _annotationConfig.DetectionClasses;
Switcher.SelectedIndex = 1;
LvClasses.SelectNum(Math.Max(0, _tempSelectedClassIdx - 1));
}
else
{
AnnotationsTab.Visibility = Visibility.Visible;
EditorTab.Visibility = Visibility.Collapsed;
LvClasses.DetectionDataGrid.ItemsSource = AllDetectionClasses;
LvClasses.SelectNum(_tempSelectedClassIdx);
Switcher.SelectedIndex = 0;
}
}
public async Task DeleteAnnotations()
{
var tempSelected = ThumbnailsView.SelectedIndex;
var result = MessageBox.Show("Чи дійсно видалити аннотації?","Підтвердження видалення", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
return;
var annotationNames = ThumbnailsView.SelectedItems.Cast<AnnotationThumbnail>().Select(x => x.Annotation.Name).ToList();
await _mediator.Publish(new AnnotationsDeletedEvent(annotationNames));
ThumbnailsView.SelectedIndex = Math.Min(SelectedAnnotations.Count, tempSelected);
}
private async Task ReloadThumbnails()
{
SelectedAnnotations.Clear();
SelectedAnnotationDict.Clear();
var annThumbnails = _annotationsDict[ExplorerEditor.CurrentAnnClass.YoloId]
.Select(x => new AnnotationThumbnail(x.Value, _azaionApi.CurrentUser.Role.IsValidator()))
.OrderBy(x => !x.IsSeed)
.ThenByDescending(x =>x.Annotation.CreatedDate);
//var dict = annThumbnails.Take(20).ToDictionary(x => x.Annotation.Name, x => x.IsSeed);
foreach (var thumb in annThumbnails)
{
SelectedAnnotations.Add(thumb);
SelectedAnnotationDict.Add(thumb.Annotation.Name, thumb);
}
await Task.CompletedTask;
}
private async void ValidateAnnotationsClick(object sender, RoutedEventArgs e)
{
var result = MessageBox.Show("Підтверджуєте валідність обраних аннотацій?","Підтвердження валідності", MessageBoxButton.OKCancel, MessageBoxImage.Question);
if (result != MessageBoxResult.OK)
return;
try
{
await _mediator.Publish(new DatasetExplorerControlEvent(PlaybackControlEnum.ValidateAnnotations), _cts.Token);
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
}
}
}