diff --git a/Azaion.Annotator/DTO/AnnotationResult.cs b/Azaion.Annotator/DTO/AnnotationResult.cs index 96dcf3c..aec26d8 100644 --- a/Azaion.Annotator/DTO/AnnotationResult.cs +++ b/Azaion.Annotator/DTO/AnnotationResult.cs @@ -13,12 +13,9 @@ public class AnnotationResult [JsonProperty(PropertyName = "t")] public TimeSpan Time { get; set; } - [JsonProperty(PropertyName = "p")] - public double Percentage { get; set; } - public double Lat { get; set; } public double Lon { get; set; } - public List Labels { get; set; } = new(); + public List Detections { get; set; } = new(); #region For Display in the grid @@ -32,10 +29,10 @@ public class AnnotationResult { get { - if (Labels.Count == 0) + if (Detections.Count == 0) return ""; - var groups = Labels.Select(x => x.ClassNumber).GroupBy(x => x).ToList(); + var groups = Detections.Select(x => x.ClassNumber).GroupBy(x => x).ToList(); return groups.Count > 1 ? string.Join(",", groups.Select(x => x.Key + 1)) : _config.AnnotationClassesDict[groups.FirstOrDefault().Key].Name; @@ -49,10 +46,10 @@ public class AnnotationResult get { var defaultColor = (Color)ColorConverter.ConvertFromString("#404040"); - if (Labels.Count == 0) + if (Detections.Count == 0) return defaultColor; - var groups = Labels.Select(x => x.ClassNumber).GroupBy(x => x).ToList(); + var groups = Detections.Select(x => x.ClassNumber).GroupBy(x => x).ToList(); return groups.Count > 1 ? defaultColor @@ -64,12 +61,11 @@ public class AnnotationResult #endregion public AnnotationResult() { } - public AnnotationResult(TimeSpan time, string timeName, List labels, Config config) + public AnnotationResult(TimeSpan time, string timeName, List detections, Config config) { _config = config; - Labels = labels; + Detections = detections; Time = time; Image = $"{timeName}.jpg"; - Percentage = 100; } } \ No newline at end of file diff --git a/Azaion.Annotator/Extensions/VLCFrameExtractor.cs b/Azaion.Annotator/Extensions/VLCFrameExtractor.cs index c981016..d0890ad 100644 --- a/Azaion.Annotator/Extensions/VLCFrameExtractor.cs +++ b/Azaion.Annotator/Extensions/VLCFrameExtractor.cs @@ -106,7 +106,7 @@ public class VLCFrameExtractor(LibVLC libVLC) if (_frameCounter > 20 && _frameCounter % 10 == 0) { - var msToAdd = (_frameCounter - _lastFrame) * (_lastFrameTimestamp.TotalMilliseconds / _lastFrame); + var msToAdd = (_frameCounter - _lastFrame) * (_lastFrame == 0 ? 0 : _lastFrameTimestamp.TotalMilliseconds / _lastFrame); var time = _lastFrameTimestamp.Add(TimeSpan.FromMilliseconds(msToAdd)); FramesQueue.Enqueue(new FrameInfo(time, _currentBitmap)); } diff --git a/Azaion.Annotator/GalleryManager.cs b/Azaion.Annotator/GalleryManager.cs index 769f6b4..73c5820 100644 --- a/Azaion.Annotator/GalleryManager.cs +++ b/Azaion.Annotator/GalleryManager.cs @@ -144,8 +144,8 @@ public class GalleryManager : IGalleryManager var size = new Size(originalImage.Width, originalImage.Height); if (!File.Exists(labelName)) { - File.Move(imgPath, Path.Combine(_config.UnknownImages, imgName)); - _logger.LogInformation($"No labels found for image {imgName}! Moved image to the {_config.UnknownImages} folder."); + File.Delete(imgPath); + _logger.LogInformation($"No labels found for image {imgName}! Image deleted!"); return null; } var labels = (await YoloLabel.ReadFromFile(labelName, cancellationToken)) diff --git a/Azaion.Annotator/MainWindow.xaml.cs b/Azaion.Annotator/MainWindow.xaml.cs index d03cf58..8b06d80 100644 --- a/Azaion.Annotator/MainWindow.xaml.cs +++ b/Azaion.Annotator/MainWindow.xaml.cs @@ -206,32 +206,34 @@ public partial class MainWindow DgAnnotations.MouseDoubleClick += (sender, args) => { var dgRow = ItemsControl.ContainerFromElement((DataGrid)sender, (args.OriginalSource as DependencyObject)!) as DataGridRow; - var res = (AnnotationResult)dgRow!.Item; - _mediaPlayer.SetPause(true); - Editor.RemoveAllAnns(); - _mediaPlayer.Time = (long)res.Time.TotalMilliseconds; - ShowTimeAnnotations(res.Time, showImage: true); + OpenAnnotationResult((AnnotationResult)dgRow!.Item); }; DgAnnotations.KeyUp += (sender, args) => { - if (args.Key != Key.Delete) - return; - - var result = MessageBox.Show("Чи дійсно видалити аннотації?","Підтвердження видалення", MessageBoxButton.OKCancel, MessageBoxImage.Question); - if (result != MessageBoxResult.OK) - return; - - var res = DgAnnotations.SelectedItems.Cast().ToList(); - foreach (var annotationResult in res) + switch (args.Key) { - var imgName = Path.GetFileNameWithoutExtension(annotationResult.Image); - var thumbnailPath = Path.Combine(_config.ThumbnailsDirectory, $"{imgName}{Config.THUMBNAIL_PREFIX}.jpg"); - File.Delete(annotationResult.Image); - File.Delete(Path.Combine(_config.LabelsDirectory, $"{imgName}.txt")); - File.Delete(thumbnailPath); - _formState.AnnotationResults.Remove(annotationResult); - Annotations.Remove(Annotations.Query(annotationResult.Time)); + case Key.Up: + case Key.Down: //cursor is already moved by system behaviour + OpenAnnotationResult((AnnotationResult)DgAnnotations.SelectedItem); + break; + case Key.Delete: + var result = MessageBox.Show("Чи дійсно видалити аннотації?","Підтвердження видалення", MessageBoxButton.OKCancel, MessageBoxImage.Question); + if (result != MessageBoxResult.OK) + return; + + var res = DgAnnotations.SelectedItems.Cast().ToList(); + foreach (var annotationResult in res) + { + var imgName = Path.GetFileNameWithoutExtension(annotationResult.Image); + var thumbnailPath = Path.Combine(_config.ThumbnailsDirectory, $"{imgName}{Config.THUMBNAIL_PREFIX}.jpg"); + File.Delete(annotationResult.Image); + File.Delete(Path.Combine(_config.LabelsDirectory, $"{imgName}.txt")); + File.Delete(thumbnailPath); + _formState.AnnotationResults.Remove(annotationResult); + Annotations.Remove(Annotations.Query(annotationResult.Time)); + } + break; } }; @@ -240,6 +242,21 @@ public partial class MainWindow DgAnnotations.ItemsSource = _formState.AnnotationResults; } + public void OpenAnnotationResult(AnnotationResult res) + { + _mediaPlayer.SetPause(true); + Editor.RemoveAllAnns(); + _mediaPlayer.Time = (long)res.Time.TotalMilliseconds; + + Dispatcher.Invoke(() => + { + VideoSlider.Value = _mediaPlayer.Position * VideoSlider.Maximum; + StatusClock.Text = $"{TimeSpan.FromMilliseconds(_mediaPlayer.Time):mm\\:ss} / {_formState.CurrentVideoLength:mm\\:ss}"; + Editor.ClearExpiredAnnotations(res.Time); + }); + + AddAnnotationsToCanvas(res.Time, res.Detections, showImage: true); + } private async Task SaveUserSettings() { if (_suspendLayout) @@ -256,7 +273,7 @@ public partial class MainWindow }, TimeSpan.FromSeconds(5)); } - private void ShowTimeAnnotations(TimeSpan time, bool showImage = false) + private void ShowTimeAnnotations(TimeSpan time) { Dispatcher.Invoke(() => { @@ -266,7 +283,7 @@ public partial class MainWindow }); var annotations = Annotations.Query(time).SelectMany(x => x).Select(x => new Detection(x)); - AddAnnotationsToCanvas(time, annotations, showImage); + AddAnnotationsToCanvas(time, annotations); } private void AddAnnotationsToCanvas(TimeSpan? time, IEnumerable labels, bool showImage = false) @@ -316,11 +333,14 @@ public partial class MainWindow } public async Task AddAnnotations(TimeSpan? time, List annotations, CancellationToken ct = default) + => await AddAnnotations(time, annotations.Select(x => new Detection(x)).ToList(), ct); + + public async Task AddAnnotations(TimeSpan? time, List annotations, CancellationToken ct = default) { var timeValue = time ?? TimeSpan.FromMinutes(0); var previousAnnotations = Annotations.Query(timeValue); Annotations.Remove(previousAnnotations); - Annotations.Add(timeValue.Subtract(_thresholdBefore), timeValue.Add(_thresholdAfter), annotations); + Annotations.Add(timeValue.Subtract(_thresholdBefore), timeValue.Add(_thresholdAfter), annotations.Cast().ToList()); var existingResult = _formState.AnnotationResults.FirstOrDefault(x => x.Time == time); if (existingResult != null) @@ -633,7 +653,7 @@ public partial class MainWindow Editor.Background = new ImageBrush { ImageSource = await imgPath.OpenImage() }; Editor.RemoveAllAnns(); AddAnnotationsToCanvas(time, detections, true); - await AddAnnotations(timeframe.Time, detections.Cast().ToList(), token); + await AddAnnotations(timeframe.Time, detections, token); var log = string.Join(Environment.NewLine, detections.Select(det => $"{_config.AnnotationClassesDict[det.ClassNumber].Name}: " + diff --git a/Azaion.Annotator/MainWindowEventHandler.cs b/Azaion.Annotator/MainWindowEventHandler.cs index e65f987..e4f1ab8 100644 --- a/Azaion.Annotator/MainWindowEventHandler.cs +++ b/Azaion.Annotator/MainWindowEventHandler.cs @@ -243,6 +243,8 @@ public class MainWindowEventHandler : private async Task SaveAnnotations() { + var annGridSelectedIndex = _mainWindow.DgAnnotations.SelectedIndex; + if (_formState.CurrentMedia == null) return; @@ -270,14 +272,17 @@ public class MainWindowEventHandler : //no need to save image, it's already there, just remove background _mainWindow.Editor.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0)); _formState.BackgroundShown = false; + + var annGrid = _mainWindow.DgAnnotations; + annGrid.SelectedIndex = Math.Min(annGrid.Items.Count, annGridSelectedIndex + 1); + _mainWindow.OpenAnnotationResult((AnnotationResult)annGrid.SelectedItem); } else { var resultHeight = (uint)Math.Round(RESULT_WIDTH / _formState.CurrentVideoSize.Width * _formState.CurrentVideoSize.Height); _mediaPlayer.TakeSnapshot(0, destinationPath, RESULT_WIDTH, resultHeight); + _mediaPlayer.Play(); } - - _mediaPlayer.Play(); } else { diff --git a/README.md b/README.md index cea4b88..a80b86e 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,4 @@ Works on Windows only for now Install: 1. C# dotnet with wpf -2. CUDNN \ No newline at end of file +2. [CUDNN](https://developer.nvidia.com/cudnn) \ No newline at end of file