diff --git a/Azaion.Annotator/Annotator.xaml b/Azaion.Annotator/Annotator.xaml index bc1b9dc..0eca01d 100644 --- a/Azaion.Annotator/Annotator.xaml +++ b/Azaion.Annotator/Annotator.xaml @@ -148,7 +148,8 @@ Grid.Column="0" Name="LvFiles" Background="Black" - SelectedItem="{Binding Path=SelectedVideo}" Foreground="#FFEEEEEE" + SelectedItem="{Binding Path=SelectedVideo}" + Foreground="#FFDDDDDD" > diff --git a/Azaion.Annotator/AnnotatorEventHandler.cs b/Azaion.Annotator/AnnotatorEventHandler.cs index 3764872..c25920a 100644 --- a/Azaion.Annotator/AnnotatorEventHandler.cs +++ b/Azaion.Annotator/AnnotatorEventHandler.cs @@ -1,7 +1,9 @@ using System.IO; +using System.Reflection.Metadata; using System.Windows; using System.Windows.Input; using Azaion.Annotator.DTO; +using Azaion.Common; using Azaion.Common.DTO; using Azaion.Common.DTO.Config; using Azaion.Common.DTO.Queue; @@ -224,7 +226,8 @@ public class AnnotatorEventHandler( mediaPlayer.Stop(); mainWindow.Title = $"Azaion Annotator - {mediaInfo.Name}"; mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.PauseForAnnotations]); - mediaPlayer.Play(new Media(libVLC, mediaInfo.Path)); + if (formState.CurrentMedia.MediaType == MediaTypes.Video) + mediaPlayer.Play(new Media(libVLC, mediaInfo.Path)); } //SAVE: MANUAL @@ -234,47 +237,44 @@ public class AnnotatorEventHandler( return; var time = formState.BackgroundTime ?? TimeSpan.FromMilliseconds(mediaPlayer.Time); - var fName = formState.VideoName.ToTimeName(time); + var originalMediaName = formState.VideoName; + var fName = originalMediaName.ToTimeName(time); var currentDetections = mainWindow.Editor.CurrentDetections .Select(x => new Detection(fName, x.GetLabel(mainWindow.Editor.RenderSize, formState.BackgroundTime.HasValue ? mainWindow.Editor.RenderSize : formState.CurrentVideoSize))) .ToList(); - formState.CurrentMedia.HasAnnotations = mainWindow.TimedAnnotations.Count != 0; + formState.CurrentMedia.HasAnnotations = currentDetections.Count != 0; mainWindow.LvFiles.Items.Refresh(); mainWindow.Editor.RemoveAllAnns(); var isVideo = formState.CurrentMedia.MediaType == MediaTypes.Video; - var imageExtension = isVideo ? ".jpg" : Path.GetExtension(formState.CurrentMedia.Path); - var imgPath = Path.Combine(dirConfig.Value.ImagesDirectory, $"{fName}{imageExtension}"); + var imgPath = Path.Combine(dirConfig.Value.ImagesDirectory, $"{fName}{Constants.JPG_EXT}"); - if (isVideo) + if (formState.BackgroundTime.HasValue) { - if (formState.BackgroundTime.HasValue) - { - //no need to save image, it's already there, just remove background - mainWindow.Editor.ResetBackground(); - formState.BackgroundTime = null; + //no need to save image, it's already there, just remove background + mainWindow.Editor.ResetBackground(); + formState.BackgroundTime = null; - //next item - var annGrid = mainWindow.DgAnnotations; - annGrid.SelectedIndex = Math.Min(annGrid.Items.Count, annGrid.SelectedIndex + 1); - mainWindow.OpenAnnotationResult((AnnotationResult)annGrid.SelectedItem); - } - else - { - var resultHeight = (uint)Math.Round(RESULT_WIDTH / formState.CurrentVideoSize.Width * formState.CurrentVideoSize.Height); - mediaPlayer.TakeSnapshot(0, imgPath, RESULT_WIDTH, resultHeight); - mediaPlayer.Play(); - } + //next item + var annGrid = mainWindow.DgAnnotations; + annGrid.SelectedIndex = Math.Min(annGrid.Items.Count, annGrid.SelectedIndex + 1); + mainWindow.OpenAnnotationResult((AnnotationResult)annGrid.SelectedItem); } else { - File.Copy(formState.CurrentMedia.Path, imgPath, overwrite: true); - await NextMedia(ct: cancellationToken); + var resultHeight = (uint)Math.Round(RESULT_WIDTH / formState.CurrentVideoSize.Width * formState.CurrentVideoSize.Height); + mediaPlayer.TakeSnapshot(0, imgPath, RESULT_WIDTH, resultHeight); + if (isVideo) + mediaPlayer.Play(); + else + await NextMedia(ct: cancellationToken); } - var annotation = await annotationService.SaveAnnotation(formState.VideoName, time, imageExtension, currentDetections, SourceEnum.Manual, token: cancellationToken); - mainWindow.AddAnnotation(annotation); + + var annotation = await annotationService.SaveAnnotation(originalMediaName, time, currentDetections, SourceEnum.Manual, token: cancellationToken); + if (isVideo) + mainWindow.AddAnnotation(annotation); } public async Task Handle(AnnotationsDeletedEvent notification, CancellationToken cancellationToken) diff --git a/Azaion.Common/Constants.cs b/Azaion.Common/Constants.cs index 61c9aeb..ff62c6a 100644 --- a/Azaion.Common/Constants.cs +++ b/Azaion.Common/Constants.cs @@ -5,7 +5,7 @@ namespace Azaion.Common; public class Constants { - public const string SECURE_RESOURCE_CACHE = "SecureResourceCache"; + public const string JPG_EXT = ".jpg"; #region DirectoriesConfig diff --git a/Azaion.Common/DTO/DetectionClass.cs b/Azaion.Common/DTO/DetectionClass.cs index d050b94..7c0a40f 100644 --- a/Azaion.Common/DTO/DetectionClass.cs +++ b/Azaion.Common/DTO/DetectionClass.cs @@ -11,6 +11,7 @@ public class DetectionClass public string Name { get; set; } = null!; public string ShortName { get; set; } = null!; + [JsonIgnore] public string UIName { get diff --git a/Azaion.Common/Services/AnnotationService.cs b/Azaion.Common/Services/AnnotationService.cs index eaf071a..9d68233 100644 --- a/Azaion.Common/Services/AnnotationService.cs +++ b/Azaion.Common/Services/AnnotationService.cs @@ -68,7 +68,7 @@ public class AnnotationService : INotificationHandler { Reference = _hardwareService.GetHardware().Hash, OffsetSpec = new OffsetTypeOffset(offset + 1), - MessageHandler = async (stream, consumer, context, message) => + MessageHandler = async (_, _, context, message) => { var msg = MessagePackSerializer.Deserialize(message.Data.Contents); await _dbFactory.Run(async db => await db.QueueOffsets @@ -89,7 +89,6 @@ public class AnnotationService : INotificationHandler msg.CreatedDate, msg.OriginalMediaName, msg.Time, - msg.ImageExtension, JsonConvert.DeserializeObject>(msg.Detections) ?? [], msg.Source, new MemoryStream(msg.Image), @@ -105,23 +104,23 @@ public class AnnotationService : INotificationHandler public async Task SaveAnnotation(AnnotationImage a, CancellationToken cancellationToken = default) { a.Time = TimeSpan.FromMilliseconds(a.Milliseconds); - return await SaveAnnotationInner(DateTime.Now, a.OriginalMediaName, a.Time, ".jpg", a.Detections.ToList(), + return await SaveAnnotationInner(DateTime.Now, a.OriginalMediaName, a.Time, a.Detections.ToList(), a.Source, new MemoryStream(a.Image), _authProvider.CurrentUser.Role, _authProvider.CurrentUser.Email, generateThumbnail: true, cancellationToken); } //Manual - public async Task SaveAnnotation(string originalMediaName, TimeSpan time, string imageExtension, List detections, SourceEnum source, Stream? stream = null, CancellationToken token = default) => - await SaveAnnotationInner(DateTime.UtcNow, originalMediaName, time, imageExtension, detections, source, stream, + public async Task SaveAnnotation(string originalMediaName, TimeSpan time, List detections, SourceEnum source, Stream? stream = null, CancellationToken token = default) => + await SaveAnnotationInner(DateTime.UtcNow, originalMediaName, time, detections, source, stream, _authProvider.CurrentUser.Role, _authProvider.CurrentUser.Email, generateThumbnail: true, token); //Manual Validate existing public async Task ValidateAnnotation(Annotation annotation, CancellationToken token = default) => - await SaveAnnotationInner(DateTime.UtcNow, annotation.OriginalMediaName, annotation.Time, annotation.ImageExtension, annotation.Detections.ToList(), SourceEnum.Manual, null, + await SaveAnnotationInner(DateTime.UtcNow, annotation.OriginalMediaName, annotation.Time, annotation.Detections.ToList(), SourceEnum.Manual, null, _authProvider.CurrentUser.Role, _authProvider.CurrentUser.Email, generateThumbnail: false, token); // Manual save from Validators -> Validated -> stream: azaion-annotations-confirm // AI, Manual save from Operators -> Created -> stream: azaion-annotations - private async Task SaveAnnotationInner(DateTime createdDate, string originalMediaName, TimeSpan time, string imageExtension, List detections, SourceEnum source, Stream? stream, + private async Task SaveAnnotationInner(DateTime createdDate, string originalMediaName, TimeSpan time, List detections, SourceEnum source, Stream? stream, RoleEnum userRole, string createdEmail, bool generateThumbnail = false, @@ -156,7 +155,7 @@ public class AnnotationService : INotificationHandler Name = fName, OriginalMediaName = originalMediaName, Time = time, - ImageExtension = imageExtension, + ImageExtension = Constants.JPG_EXT, CreatedEmail = createdEmail, CreatedRole = userRole, AnnotationStatus = status, diff --git a/Azaion.Dataset/DatasetExplorerEventHandler.cs b/Azaion.Dataset/DatasetExplorerEventHandler.cs index f06540a..b7845ee 100644 --- a/Azaion.Dataset/DatasetExplorerEventHandler.cs +++ b/Azaion.Dataset/DatasetExplorerEventHandler.cs @@ -63,7 +63,7 @@ public class DatasetExplorerEventHandler( var detections = datasetExplorer.ExplorerEditor.CurrentDetections .Select(x => new Detection(a.Name, x.GetLabel(datasetExplorer.ExplorerEditor.RenderSize))) .ToList(); - await annotationService.SaveAnnotation(a.OriginalMediaName, a.Time, a.ImageExtension, detections, SourceEnum.Manual, token: cancellationToken); + await annotationService.SaveAnnotation(a.OriginalMediaName, a.Time, detections, SourceEnum.Manual, token: cancellationToken); datasetExplorer.SwitchTab(toEditor: false); break; case PlaybackControlEnum.RemoveSelectedAnns: diff --git a/Azaion.Suite/Azaion.Suite.csproj b/Azaion.Suite/Azaion.Suite.csproj index 823acde..48eb08b 100644 --- a/Azaion.Suite/Azaion.Suite.csproj +++ b/Azaion.Suite/Azaion.Suite.csproj @@ -31,8 +31,8 @@ - - + + @@ -40,12 +40,14 @@ PreserveNewest - + + PreserveNewest - - + + + PreserveNewest - + @@ -58,8 +60,8 @@ - - + + diff --git a/Azaion.Suite/config.json b/Azaion.Suite/config.json index 6829833..8c15991 100644 --- a/Azaion.Suite/config.json +++ b/Azaion.Suite/config.json @@ -26,7 +26,9 @@ { "Id": 8, "Name": "Танк з захистом", "ShortName": "Танк.захист" }, { "Id": 9, "Name": "Дим", "ShortName": "Дим" }, { "Id": 10, "Name": "Літак", "ShortName": "Літак" }, - { "Id": 11, "Name": "Мотоцикл", "ShortName": "Мото" } + { "Id": 11, "Name": "Мотоцикл", "ShortName": "Мото" }, + { "Id": 12, "Name": "Маскування сіткою", "ShortName": "Сітка" }, + { "Id": 13, "Name": "Маскування гілками", "ShortName": "Гілки" } ], "LastSelectedExplorerClass": null, "VideoFormats": [ "mp4", "mov", "avi" ], @@ -45,4 +47,4 @@ "TrackingIntersectionThreshold": 0.8 }, "ThumbnailConfig": { "Size": "240,135", "Border": 10 } -} +} \ No newline at end of file diff --git a/Azaion.Suite/config.production.json b/Azaion.Suite/config.production.json index 500c7eb..96c92dc 100644 --- a/Azaion.Suite/config.production.json +++ b/Azaion.Suite/config.production.json @@ -25,7 +25,9 @@ { "Id": 8, "Name": "Танк з захистом", "ShortName": "Танк.захист" }, { "Id": 9, "Name": "Дим", "ShortName": "Дим" }, { "Id": 10, "Name": "Літак", "ShortName": "Літак" }, - { "Id": 11, "Name": "Мотоцикл", "ShortName": "Мото" } + { "Id": 11, "Name": "Мотоцикл", "ShortName": "Мото" }, + { "Id": 12, "Name": "Маскування сіткою", "ShortName": "Сітка" }, + { "Id": 13, "Name": "Маскування гілками", "ShortName": "Гілки" } ], "LastSelectedExplorerClass": null, "VideoFormats": [ "mp4", "mov", "avi" ], @@ -44,4 +46,4 @@ "TrackingIntersectionThreshold": 0.8 }, "ThumbnailConfig": { "Size": "240,135", "Border": 10 } -} +} \ No newline at end of file