splitting python complete

This commit is contained in:
Oleksandr Bezdieniezhnykh
2025-08-12 14:48:56 +03:00
parent fc6e5db795
commit ad782bcbaa
31 changed files with 834 additions and 369 deletions
+85 -97
View File
@@ -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));