mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 13:26:30 +00:00
add db WIP 2, 80%
refactor, renames
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -8,13 +7,14 @@ using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Threading;
|
||||
using Azaion.Annotator.DTO;
|
||||
using Azaion.Annotator.Extensions;
|
||||
using Azaion.Common;
|
||||
using Azaion.Common.DTO;
|
||||
using Azaion.Common.DTO.Config;
|
||||
using Azaion.Common.DTO.Queue;
|
||||
using Azaion.Common.Extensions;
|
||||
using Azaion.Common.Services;
|
||||
using LibVLCSharp.Shared;
|
||||
using MediatR;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
@@ -40,9 +40,10 @@ public partial class Annotator
|
||||
private readonly ILogger<Annotator> _logger;
|
||||
private readonly VLCFrameExtractor _vlcFrameExtractor;
|
||||
private readonly IAIDetector _aiDetector;
|
||||
private readonly AnnotationService _annotationService;
|
||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||
|
||||
private ObservableCollection<AnnotationClass> AnnotationClasses { get; set; } = new();
|
||||
private ObservableCollection<DetectionClass> AnnotationClasses { get; set; } = new();
|
||||
private bool _suspendLayout;
|
||||
|
||||
private readonly TimeSpan _thresholdBefore = TimeSpan.FromMilliseconds(100);
|
||||
@@ -52,7 +53,7 @@ public partial class Annotator
|
||||
private ObservableCollection<MediaFileInfo> AllMediaFiles { get; set; } = new();
|
||||
private ObservableCollection<MediaFileInfo> FilteredMediaFiles { get; set; } = new();
|
||||
|
||||
public IntervalTree<TimeSpan, List<YoloLabel>> Annotations { get; set; } = new();
|
||||
public IntervalTree<TimeSpan, List<Detection>> Detections { get; set; } = new();
|
||||
private AutodetectDialog _autoDetectDialog = new() { Topmost = true };
|
||||
|
||||
public Annotator(
|
||||
@@ -64,7 +65,8 @@ public partial class Annotator
|
||||
HelpWindow helpWindow,
|
||||
ILogger<Annotator> logger,
|
||||
VLCFrameExtractor vlcFrameExtractor,
|
||||
IAIDetector aiDetector)
|
||||
IAIDetector aiDetector,
|
||||
AnnotationService annotationService)
|
||||
{
|
||||
InitializeComponent();
|
||||
_appConfig = appConfig.Value;
|
||||
@@ -77,6 +79,7 @@ public partial class Annotator
|
||||
_logger = logger;
|
||||
_vlcFrameExtractor = vlcFrameExtractor;
|
||||
_aiDetector = aiDetector;
|
||||
_annotationService = annotationService;
|
||||
|
||||
Loaded += OnLoaded;
|
||||
Closed += OnFormClosed;
|
||||
@@ -98,7 +101,7 @@ public partial class Annotator
|
||||
|
||||
ReloadFiles();
|
||||
|
||||
AnnotationClasses = new ObservableCollection<AnnotationClass>(_appConfig.AnnotationConfig.AnnotationClasses);
|
||||
AnnotationClasses = new ObservableCollection<DetectionClass>(_appConfig.AnnotationConfig.AnnotationClasses);
|
||||
LvClasses.ItemsSource = AnnotationClasses;
|
||||
LvClasses.SelectedIndex = 0;
|
||||
|
||||
@@ -152,7 +155,7 @@ public partial class Annotator
|
||||
|
||||
LvClasses.SelectionChanged += (_, _) =>
|
||||
{
|
||||
var selectedClass = (AnnotationClass)LvClasses.SelectedItem;
|
||||
var selectedClass = (DetectionClass)LvClasses.SelectedItem;
|
||||
Editor.CurrentAnnClass = selectedClass;
|
||||
_mediator.Publish(new AnnClassSelectedEvent(selectedClass));
|
||||
};
|
||||
@@ -188,7 +191,7 @@ public partial class Annotator
|
||||
OpenAnnotationResult((AnnotationResult)DgAnnotations.SelectedItem);
|
||||
break;
|
||||
case Key.Delete:
|
||||
var result = MessageBox.Show(Application.Current.MainWindow, "Чи дійсно видалити аннотації?","Підтвердження видалення", MessageBoxButton.OKCancel, MessageBoxImage.Question);
|
||||
var result = MessageBox.Show("Чи дійсно видалити аннотації?","Підтвердження видалення", MessageBoxButton.OKCancel, MessageBoxImage.Question);
|
||||
if (result != MessageBoxResult.OK)
|
||||
return;
|
||||
|
||||
@@ -202,7 +205,7 @@ public partial class Annotator
|
||||
File.Delete(Path.Combine(_appConfig.DirectoriesConfig.LabelsDirectory, $"{imgName}.txt"));
|
||||
File.Delete(thumbnailPath);
|
||||
_formState.AnnotationResults.Remove(annotationResult);
|
||||
Annotations.Remove(Annotations.Query(annotationResult.Time));
|
||||
Detections.Remove(Detections.Query(annotationResult.Time));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -251,7 +254,7 @@ public partial class Annotator
|
||||
Editor.ClearExpiredAnnotations(time);
|
||||
});
|
||||
|
||||
var annotations = Annotations.Query(time).SelectMany(x => x).Select(x => new Detection(x));
|
||||
var annotations = Detections.Query(time).SelectMany(x => x).Select(x => new Detection(_formState.GetTimeName(time), x));
|
||||
AddAnnotationsToCanvas(time, annotations);
|
||||
}
|
||||
|
||||
@@ -285,7 +288,7 @@ public partial class Annotator
|
||||
private async Task ReloadAnnotations(CancellationToken ct = default)
|
||||
{
|
||||
_formState.AnnotationResults.Clear();
|
||||
Annotations.Clear();
|
||||
Detections.Clear();
|
||||
Editor.RemoveAllAnns();
|
||||
|
||||
var labelDir = new DirectoryInfo(_appConfig.DirectoriesConfig.LabelsDirectory);
|
||||
@@ -294,22 +297,21 @@ public partial class Annotator
|
||||
|
||||
var labelFiles = labelDir.GetFiles($"{_formState.VideoName}_??????.txt");
|
||||
foreach (var file in labelFiles)
|
||||
{
|
||||
var name = Path.GetFileNameWithoutExtension(file.Name);
|
||||
var time = Constants.GetTime(name);
|
||||
await AddAnnotations(time, await YoloLabel.ReadFromFile(file.FullName, ct), ct);
|
||||
}
|
||||
await AddAnnotations(Path.GetFileNameWithoutExtension(file.Name), await YoloLabel.ReadFromFile(file.FullName, ct), ct);
|
||||
}
|
||||
|
||||
public async Task AddAnnotations(TimeSpan? time, List<YoloLabel> annotations, CancellationToken ct = default)
|
||||
=> await AddAnnotations(time, annotations.Select(x => new Detection(x)).ToList(), ct);
|
||||
//Load from yolo label file
|
||||
public async Task AddAnnotations(string name, List<YoloLabel> annotations, CancellationToken ct = default)
|
||||
=> await AddAnnotations(name, annotations.Select(x => new Detection(name, x)).ToList(), ct);
|
||||
|
||||
public async Task AddAnnotations(TimeSpan? time, List<Detection> detections, CancellationToken ct = default)
|
||||
//Add manually
|
||||
public async Task AddAnnotations(string name, List<Detection> detections, CancellationToken ct = default)
|
||||
{
|
||||
var time = Constants.GetTime(name);
|
||||
var timeValue = time ?? TimeSpan.FromMinutes(0);
|
||||
var previousAnnotations = Annotations.Query(timeValue);
|
||||
Annotations.Remove(previousAnnotations);
|
||||
Annotations.Add(timeValue.Subtract(_thresholdBefore), timeValue.Add(_thresholdAfter), detections.Cast<YoloLabel>().ToList());
|
||||
var previousAnnotations = Detections.Query(timeValue);
|
||||
Detections.Remove(previousAnnotations);
|
||||
Detections.Add(timeValue.Subtract(_thresholdBefore), timeValue.Add(_thresholdAfter), detections);
|
||||
|
||||
var existingResult = _formState.AnnotationResults.FirstOrDefault(x => x.Time == time);
|
||||
if (existingResult != null)
|
||||
@@ -345,15 +347,15 @@ public partial class Annotator
|
||||
return (-1).ToColor();
|
||||
|
||||
return colorNumber >= detectionClasses.Count
|
||||
? _appConfig.AnnotationConfig.AnnotationClassesDict[detectionClasses.LastOrDefault()].Color
|
||||
: _appConfig.AnnotationConfig.AnnotationClassesDict[detectionClasses[colorNumber]].Color;
|
||||
? _appConfig.AnnotationConfig.DetectionClassesDict[detectionClasses.LastOrDefault()].Color
|
||||
: _appConfig.AnnotationConfig.DetectionClassesDict[detectionClasses[colorNumber]].Color;
|
||||
}
|
||||
|
||||
var detectionClasses = detections.Select(x => x.ClassNumber).Distinct().ToList();
|
||||
|
||||
annotationResult.ClassName = detectionClasses.Count > 1
|
||||
? string.Join(", ", detectionClasses.Select(x => _appConfig.AnnotationConfig.AnnotationClassesDict[x].ShortName))
|
||||
: _appConfig.AnnotationConfig.AnnotationClassesDict[detectionClasses.FirstOrDefault()].Name;
|
||||
? string.Join(", ", detectionClasses.Select(x => _appConfig.AnnotationConfig.DetectionClassesDict[x].ShortName))
|
||||
: _appConfig.AnnotationConfig.DetectionClassesDict[detectionClasses.FirstOrDefault()].Name;
|
||||
|
||||
annotationResult.ClassColor0 = GetAnnotationClass(detectionClasses, 0);
|
||||
annotationResult.ClassColor1 = GetAnnotationClass(detectionClasses, 1);
|
||||
@@ -442,8 +444,8 @@ public partial class Annotator
|
||||
// private void AddClassBtnClick(object sender, RoutedEventArgs e)
|
||||
// {
|
||||
// LvClasses.IsReadOnly = false;
|
||||
// AnnotationClasses.Add(new AnnotationClass(AnnotationClasses.Count));
|
||||
// LvClasses.SelectedIndex = AnnotationClasses.Count - 1;
|
||||
// DetectionClasses.Add(new DetectionClass(DetectionClasses.Count));
|
||||
// LvClasses.SelectedIndex = DetectionClasses.Count - 1;
|
||||
// }
|
||||
private async void OpenFolderItemClick(object sender, RoutedEventArgs e) => await OpenFolder();
|
||||
private async void OpenFolderButtonClick(object sender, RoutedEventArgs e) => await OpenFolder();
|
||||
@@ -578,9 +580,10 @@ public partial class Annotator
|
||||
{
|
||||
try
|
||||
{
|
||||
var fName = Path.GetFileNameWithoutExtension(mediaInfo.Path);
|
||||
var stream = new FileStream(mediaInfo.Path, FileMode.Open);
|
||||
var detections = await _aiDetector.Detect(stream, token);
|
||||
await ProcessDetection((TimeSpan.FromMilliseconds(0), stream), detections, token);
|
||||
var detections = await _aiDetector.Detect(fName, stream, token);
|
||||
await ProcessDetection((TimeSpan.FromMilliseconds(0), stream), Path.GetExtension(mediaInfo.Path), detections, token);
|
||||
if (detections.Count != 0)
|
||||
mediaInfo.HasAnnotations = true;
|
||||
}
|
||||
@@ -599,7 +602,8 @@ public partial class Annotator
|
||||
Console.WriteLine($"Detect time: {timeframe.Time}");
|
||||
try
|
||||
{
|
||||
var detections = await _aiDetector.Detect(timeframe.Stream, token);
|
||||
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)
|
||||
@@ -628,7 +632,7 @@ public partial class Annotator
|
||||
continue;
|
||||
|
||||
mediaInfo.HasAnnotations = true;
|
||||
await ProcessDetection(timeframe, detections, token);
|
||||
await ProcessDetection(timeframe, "jpg", detections, token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -685,7 +689,7 @@ public partial class Annotator
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task ProcessDetection((TimeSpan Time, Stream Stream) timeframe, List<Detection> detections, CancellationToken token = default)
|
||||
private async Task ProcessDetection((TimeSpan Time, Stream Stream) timeframe, string imageExtension, List<Detection> detections, CancellationToken token = default)
|
||||
{
|
||||
_previousDetection = (timeframe.Time, detections);
|
||||
await Dispatcher.Invoke(async () =>
|
||||
@@ -695,22 +699,24 @@ public partial class Annotator
|
||||
var time = timeframe.Time;
|
||||
|
||||
var fName = _formState.GetTimeName(timeframe.Time);
|
||||
var imgPath = Path.Combine(_appConfig.DirectoriesConfig.ImagesDirectory, $"{fName}.jpg");
|
||||
var imgPath = Path.Combine(_appConfig.DirectoriesConfig.ImagesDirectory, $"{fName}.{imageExtension}");
|
||||
|
||||
Editor.Background = new ImageBrush { ImageSource = await imgPath.OpenImage() };
|
||||
Editor.RemoveAllAnns();
|
||||
AddAnnotationsToCanvas(time, detections, true);
|
||||
await AddAnnotations(timeframe.Time, detections, token);
|
||||
await AddAnnotations(fName, detections, token);
|
||||
|
||||
await _annotationService.SaveAnnotation(fName, imageExtension, detections, SourceEnum.AI, timeframe.Stream, token);
|
||||
|
||||
var log = string.Join(Environment.NewLine, detections.Select(det =>
|
||||
$"{_appConfig.AnnotationConfig.AnnotationClassesDict[det.ClassNumber].Name}: " +
|
||||
$"{_appConfig.AnnotationConfig.DetectionClassesDict[det.ClassNumber].Name}: " +
|
||||
$"xy=({det.CenterX:F2},{det.CenterY:F2}), " +
|
||||
$"size=({det.Width:F2}, {det.Height:F2}), " +
|
||||
$"prob: {det.Probability:F1}%"));
|
||||
|
||||
Dispatcher.Invoke(() => _autoDetectDialog.Log(log));
|
||||
|
||||
await _mediator.Publish(new ImageCreatedEvent(imgPath), token);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user