small fixes, renames

This commit is contained in:
Alex Bezdieniezhnykh
2025-01-15 16:41:42 +02:00
parent ae2c62350a
commit 1bc1d81fde
16 changed files with 234 additions and 181 deletions
+45 -50
View File
@@ -28,7 +28,7 @@ using MediaPlayer = LibVLCSharp.Shared.MediaPlayer;
namespace Azaion.Annotator;
public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
public partial class Annotator
{
private readonly AppConfig _appConfig;
private readonly LibVLC _libVLC;
@@ -52,8 +52,8 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
private readonly TimeSpan _thresholdAfter = TimeSpan.FromMilliseconds(300);
private ObservableCollection<MediaFileInfo> AllMediaFiles { get; set; } = new();
private ObservableCollection<MediaFileInfo> FilteredMediaFiles { get; set; } = new();
public ObservableCollection<MediaFileInfo> AllMediaFiles { get; set; } = new();
public ObservableCollection<MediaFileInfo> FilteredMediaFiles { get; set; } = new();
public IntervalTree<TimeSpan, Annotation> TimedAnnotations { get; set; } = new();
private AutodetectDialog _autoDetectDialog = new() { Topmost = true };
@@ -96,7 +96,7 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
try
{
_appConfig.DirectoriesConfig.VideosDirectory = TbFolder.Text;
ReloadFiles();
await ReloadFiles();
await SaveUserSettings();
}
catch (Exception e)
@@ -335,24 +335,12 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
_formState.AnnotationResults.Insert(index, new AnnotationResult(_appConfig.AnnotationConfig.DetectionClassesDict, annotation));
}
private void ReloadFiles()
private async Task ReloadFiles()
{
var dir = new DirectoryInfo(_appConfig.DirectoriesConfig.VideosDirectory);
if (!dir.Exists)
return;
var labelNames = new DirectoryInfo(_appConfig.DirectoriesConfig.LabelsDirectory).GetFiles()
.Select(x =>
{
var name = Path.GetFileNameWithoutExtension(x.Name);
return name.Length > 8
? name[..^7]
: name;
})
.GroupBy(x => x)
.Select(gr => gr.Key)
.ToDictionary(x => x);
var videoFiles = dir.GetFiles(_appConfig.AnnotationConfig.VideoFormats.ToArray()).Select(x =>
{
using var media = new Media(_libVLC, x.FullName);
@@ -361,22 +349,32 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
{
Name = x.Name,
Path = x.FullName,
MediaType = MediaTypes.Video,
HasAnnotations = labelNames.ContainsKey(Path.GetFileNameWithoutExtension(x.Name).Replace(" ", ""))
MediaType = MediaTypes.Video
};
media.ParsedChanged += (_, _) => fInfo.Duration = TimeSpan.FromMilliseconds(media.Duration);
return fInfo;
}).ToList();
var imageFiles = dir.GetFiles(_appConfig.AnnotationConfig.ImageFormats.ToArray()).Select(x => new MediaFileInfo
var imageFiles = dir.GetFiles(_appConfig.AnnotationConfig.ImageFormats.ToArray())
.Select(x => new MediaFileInfo
{
Name = x.Name,
Path = x.FullName,
MediaType = MediaTypes.Image,
HasAnnotations = labelNames.ContainsKey(Path.GetFileNameWithoutExtension(x.Name).Replace(" ", ""))
MediaType = MediaTypes.Image
});
var allFiles = videoFiles.Concat(imageFiles).ToList();
AllMediaFiles = new ObservableCollection<MediaFileInfo>(videoFiles.Concat(imageFiles).ToList());
var allFileNames = allFiles.Select(x => x.FName).ToList();
var labelsDict = await _dbFactory.Run(async db => await db.Annotations
.GroupBy(x => x.Name.Substring(0, x.Name.Length - 7))
.Where(x => allFileNames.Contains(x.Key))
.ToDictionaryAsync(x => x.Key, x => x.Key));
foreach (var mediaFile in allFiles)
mediaFile.HasAnnotations = labelsDict.ContainsKey(mediaFile.FName);
AllMediaFiles = new ObservableCollection<MediaFileInfo>(allFiles);
LvFiles.ItemsSource = AllMediaFiles;
BlinkHelp(AllMediaFiles.Count == 0
@@ -406,6 +404,8 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
_mediaPlayer.SetPause(true);
_mediaPlayer.Time = timeMilliseconds;
VideoSlider.Value = _mediaPlayer.Position * 100;
StatusClock.Text = $"{TimeSpan.FromMilliseconds(_mediaPlayer.Time):mm\\:ss} / {_formState.CurrentVideoLength:mm\\:ss}";
}
private void SeekTo(TimeSpan time) =>
@@ -435,7 +435,7 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
_appConfig.DirectoriesConfig.VideosDirectory = dlg.FileName;
TbFolder.Text = dlg.FileName;
ReloadFiles();
await ReloadFiles();
await SaveUserSettings();
}
@@ -443,6 +443,7 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
{
FilteredMediaFiles = new ObservableCollection<MediaFileInfo>(AllMediaFiles.Where(x => x.Name.ToLower().Contains(TbFilter.Text.ToLower())).ToList());
LvFiles.ItemsSource = FilteredMediaFiles;
LvFiles.ItemsSource = FilteredMediaFiles;
}
private void PlayClick(object sender, RoutedEventArgs e)
@@ -487,7 +488,7 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
LvFiles.SelectedIndex = 0;
await _mediator.Publish(new AnnotatorControlEvent(PlaybackControlEnum.Play));
_mediaPlayer.SetPause(true);
_mediaPlayer.Stop();
var manualCancellationSource = new CancellationTokenSource();
var token = manualCancellationSource.Token;
@@ -573,26 +574,32 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
{
var fName = _formState.GetTimeName(timeframe.Time);
var detections = await _aiDetector.Detect(fName, timeframe.Stream, token);
var isValid = IsValidDetection(timeframe.Time, detections);
if (timeframe.Time.TotalSeconds > prevSeekTime + 1)
var isValid = IsValidDetection(timeframe.Time, detections);
Console.WriteLine($"Detection time: {timeframe.Time}");
var log = string.Join(Environment.NewLine, detections.Select(det =>
$"{_appConfig.AnnotationConfig.DetectionClassesDict[det.ClassNumber].Name}: " +
$"xy=({det.CenterX:F2},{det.CenterY:F2}), " +
$"size=({det.Width:F2}, {det.Height:F2}), " +
$"prob: {det.Probability:F1}%"));
log = $"Detection time: {timeframe.Time}, Valid: {isValid}. {Environment.NewLine} {log}";
Dispatcher.Invoke(() => _autoDetectDialog.Log(log));
if (timeframe.Time.TotalMilliseconds > prevSeekTime + 250)
{
Dispatcher.Invoke(() => SeekTo(timeframe.Time));
prevSeekTime = timeframe.Time.TotalSeconds;
prevSeekTime = timeframe.Time.TotalMilliseconds;
if (!isValid) //Show frame anyway
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
timeframe.Stream.Seek(0, SeekOrigin.Begin);
bitmap.StreamSource = timeframe.Stream;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
Dispatcher.Invoke(() =>
{
Editor.RemoveAllAnns();
Editor.Background = new ImageBrush { ImageSource = bitmap };
Editor.Background = new ImageBrush
{
ImageSource = timeframe.Stream.OpenImage()
};
});
}
}
@@ -602,6 +609,7 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
mediaInfo.HasAnnotations = true;
await ProcessDetection(timeframe, ".jpg", detections, token);
await timeframe.Stream.DisposeAsync();
}
catch (Exception ex)
{
@@ -665,7 +673,6 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
{
try
{
var time = timeframe.Time;
var fName = _formState.GetTimeName(timeframe.Time);
var annotation = await _annotationService.SaveAnnotation(fName, imageExtension, detections, SourceEnum.AI, timeframe.Stream, token);
@@ -682,8 +689,6 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
$"prob: {det.Probability:F1}%"));
Dispatcher.Invoke(() => _autoDetectDialog.Log(log));
}
catch (Exception e)
{
@@ -691,14 +696,4 @@ public partial class Annotator : INotificationHandler<AnnotationsDeletedEvent>
}
});
}
public async Task Handle(AnnotationsDeletedEvent notification, CancellationToken cancellationToken)
{
var annResDict = _formState.AnnotationResults.ToDictionary(x => x.Annotation.Name, x => x);
foreach (var ann in notification.Annotations)
{
_formState.AnnotationResults.Remove(annResDict[ann.Name]);
TimedAnnotations.Remove(ann);
}
}
}
+26 -1
View File
@@ -28,7 +28,8 @@ public class AnnotatorEventHandler(
INotificationHandler<KeyEvent>,
INotificationHandler<AnnClassSelectedEvent>,
INotificationHandler<AnnotatorControlEvent>,
INotificationHandler<VolumeChangedEvent>
INotificationHandler<VolumeChangedEvent>,
INotificationHandler<AnnotationsDeletedEvent>
{
private const int STEP = 20;
private const int LARGE_STEP = 5000;
@@ -269,4 +270,28 @@ public class AnnotatorEventHandler(
var annotation = await annotationService.SaveAnnotation(fName, imageExtension, currentDetections, SourceEnum.Manual, token: cancellationToken);
mainWindow.AddAnnotation(annotation);
}
public async Task Handle(AnnotationsDeletedEvent notification, CancellationToken cancellationToken)
{
var annResDict = formState.AnnotationResults.ToDictionary(x => x.Annotation.Name, x => x);
foreach (var ann in notification.Annotations)
{
if (!annResDict.TryGetValue(ann.Name, out var value))
continue;
formState.AnnotationResults.Remove(value);
mainWindow.TimedAnnotations.Remove(ann);
}
if (formState.AnnotationResults.Count == 0)
{
var media = mainWindow.AllMediaFiles.FirstOrDefault(x => x.Name == formState.CurrentMedia?.Name);
if (media != null)
{
media.HasAnnotations = false;
mainWindow.LvFiles.Items.Refresh();
}
}
}
}
@@ -73,7 +73,7 @@ public class VLCFrameExtractor(LibVLC libVLC, IOptions<AIRecognitionConfig> conf
using var outputImage = surface.Snapshot();
using var data = outputImage.Encode(SKEncodedImageFormat.Jpeg, 85);
using var ms = new MemoryStream();
var ms = new MemoryStream();
data.SaveTo(ms);
yield return (frameInfo.Time, ms);
+4 -6
View File
@@ -1,9 +1,8 @@
using System.IO;
using Azaion.Annotator.DTO;
using System.Diagnostics;
using System.IO;
using Azaion.Annotator.Extensions;
using Azaion.Common.DTO;
using Azaion.Common.DTO.Config;
using Azaion.Common.Services;
using Azaion.CommonSecurity.Services;
using Compunet.YoloV8;
using Microsoft.Extensions.Options;
@@ -34,11 +33,10 @@ public class YOLODetector(IOptions<AIRecognitionConfig> recognitionConfig, IReso
}
imageStream.Seek(0, SeekOrigin.Begin);
var image = Image.Load<Rgb24>(imageStream);
using var image = Image.Load<Rgb24>(imageStream);
var result = await _predictor.DetectAsync(image);
var imageSize = new System.Windows.Size(image.Width, image.Height);
var detections = result.Select(d =>
{
var label = new YoloLabel(new CanvasLabel(d.Name.Id, d.Bounds.X, d.Bounds.Y, d.Bounds.Width, d.Bounds.Height), imageSize, imageSize);
+1 -4
View File
@@ -1,5 +1,4 @@
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
namespace Azaion.Common.DTO;
@@ -7,9 +6,7 @@ namespace Azaion.Common.DTO;
public class FormState
{
public MediaFileInfo? CurrentMedia { get; set; }
public string VideoName => string.IsNullOrEmpty(CurrentMedia?.Name)
? ""
: Path.GetFileNameWithoutExtension(CurrentMedia.Name).Replace(" ", "");
public string VideoName => CurrentMedia?.FName ?? "";
public string CurrentMrl { get; set; } = null!;
public Size CurrentVideoSize { get; set; }
+2
View File
@@ -8,4 +8,6 @@ public class MediaFileInfo
public string DurationStr => $"{Duration:h\\:mm\\:ss}";
public bool HasAnnotations { get; set; }
public MediaTypes MediaType { get; set; }
public string FName => System.IO.Path.GetFileNameWithoutExtension(Name).Replace(" ", "");
}
+3
View File
@@ -32,10 +32,12 @@ public class Annotation
public double Lat { get; set; }
public double Lon { get; set; }
#region Calculated
public List<int> Classes => Detections.Select(x => x.ClassNumber).ToList();
public string ImagePath => Path.Combine(_imagesDir, $"{Name}{ImageExtension}");
public string LabelPath => Path.Combine(_labelsDir, $"{Name}.txt");
public string ThumbPath => Path.Combine(_thumbDir, $"{Name}{Constants.THUMBNAIL_PREFIX}.jpg");
public string OriginalMediaName => $"{Name[..^7]}";
private TimeSpan? _time;
public TimeSpan Time
@@ -60,6 +62,7 @@ public class Annotation
return _time.Value;
}
}
#endregion Calculated
}
+7 -3
View File
@@ -41,8 +41,8 @@ public class DbFactory : IDbFactory
_memoryDataOptions = new DataOptions()
.UseDataProvider(SQLiteTools.GetDataProvider())
.UseConnection(_memoryConnection)
.UseMappingSchema(AnnotationsDbSchemaHolder.MappingSchema);
//.UseTracing(TraceLevel.Info, t => logger.LogInformation(t.SqlText));
.UseMappingSchema(AnnotationsDbSchemaHolder.MappingSchema)
;//.UseTracing(TraceLevel.Info, t => logger.LogInformation(t.SqlText));
_fileConnection = new SQLiteConnection(FileConnStr);
@@ -50,7 +50,6 @@ public class DbFactory : IDbFactory
.UseDataProvider(SQLiteTools.GetDataProvider())
.UseConnection(_fileConnection)
.UseMappingSchema(AnnotationsDbSchemaHolder.MappingSchema);
_ = _fileDataOptions.UseTracing(TraceLevel.Info, t => logger.LogInformation(t.SqlText));
if (!File.Exists(_annConfig.AnnotationsDbFile))
CreateDb();
@@ -122,6 +121,11 @@ public static class AnnotationsDbSchemaHolder
.HasTableName(Constants.ANNOTATIONS_TABLENAME)
.HasPrimaryKey(x => x.Name)
.Ignore(x => x.Time)
.Ignore(x => x.Classes)
.Ignore(x => x.ImagePath)
.Ignore(x => x.LabelPath)
.Ignore(x => x.ThumbPath)
.Ignore(x => x.OriginalMediaName)
.Association(a => a.Detections, (a, d) => a.Name == d.AnnotationName);
builder.Entity<Detection>()
+7 -1
View File
@@ -7,8 +7,14 @@ public static class BitmapExtensions
{
public static async Task<BitmapImage> OpenImage(this string imagePath)
{
var image = new BitmapImage();
await using var stream = File.OpenRead(imagePath);
return OpenImage(stream);
}
public static BitmapImage OpenImage(this Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
+22 -20
View File
@@ -67,7 +67,22 @@ public class AnnotationService : INotificationHandler<AnnotationsDeletedEvent>
OffsetSpec = new OffsetTypeOffset(offset + 1),
MessageHandler = async (stream, consumer, context, message) =>
{
await Consume(MessagePackSerializer.Deserialize<AnnotationCreatedMessage>(message.Data.Contents), cancellationToken);
var msg = MessagePackSerializer.Deserialize<AnnotationCreatedMessage>(message.Data.Contents);
if (msg.CreatedRole != RoleEnum.Operator) //Process only operator's messages
return;
await SaveAnnotationInner(
msg.CreatedDate,
msg.Name,
msg.ImageExtension,
JsonConvert.DeserializeObject<List<Detection>>(msg.Detections) ?? [],
msg.Source,
new MemoryStream(msg.Image),
msg.CreatedRole,
msg.CreatedEmail,
generateThumbnail: true,
cancellationToken);
await _dbFactory.Run(async db => await db.QueueOffsets
.Where(x => x.QueueName == Constants.MQ_ANNOTATIONS_QUEUE)
.Set(x => x.Offset, context.Offset)
@@ -92,24 +107,11 @@ public class AnnotationService : INotificationHandler<AnnotationsDeletedEvent>
await SaveAnnotationInner(DateTime.UtcNow, annotation.Name, annotation.ImageExtension, annotation.Detections.ToList(), SourceEnum.Manual, null, _apiClient.User.Role, _apiClient.User.Email,
generateThumbnail: false, token);
//Queue (only from operators)
public async Task Consume(AnnotationCreatedMessage message, CancellationToken cancellationToken = default)
{
if (message.CreatedRole != RoleEnum.Operator) //Process only operator's messages
return;
await SaveAnnotationInner(
message.CreatedDate,
message.Name,
message.ImageExtension,
JsonConvert.DeserializeObject<List<Detection>>(message.Detections) ?? [],
message.Source,
new MemoryStream(message.Image),
message.CreatedRole,
message.CreatedEmail,
generateThumbnail: true,
cancellationToken);
}
// //Queue (only from operators)
// public async Task Consume(AnnotationCreatedMessage message, CancellationToken cancellationToken = default)
// {
//
// }
private async Task<Annotation> SaveAnnotationInner(DateTime createdDate, string fName, string imageExtension, List<Detection> detections, SourceEnum source, Stream? stream,
RoleEnum userRole,
@@ -168,7 +170,7 @@ public class AnnotationService : INotificationHandler<AnnotationsDeletedEvent>
if (generateThumbnail)
await _galleryService.CreateThumbnail(annotation, token);
await _producer.SendToQueue(annotation, token);
await _producer.SendToInnerQueue(annotation, token);
await _mediator.Publish(new AnnotationCreatedEvent(annotation), token);
await ThrottleExt.ThrottleRunAfter(() =>
+3 -3
View File
@@ -53,7 +53,7 @@ public class FailsafeAnnotationsProducer
await Init(cancellationToken);
while (!cancellationToken.IsCancellationRequested)
{
var messages = await GetFromQueue(cancellationToken);
var messages = await GetFromInnerQueue(cancellationToken);
foreach (var messagesChunk in messages.Chunk(10)) //Sending by 10
{
var sent = false;
@@ -91,7 +91,7 @@ public class FailsafeAnnotationsProducer
}
}
private async Task<List<AnnotationCreatedMessage>> GetFromQueue(CancellationToken cancellationToken = default)
private async Task<List<AnnotationCreatedMessage>> GetFromInnerQueue(CancellationToken cancellationToken = default)
{
return await _dbFactory.Run(async db =>
{
@@ -124,7 +124,7 @@ public class FailsafeAnnotationsProducer
});
}
public async Task SendToQueue(Annotation annotation, CancellationToken cancellationToken = default)
public async Task SendToInnerQueue(Annotation annotation, CancellationToken cancellationToken = default)
{
await _dbFactory.Run(async db =>
await db.InsertAsync(new AnnotationName { Name = annotation.Name }, token: cancellationToken));
@@ -1,47 +1,14 @@
using System.Reflection;
using Azaion.CommonSecurity.DTO;
using Azaion.CommonSecurity.DTO;
namespace Azaion.CommonSecurity.Services;
public interface IResourceLoader
{
Task<MemoryStream> Load(string fileName, CancellationToken cancellationToken = default);
Assembly? LoadAssembly(string asmName);
}
public class ResourceLoader(AzaionApiClient api, ApiCredentials credentials) : IResourceLoader
{
private static readonly List<string> EncryptedResources =
[
"Azaion.Annotator",
"Azaion.Dataset"
];
public Assembly? LoadAssembly(string resourceName)
{
var assemblyName = resourceName.Split(',').First();
if (EncryptedResources.Contains(assemblyName))
{
try
{
var stream = Load($"{assemblyName}.dll").GetAwaiter().GetResult();
return Assembly.Load(stream.ToArray());
}
catch (Exception e)
{
Console.WriteLine(e);
var currentLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
var dllPath = Path.Combine(currentLocation, SecurityConstants.DUMMY_DIR, $"{assemblyName}.dll");
return Assembly.LoadFile(dllPath);
}
}
var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => a.GetName().Name == assemblyName);
return loadedAssembly;
}
public async Task<MemoryStream> Load(string fileName, CancellationToken cancellationToken = default)
{
var hardwareService = new HardwareService();
+5 -37
View File
@@ -16,7 +16,7 @@ using Color = ScottPlot.Color;
namespace Azaion.Dataset;
public partial class DatasetExplorer : INotificationHandler<AnnotationCreatedEvent>, INotificationHandler<AnnotationsDeletedEvent>
public partial class DatasetExplorer
{
private readonly ILogger<DatasetExplorer> _logger;
private readonly AnnotationConfig _annotationConfig;
@@ -27,13 +27,13 @@ public partial class DatasetExplorer : INotificationHandler<AnnotationCreatedEve
public ObservableCollection<DetectionClass> AllDetectionClasses { get; set; } = new();
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;
private readonly Dictionary<string, AnnotationThumbnail> _selectedAnnotationDict = new();
public bool ThumbnailLoading { get; set; }
@@ -144,7 +144,7 @@ public partial class DatasetExplorer : INotificationHandler<AnnotationCreatedEve
DataContext = this;
}
private void AddAnnotationToDict(Annotation annotation)
public void AddAnnotationToDict(Annotation annotation)
{
foreach (var c in annotation.Classes)
_annotationsDict[c].Add(annotation);
@@ -292,39 +292,7 @@ public partial class DatasetExplorer : INotificationHandler<AnnotationCreatedEve
{
var annThumb = new AnnotationThumbnail(ann);
SelectedAnnotations.Add(annThumb);
_selectedAnnotationDict.Add(annThumb.Annotation.Name, annThumb);
}
}
public async Task Handle(AnnotationCreatedEvent notification, CancellationToken cancellationToken)
{
var annotation = notification.Annotation;
var selectedClass = ((DetectionClass?)LvClasses.SelectedItem)?.Id;
if (selectedClass == null)
return;
//TODO: For editing existing need to handle updates
AddAnnotationToDict(annotation);
if (annotation.Classes.Contains(selectedClass.Value))
{
var annThumb = new AnnotationThumbnail(annotation);
SelectedAnnotations.Add(annThumb);
_selectedAnnotationDict.Add(annThumb.Annotation.Name, annThumb);
}
}
public async Task Handle(AnnotationsDeletedEvent notification, CancellationToken cancellationToken)
{
var names = notification.Annotations.Select(x => x.Name).ToList();
var annThumbs = _selectedAnnotationDict
.Where(x => names.Contains(x.Key))
.Select(x => x.Value)
.ToList();
foreach (var annThumb in annThumbs)
{
SelectedAnnotations.Remove(annThumb);
_selectedAnnotationDict.Remove(annThumb.Annotation.Name);
SelectedAnnotationDict.Add(annThumb.Annotation.Name, annThumb);
}
}
+36 -3
View File
@@ -10,9 +10,11 @@ namespace Azaion.Dataset;
public class DatasetExplorerEventHandler(
DatasetExplorer datasetExplorer,
AnnotationService annotationService)
: INotificationHandler<KeyEvent>,
INotificationHandler<DatasetExplorerControlEvent>
AnnotationService annotationService) :
INotificationHandler<KeyEvent>,
INotificationHandler<DatasetExplorerControlEvent>,
INotificationHandler<AnnotationCreatedEvent>,
INotificationHandler<AnnotationsDeletedEvent>
{
private readonly Dictionary<Key, PlaybackControlEnum> _keysControlEnumDict = new()
{
@@ -83,4 +85,35 @@ public class DatasetExplorerEventHandler(
break;
}
}
public async Task Handle(AnnotationCreatedEvent notification, CancellationToken cancellationToken)
{
var annotation = notification.Annotation;
var selectedClass = ((DetectionClass?)datasetExplorer.LvClasses.SelectedItem)?.Id;
if (selectedClass == null)
return;
//TODO: For editing existing need to handle updates
datasetExplorer.AddAnnotationToDict(annotation);
if (annotation.Classes.Contains(selectedClass.Value))
{
var annThumb = new AnnotationThumbnail(annotation);
datasetExplorer.SelectedAnnotations.Add(annThumb);
datasetExplorer.SelectedAnnotationDict.Add(annThumb.Annotation.Name, annThumb);
}
}
public async Task Handle(AnnotationsDeletedEvent notification, CancellationToken cancellationToken)
{
var names = notification.Annotations.Select(x => x.Name).ToList();
var annThumbs = datasetExplorer.SelectedAnnotationDict
.Where(x => names.Contains(x.Key))
.Select(x => x.Value)
.ToList();
foreach (var annThumb in annThumbs)
{
datasetExplorer.SelectedAnnotations.Remove(annThumb);
datasetExplorer.SelectedAnnotationDict.Remove(annThumb.Annotation.Name);
}
}
}
+33 -2
View File
@@ -1,4 +1,5 @@
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Threading;
using Azaion.Annotator;
@@ -48,6 +49,12 @@ public partial class App
StartLogin();
}
private readonly List<string> _encryptedResources =
[
"Azaion.Annotator",
"Azaion.Dataset"
];
private void StartLogin()
{
new ConfigUpdater().CheckConfig();
@@ -57,7 +64,30 @@ public partial class App
_apiClient = AzaionApiClient.Create(args);
_resourceLoader = new ResourceLoader(_apiClient, args);
_securedConfig = await _resourceLoader.Load("secured-config.json");
AppDomain.CurrentDomain.AssemblyResolve += (_, a) => _resourceLoader.LoadAssembly(a.Name);
AppDomain.CurrentDomain.AssemblyResolve += (_, a) =>
{
var assemblyName = a.Name.Split(',').First();
if (_encryptedResources.Contains(assemblyName))
{
try
{
var stream = _resourceLoader.Load($"{assemblyName}.dll").GetAwaiter().GetResult();
return Assembly.Load(stream.ToArray());
}
catch (Exception e)
{
Console.WriteLine(e);
var currentLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
var dllPath = Path.Combine(currentLocation, SecurityConstants.DUMMY_DIR, $"{assemblyName}.dll");
return Assembly.LoadFile(dllPath);
}
}
var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => a.GetName().Name == assemblyName);
return loadedAssembly;
};
StartMain();
await _host.StartAsync();
@@ -108,7 +138,8 @@ public partial class App
services.AddSingleton<IAIDetector, YOLODetector>();
services.AddMediatR(c => c.RegisterServicesFromAssemblies(
typeof(Annotator.Annotator).Assembly,
typeof(DatasetExplorer).Assembly));
typeof(DatasetExplorer).Assembly,
typeof(AnnotationService).Assembly));
services.AddSingleton<LibVLC>(_ => new LibVLC());
services.AddSingleton<FormState>();
services.AddSingleton<MediaPlayer>(sp =>
+22
View File
@@ -1,4 +1,5 @@
using Azaion.Common.DTO;
using FluentAssertions;
using Xunit;
namespace Azaion.Annotator.Test;
@@ -17,4 +18,25 @@ public class DictTest
new YoloLabel(0, 1, 3, 2, 1)
];
}
[Theory]
[InlineData(null, 0)]
[InlineData(new int[]{}, 0)]
[InlineData(new int[]{1, 2, 5}, 1)]
[InlineData(new int[]{3, -2, 5}, -2)]
[InlineData(new int[]{3, -2, 2, 4}, 2)]
[InlineData(new int[]{3, -2, -100, 2, 4}, 2)]
public void ComputeClosestToZeroTest(int[] ts, int expected) =>
ComputeClosestToZero(ts).Should().Be(expected);
private int ComputeClosestToZero(int[]? ts)
{
if (ts is null || ts.Length == 0)
return 0;
return ts
.OrderBy(Math.Abs)
.ThenByDescending(x => x) // 2 -2 3 4 -10
.FirstOrDefault();
}
}