Files
annotations/Azaion.Dataset/DatasetExplorer.xaml.cs
T
Alex Bezdieniezhnykh d842466594 gps matcher async
put cryptography lib to fixed version
fix race condition bug in queue handler
add lock to db writing and backup to file db on each write
2025-05-29 00:35:35 +03:00

298 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.Extensions;
using Azaion.Common.Services;
using Azaion.CommonSecurity.DTO;
using Azaion.CommonSecurity.Services;
using LinqToDB;
using MediatR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
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);
}
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();
var allAnnotations = await _dbFactory.Run(async db =>
await db.Annotations.LoadWith(x => x.Detections)
.OrderBy(x => x.AnnotationStatus)
.ThenByDescending(x => x.CreatedDate)
.ToListAsync());
foreach (var annotation in allAnnotations)
AddAnnotationToDict(annotation);
await ReloadThumbnails();
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 void LoadClassDistribution()
{
var data = _annotationsDict
.Where(x => x.Key != -1)
.OrderBy(x => x.Key)
.Select(gr => new ClusterDistribution
{
Label = $"{_annotationConfig.DetectionClassesDict[gr.Key].UIName}: {gr.Value.Count}",
Color = _annotationConfig.DetectionClassesDict[gr.Key].Color,
ClassCount = gr.Value.Count
})
.Where(x => x.ClassCount > 0)
.ToList();
var maxClassCount = Math.Max(1, data.Max(x => x.ClassCount));
foreach (var cl in data)
{
cl.Color = cl.Color.CreateTransparent(150);
cl.BarWidth = Math.Clamp(cl.ClassCount / (double)maxClassCount, 0, 1);
}
ClassDistributionPlot.Items = data;
}
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);
}
}
}