mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 09:06:30 +00:00
splitting python complete
This commit is contained in:
@@ -29,7 +29,7 @@ namespace Azaion.Annotator;
|
||||
public partial class Annotator
|
||||
{
|
||||
private readonly AppConfig _appConfig;
|
||||
private readonly LibVLC _libVLC;
|
||||
private readonly LibVLC _libVlc;
|
||||
private readonly MediaPlayer _mediaPlayer;
|
||||
private readonly IMediator _mediator;
|
||||
private readonly FormState _formState;
|
||||
@@ -42,17 +42,17 @@ public partial class Annotator
|
||||
private readonly IInferenceClient _inferenceClient;
|
||||
|
||||
private bool _suspendLayout;
|
||||
private bool _gpsPanelVisible = false;
|
||||
private bool _gpsPanelVisible;
|
||||
|
||||
public readonly CancellationTokenSource MainCancellationSource = new();
|
||||
private readonly CancellationTokenSource _mainCancellationSource = new();
|
||||
public CancellationTokenSource DetectionCancellationSource = new();
|
||||
public bool IsInferenceNow = false;
|
||||
private bool _isInferenceNow;
|
||||
|
||||
private readonly TimeSpan _thresholdBefore = TimeSpan.FromMilliseconds(50);
|
||||
private readonly TimeSpan _thresholdAfter = TimeSpan.FromMilliseconds(150);
|
||||
|
||||
public ObservableCollection<MediaFileInfo> AllMediaFiles { get; set; } = new();
|
||||
public ObservableCollection<MediaFileInfo> FilteredMediaFiles { get; set; } = new();
|
||||
private ObservableCollection<MediaFileInfo> FilteredMediaFiles { get; set; } = new();
|
||||
public Dictionary<string, MediaFileInfo> MediaFilesDict = new();
|
||||
|
||||
public IntervalTree<TimeSpan, Annotation> TimedAnnotations { get; set; } = new();
|
||||
@@ -61,7 +61,7 @@ public partial class Annotator
|
||||
public Annotator(
|
||||
IConfigUpdater configUpdater,
|
||||
IOptions<AppConfig> appConfig,
|
||||
LibVLC libVLC,
|
||||
LibVLC libVlc,
|
||||
MediaPlayer mediaPlayer,
|
||||
IMediator mediator,
|
||||
FormState formState,
|
||||
@@ -78,7 +78,7 @@ public partial class Annotator
|
||||
Title = MainTitle;
|
||||
_appConfig = appConfig.Value;
|
||||
_configUpdater = configUpdater;
|
||||
_libVLC = libVLC;
|
||||
_libVlc = libVlc;
|
||||
_mediaPlayer = mediaPlayer;
|
||||
_mediator = mediator;
|
||||
_formState = formState;
|
||||
@@ -91,7 +91,7 @@ public partial class Annotator
|
||||
Loaded += OnLoaded;
|
||||
Closed += OnFormClosed;
|
||||
Activated += (_, _) => _formState.ActiveWindow = WindowEnum.Annotator;
|
||||
TbFolder.TextChanged += async (sender, args) =>
|
||||
TbFolder.TextChanged += async (_, _) =>
|
||||
{
|
||||
if (!Path.Exists(TbFolder.Text))
|
||||
return;
|
||||
@@ -179,22 +179,8 @@ public partial class Annotator
|
||||
VideoView.MediaPlayer = _mediaPlayer;
|
||||
|
||||
//On start playing media
|
||||
_mediaPlayer.Playing += async (sender, args) =>
|
||||
_mediaPlayer.Playing += (_, _) =>
|
||||
{
|
||||
if (_formState.CurrentMrl == _mediaPlayer.Media?.Mrl)
|
||||
return; //already loaded all the info
|
||||
|
||||
await Dispatcher.Invoke(async () => await ReloadAnnotations());
|
||||
|
||||
//show image
|
||||
if (_formState.CurrentMedia?.MediaType == MediaTypes.Image)
|
||||
{
|
||||
await Task.Delay(100); //wait to load the frame and set on pause
|
||||
ShowTimeAnnotations(TimeSpan.FromMilliseconds(_mediaPlayer.Time), showImage: true);
|
||||
return;
|
||||
}
|
||||
|
||||
_formState.CurrentMrl = _mediaPlayer.Media?.Mrl ?? "";
|
||||
uint vw = 0, vh = 0;
|
||||
_mediaPlayer.Size(0, ref vw, ref vh);
|
||||
_formState.CurrentMediaSize = new Size(vw, vh);
|
||||
@@ -211,12 +197,12 @@ public partial class Annotator
|
||||
var selectedClass = args.DetectionClass;
|
||||
Editor.CurrentAnnClass = selectedClass;
|
||||
_mediator.Publish(new AnnClassSelectedEvent(selectedClass));
|
||||
};
|
||||
};
|
||||
|
||||
_mediaPlayer.PositionChanged += (o, args) =>
|
||||
_mediaPlayer.PositionChanged += (_, _) =>
|
||||
ShowTimeAnnotations(TimeSpan.FromMilliseconds(_mediaPlayer.Time));
|
||||
|
||||
VideoSlider.ValueChanged += (value, newValue) =>
|
||||
VideoSlider.ValueChanged += (_, newValue) =>
|
||||
_mediaPlayer.Position = (float)(newValue / VideoSlider.Maximum);
|
||||
|
||||
VideoSlider.KeyDown += (sender, args) =>
|
||||
@@ -227,51 +213,49 @@ public partial class Annotator
|
||||
|
||||
DgAnnotations.MouseDoubleClick += (sender, args) =>
|
||||
{
|
||||
var dgRow = ItemsControl.ContainerFromElement((DataGrid)sender, (args.OriginalSource as DependencyObject)!) as DataGridRow;
|
||||
if (dgRow != null)
|
||||
OpenAnnotationResult((AnnotationResult)dgRow!.Item);
|
||||
if (ItemsControl.ContainerFromElement((DataGrid)sender, (args.OriginalSource as DependencyObject)!) is DataGridRow dgRow)
|
||||
OpenAnnotationResult((Annotation)dgRow.Item);
|
||||
|
||||
};
|
||||
|
||||
DgAnnotations.KeyUp += async (sender, args) =>
|
||||
DgAnnotations.KeyUp += async (_, args) =>
|
||||
{
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.Up:
|
||||
case Key.Down: //cursor is already moved by system behaviour
|
||||
OpenAnnotationResult((AnnotationResult)DgAnnotations.SelectedItem);
|
||||
OpenAnnotationResult((Annotation)DgAnnotations.SelectedItem);
|
||||
break;
|
||||
case Key.Delete:
|
||||
var result = MessageBox.Show("Чи дійсно видалити аннотації?","Підтвердження видалення", MessageBoxButton.OKCancel, MessageBoxImage.Question);
|
||||
if (result != MessageBoxResult.OK)
|
||||
return;
|
||||
|
||||
var res = DgAnnotations.SelectedItems.Cast<AnnotationResult>().ToList();
|
||||
var annotationNames = res.Select(x => x.Annotation.Name).ToList();
|
||||
var res = DgAnnotations.SelectedItems.Cast<Annotation>().ToList();
|
||||
var annotationNames = res.Select(x => x.Name).ToList();
|
||||
|
||||
await _mediator.Publish(new AnnotationsDeletedEvent(annotationNames));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
Editor.Mediator = _mediator;
|
||||
DgAnnotations.ItemsSource = _formState.AnnotationResults;
|
||||
}
|
||||
|
||||
public void OpenAnnotationResult(AnnotationResult res)
|
||||
private void OpenAnnotationResult(Annotation ann)
|
||||
{
|
||||
_mediaPlayer.SetPause(true);
|
||||
Editor.RemoveAllAnns();
|
||||
_mediaPlayer.Time = (long)res.Annotation.Time.TotalMilliseconds;
|
||||
if (!ann.IsSplit)
|
||||
Editor.RemoveAllAnns();
|
||||
|
||||
_mediaPlayer.Time = (long)ann.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.Annotation.Time);
|
||||
Editor.ClearExpiredAnnotations(ann.Time);
|
||||
});
|
||||
|
||||
ShowAnnotation(res.Annotation, showImage: true);
|
||||
ShowAnnotation(ann, showImage: true, openResult: true);
|
||||
}
|
||||
private void SaveUserSettings()
|
||||
{
|
||||
@@ -284,7 +268,7 @@ public partial class Annotator
|
||||
_configUpdater.Save(_appConfig);
|
||||
}
|
||||
|
||||
private void ShowTimeAnnotations(TimeSpan time, bool showImage = false)
|
||||
public void ShowTimeAnnotations(TimeSpan time, bool showImage = false)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
@@ -292,60 +276,68 @@ public partial class Annotator
|
||||
StatusClock.Text = $"{TimeSpan.FromMilliseconds(_mediaPlayer.Time):mm\\:ss} / {_formState.CurrentVideoLength:mm\\:ss}";
|
||||
Editor.ClearExpiredAnnotations(time);
|
||||
});
|
||||
var annotation = TimedAnnotations.Query(time).FirstOrDefault();
|
||||
if (annotation != null) ShowAnnotation(annotation, showImage);
|
||||
var annotations = TimedAnnotations.Query(time).ToList();
|
||||
if (!annotations.Any())
|
||||
return;
|
||||
foreach (var ann in annotations)
|
||||
ShowAnnotation(ann, showImage);
|
||||
}
|
||||
|
||||
private void ShowAnnotation(Annotation annotation, bool showImage = false)
|
||||
private void ShowAnnotation(Annotation annotation, bool showImage = false, bool openResult = false)
|
||||
{
|
||||
Dispatcher.Invoke(async () =>
|
||||
{
|
||||
if (showImage)
|
||||
if (showImage && !annotation.IsSplit && File.Exists(annotation.ImagePath))
|
||||
{
|
||||
if (File.Exists(annotation.ImagePath))
|
||||
{
|
||||
Editor.SetBackground(await annotation.ImagePath.OpenImage());
|
||||
_formState.BackgroundTime = annotation.Time;
|
||||
}
|
||||
Editor.SetBackground(await annotation.ImagePath.OpenImage());
|
||||
_formState.BackgroundTime = annotation.Time;
|
||||
}
|
||||
Editor.CreateDetections(annotation.Time, annotation.Detections, _appConfig.AnnotationConfig.DetectionClasses, _formState.CurrentMediaSize);
|
||||
|
||||
if (annotation.SplitTile != null && openResult)
|
||||
{
|
||||
var canvasTileLocation = new CanvasLabel(new YoloLabel(annotation.SplitTile, _formState.CurrentMediaSize),
|
||||
RenderSize);
|
||||
Editor.ZoomTo(new Point(canvasTileLocation.CenterX, canvasTileLocation.CenterY));
|
||||
}
|
||||
else
|
||||
Editor.CreateDetections(annotation, _appConfig.AnnotationConfig.DetectionClasses, _formState.CurrentMediaSize);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task ReloadAnnotations()
|
||||
public async Task ReloadAnnotations()
|
||||
{
|
||||
_formState.AnnotationResults.Clear();
|
||||
TimedAnnotations.Clear();
|
||||
Editor.RemoveAllAnns();
|
||||
|
||||
var annotations = await _dbFactory.Run(async db =>
|
||||
await db.Annotations.LoadWith(x => x.Detections)
|
||||
.Where(x => x.OriginalMediaName == _formState.MediaName)
|
||||
.OrderBy(x => x.Time)
|
||||
.ToListAsync(token: MainCancellationSource.Token));
|
||||
|
||||
TimedAnnotations.Clear();
|
||||
_formState.AnnotationResults.Clear();
|
||||
foreach (var ann in annotations)
|
||||
await Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
TimedAnnotations.Add(ann.Time.Subtract(_thresholdBefore), ann.Time.Add(_thresholdAfter), ann);
|
||||
_formState.AnnotationResults.Add(new AnnotationResult(_appConfig.AnnotationConfig.DetectionClassesDict, ann));
|
||||
}
|
||||
_formState.AnnotationResults.Clear();
|
||||
TimedAnnotations.Clear();
|
||||
Editor.RemoveAllAnns();
|
||||
|
||||
var annotations = await _dbFactory.Run(async db =>
|
||||
await db.Annotations.LoadWith(x => x.Detections)
|
||||
.Where(x => x.OriginalMediaName == _formState.MediaName)
|
||||
.OrderBy(x => x.Time)
|
||||
.ToListAsync(token: _mainCancellationSource.Token));
|
||||
|
||||
TimedAnnotations.Clear();
|
||||
_formState.AnnotationResults.Clear();
|
||||
foreach (var ann in annotations)
|
||||
{
|
||||
// Duplicate for speed
|
||||
TimedAnnotations.Add(ann.Time.Subtract(_thresholdBefore), ann.Time.Add(_thresholdAfter), ann);
|
||||
_formState.AnnotationResults.Add(ann);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Add manually
|
||||
public void AddAnnotation(Annotation annotation)
|
||||
{
|
||||
var mediaInfo = (MediaFileInfo)LvFiles.SelectedItem;
|
||||
if ((mediaInfo?.FName ?? "") != annotation.OriginalMediaName)
|
||||
return;
|
||||
|
||||
var time = annotation.Time;
|
||||
var previousAnnotations = TimedAnnotations.Query(time);
|
||||
TimedAnnotations.Remove(previousAnnotations);
|
||||
TimedAnnotations.Add(time.Subtract(_thresholdBefore), time.Add(_thresholdAfter), annotation);
|
||||
|
||||
var existingResult = _formState.AnnotationResults.FirstOrDefault(x => x.Annotation.Time == time);
|
||||
var existingResult = _formState.AnnotationResults.FirstOrDefault(x => x.Time == time);
|
||||
if (existingResult != null)
|
||||
{
|
||||
try
|
||||
@@ -360,16 +352,14 @@ public partial class Annotator
|
||||
}
|
||||
|
||||
var dict = _formState.AnnotationResults
|
||||
.Select((x, i) => new { x.Annotation.Time, Index = i })
|
||||
.Select((x, i) => new { x.Time, Index = i })
|
||||
.ToDictionary(x => x.Time, x => x.Index);
|
||||
|
||||
var index = dict.Where(x => x.Key < time)
|
||||
.OrderBy(x => time - x.Key)
|
||||
.Select(x => x.Value + 1)
|
||||
.FirstOrDefault();
|
||||
|
||||
var annRes = new AnnotationResult(_appConfig.AnnotationConfig.DetectionClassesDict, annotation);
|
||||
_formState.AnnotationResults.Insert(index, annRes);
|
||||
_formState.AnnotationResults.Insert(index, annotation);
|
||||
}
|
||||
|
||||
private async Task ReloadFiles()
|
||||
@@ -380,7 +370,7 @@ public partial class Annotator
|
||||
|
||||
var videoFiles = dir.GetFiles(_appConfig.AnnotationConfig.VideoFormats.ToArray()).Select(x =>
|
||||
{
|
||||
using var media = new Media(_libVLC, x.FullName);
|
||||
var media = new Media(_libVlc, x.FullName);
|
||||
media.Parse();
|
||||
var fInfo = new MediaFileInfo
|
||||
{
|
||||
@@ -403,14 +393,16 @@ public partial class Annotator
|
||||
|
||||
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))
|
||||
var labelsDict = await _dbFactory.Run(async db =>
|
||||
await db.Annotations
|
||||
.GroupBy(x => x.OriginalMediaName)
|
||||
.Where(x => allFileNames.Contains(x.Key))
|
||||
.ToDictionaryAsync(x => x.Key, x => x.Key));
|
||||
|
||||
.Select(x => x.Key)
|
||||
.ToDictionaryAsync(x => x, x => x));
|
||||
|
||||
foreach (var mediaFile in allFiles)
|
||||
mediaFile.HasAnnotations = labelsDict.ContainsKey(mediaFile.FName);
|
||||
|
||||
|
||||
AllMediaFiles = new ObservableCollection<MediaFileInfo>(allFiles);
|
||||
MediaFilesDict = AllMediaFiles.GroupBy(x => x.Name)
|
||||
.ToDictionary(gr => gr.Key, gr => gr.First());
|
||||
@@ -420,13 +412,13 @@ public partial class Annotator
|
||||
|
||||
private void OnFormClosed(object? sender, EventArgs e)
|
||||
{
|
||||
MainCancellationSource.Cancel();
|
||||
_mainCancellationSource.Cancel();
|
||||
_inferenceService.StopInference();
|
||||
DetectionCancellationSource.Cancel();
|
||||
|
||||
_mediaPlayer.Stop();
|
||||
_mediaPlayer.Dispose();
|
||||
_libVLC.Dispose();
|
||||
_libVlc.Dispose();
|
||||
}
|
||||
|
||||
private void OpenContainingFolder(object sender, RoutedEventArgs e)
|
||||
@@ -447,13 +439,10 @@ public partial class Annotator
|
||||
StatusClock.Text = $"{TimeSpan.FromMilliseconds(_mediaPlayer.Time):mm\\:ss} / {_formState.CurrentVideoLength:mm\\:ss}";
|
||||
}
|
||||
|
||||
private void SeekTo(TimeSpan time) =>
|
||||
SeekTo((long)time.TotalMilliseconds);
|
||||
private void OpenFolderItemClick(object sender, RoutedEventArgs e) => OpenFolder();
|
||||
private void OpenFolderButtonClick(object sender, RoutedEventArgs e) => OpenFolder();
|
||||
|
||||
private async void OpenFolderItemClick(object sender, RoutedEventArgs e) => await OpenFolder();
|
||||
private async void OpenFolderButtonClick(object sender, RoutedEventArgs e) => await OpenFolder();
|
||||
|
||||
private async Task OpenFolder()
|
||||
private void OpenFolder()
|
||||
{
|
||||
var dlg = new CommonOpenFileDialog
|
||||
{
|
||||
@@ -468,7 +457,6 @@ public partial class Annotator
|
||||
|
||||
_appConfig.DirectoriesConfig.VideosDirectory = dlg.FileName;
|
||||
TbFolder.Text = dlg.FileName;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void TbFilter_OnTextChanged(object sender, TextChangedEventArgs e)
|
||||
@@ -525,7 +513,7 @@ public partial class Annotator
|
||||
|
||||
public async Task AutoDetect()
|
||||
{
|
||||
if (IsInferenceNow)
|
||||
if (_isInferenceNow)
|
||||
return;
|
||||
|
||||
if (LvFiles.Items.IsEmpty)
|
||||
@@ -535,7 +523,7 @@ public partial class Annotator
|
||||
|
||||
Dispatcher.Invoke(() => Editor.SetBackground(null));
|
||||
|
||||
IsInferenceNow = true;
|
||||
_isInferenceNow = true;
|
||||
AIDetectBtn.IsEnabled = false;
|
||||
|
||||
DetectionCancellationSource = new CancellationTokenSource();
|
||||
@@ -550,7 +538,7 @@ public partial class Annotator
|
||||
await _inferenceService.RunInference(files, DetectionCancellationSource.Token);
|
||||
|
||||
LvFiles.Items.Refresh();
|
||||
IsInferenceNow = false;
|
||||
_isInferenceNow = false;
|
||||
StatusHelp.Text = "Розпізнавання зваершено";
|
||||
AIDetectBtn.IsEnabled = true;
|
||||
}
|
||||
@@ -596,7 +584,7 @@ public class GradientStyleSelector : StyleSelector
|
||||
{
|
||||
public override Style? SelectStyle(object item, DependencyObject container)
|
||||
{
|
||||
if (container is not DataGridRow row || row.DataContext is not AnnotationResult result)
|
||||
if (container is not DataGridRow row || row.DataContext is not Annotation result)
|
||||
return null;
|
||||
|
||||
var style = new Style(typeof(DataGridRow));
|
||||
|
||||
Reference in New Issue
Block a user