mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 11:16: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));
|
||||
|
||||
@@ -23,7 +23,7 @@ using MediaPlayer = LibVLCSharp.Shared.MediaPlayer;
|
||||
namespace Azaion.Annotator;
|
||||
|
||||
public class AnnotatorEventHandler(
|
||||
LibVLC libVLC,
|
||||
LibVLC libVlc,
|
||||
MediaPlayer mediaPlayer,
|
||||
Annotator mainWindow,
|
||||
FormState formState,
|
||||
@@ -47,8 +47,7 @@ public class AnnotatorEventHandler(
|
||||
{
|
||||
private const int STEP = 20;
|
||||
private const int LARGE_STEP = 5000;
|
||||
private const int RESULT_WIDTH = 1280;
|
||||
private readonly string tempImgPath = Path.Combine(dirConfig.Value.ImagesDirectory, "___temp___.jpg");
|
||||
private readonly string _tempImgPath = Path.Combine(dirConfig.Value.ImagesDirectory, "___temp___.jpg");
|
||||
|
||||
private readonly Dictionary<Key, PlaybackControlEnum> _keysControlEnumDict = new()
|
||||
{
|
||||
@@ -144,8 +143,8 @@ public class AnnotatorEventHandler(
|
||||
if (mediaPlayer.IsPlaying)
|
||||
{
|
||||
mediaPlayer.Pause();
|
||||
mediaPlayer.TakeSnapshot(0, tempImgPath, 0, 0);
|
||||
mainWindow.Editor.SetBackground(await tempImgPath.OpenImage());
|
||||
mediaPlayer.TakeSnapshot(0, _tempImgPath, 0, 0);
|
||||
mainWindow.Editor.SetBackground(await _tempImgPath.OpenImage());
|
||||
formState.BackgroundTime = TimeSpan.FromMilliseconds(mediaPlayer.Time);
|
||||
}
|
||||
else
|
||||
@@ -238,16 +237,21 @@ public class AnnotatorEventHandler(
|
||||
return;
|
||||
var mediaInfo = (MediaFileInfo)mainWindow.LvFiles.SelectedItem;
|
||||
|
||||
if (formState.CurrentMedia == mediaInfo)
|
||||
return; //already loaded
|
||||
|
||||
formState.CurrentMedia = mediaInfo;
|
||||
mainWindow.Title = $"{mainWindow.MainTitle} - {mediaInfo.Name}";
|
||||
|
||||
|
||||
await mainWindow.ReloadAnnotations();
|
||||
|
||||
if (mediaInfo.MediaType == MediaTypes.Video)
|
||||
{
|
||||
mainWindow.Editor.SetBackground(null);
|
||||
//need to wait a bit for correct VLC playback event handling
|
||||
await Task.Delay(100, ct);
|
||||
mediaPlayer.Stop();
|
||||
mediaPlayer.Play(new Media(libVLC, mediaInfo.Path));
|
||||
mediaPlayer.Play(new Media(libVlc, mediaInfo.Path));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -256,6 +260,7 @@ public class AnnotatorEventHandler(
|
||||
formState.CurrentMediaSize = new Size(image.PixelWidth, image.PixelHeight);
|
||||
mainWindow.Editor.SetBackground(image);
|
||||
mediaPlayer.Stop();
|
||||
mainWindow.ShowTimeAnnotations(TimeSpan.Zero, showImage: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,13 +287,14 @@ public class AnnotatorEventHandler(
|
||||
// var annGrid = mainWindow.DgAnnotations;
|
||||
// annGrid.SelectedIndex = Math.Min(annGrid.Items.Count, annGrid.SelectedIndex + 1);
|
||||
// mainWindow.OpenAnnotationResult((AnnotationResult)annGrid.SelectedItem);
|
||||
|
||||
mainWindow.Editor.SetBackground(null);
|
||||
formState.BackgroundTime = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
await NextMedia(ct: cancellationToken);
|
||||
}
|
||||
mainWindow.Editor.SetBackground(null);
|
||||
formState.BackgroundTime = null;
|
||||
|
||||
mainWindow.LvFiles.Items.Refresh();
|
||||
mainWindow.Editor.RemoveAllAnns();
|
||||
@@ -301,7 +307,7 @@ public class AnnotatorEventHandler(
|
||||
if (!File.Exists(imgPath))
|
||||
{
|
||||
var source = (mainWindow.Editor.BackgroundImage.Source as BitmapSource)!;
|
||||
if (source.PixelWidth <= RESULT_WIDTH * 2 && source.PixelHeight <= RESULT_WIDTH * 2) // Allow to be up to 2560*2560 to save to 1280*1280
|
||||
if (source.PixelWidth <= Constants.AI_TILE_SIZE * 2 && source.PixelHeight <= Constants.AI_TILE_SIZE * 2) // Allow to be up to 2560*2560 to save to 1280*1280
|
||||
{
|
||||
//Save image
|
||||
await using var stream = new FileStream(imgPath, FileMode.Create);
|
||||
@@ -314,28 +320,28 @@ public class AnnotatorEventHandler(
|
||||
{
|
||||
//Tiling
|
||||
|
||||
//1. Restore original picture coordinates
|
||||
var pictureCoordinatesDetections = canvasDetections.Select(x => new CanvasLabel(
|
||||
//1. Convert from RenderSize to CurrentMediaSize
|
||||
var detectionCoords = canvasDetections.Select(x => new CanvasLabel(
|
||||
new YoloLabel(x, mainWindow.Editor.RenderSize, formState.CurrentMediaSize), formState.CurrentMediaSize, null, x.Confidence))
|
||||
.ToList();
|
||||
|
||||
//2. Split to 1280*1280 frames
|
||||
var results = TileProcessor.Split(formState.CurrentMediaSize, pictureCoordinatesDetections, cancellationToken);
|
||||
//2. Split to frames
|
||||
var results = TileProcessor.Split(formState.CurrentMediaSize, detectionCoords, cancellationToken);
|
||||
|
||||
//3. Save each frame as a separate annotation
|
||||
BitmapEncoder tileEncoder = new JpegBitmapEncoder();
|
||||
foreach (var res in results)
|
||||
{
|
||||
var mediaName = $"{formState.MediaName}!split!{res.Tile.X}_{res.Tile.Y}!";
|
||||
var time = TimeSpan.Zero;
|
||||
var annotationName = mediaName.ToTimeName(time);
|
||||
var annotationName = $"{formState.MediaName}{Constants.SPLIT_SUFFIX}{res.Tile.Left:0000}_{res.Tile.Top:0000}!".ToTimeName(time);
|
||||
|
||||
var tileImgPath = Path.Combine(dirConfig.Value.ImagesDirectory, $"{annotationName}{Constants.JPG_EXT}");
|
||||
await using var tileStream = new FileStream(tileImgPath, FileMode.Create);
|
||||
var bitmap = new CroppedBitmap(source, new Int32Rect((int)res.Tile.X, (int)res.Tile.Y, (int)res.Tile.Width, (int)res.Tile.Height));
|
||||
tileEncoder.Frames.Add(BitmapFrame.Create(bitmap));
|
||||
var bitmap = new CroppedBitmap(source, new Int32Rect((int)res.Tile.Left, (int)res.Tile.Top, (int)res.Tile.Width, (int)res.Tile.Height));
|
||||
|
||||
var tileEncoder = new JpegBitmapEncoder { Frames = [BitmapFrame.Create(bitmap)] };
|
||||
tileEncoder.Save(tileStream);
|
||||
await tileStream.FlushAsync(cancellationToken);
|
||||
tileStream.Close();
|
||||
|
||||
var frameSize = new Size(res.Tile.Width, res.Tile.Height);
|
||||
var detections = res.Detections
|
||||
@@ -343,18 +349,18 @@ public class AnnotatorEventHandler(
|
||||
.Select(x => new Detection(annotationName, new YoloLabel(x, frameSize)))
|
||||
.ToList();
|
||||
|
||||
annotationsResult.Add(await annotationService.SaveAnnotation(mediaName, time, detections, token: cancellationToken));
|
||||
annotationsResult.Add(await annotationService.SaveAnnotation(formState.MediaName, annotationName, time, detections, token: cancellationToken));
|
||||
}
|
||||
return annotationsResult;
|
||||
}
|
||||
}
|
||||
|
||||
var timeImg = formState.BackgroundTime ?? TimeSpan.FromMilliseconds(mediaPlayer.Time);
|
||||
var timeName = formState.MediaName.ToTimeName(timeImg);
|
||||
var annName = formState.MediaName.ToTimeName(timeImg);
|
||||
var currentDetections = canvasDetections.Select(x =>
|
||||
new Detection(timeName, new YoloLabel(x, mainWindow.Editor.RenderSize)))
|
||||
new Detection(annName, new YoloLabel(x, mainWindow.Editor.RenderSize)))
|
||||
.ToList();
|
||||
var annotation = await annotationService.SaveAnnotation(formState.MediaName, timeImg, currentDetections, token: cancellationToken);
|
||||
var annotation = await annotationService.SaveAnnotation(formState.MediaName, annName, timeImg, currentDetections, token: cancellationToken);
|
||||
return [annotation];
|
||||
}
|
||||
|
||||
@@ -367,15 +373,15 @@ public class AnnotatorEventHandler(
|
||||
var namesSet = notification.AnnotationNames.ToHashSet();
|
||||
|
||||
var remainAnnotations = formState.AnnotationResults
|
||||
.Where(x => !namesSet.Contains(x.Annotation?.Name ?? "")).ToList();
|
||||
.Where(x => !namesSet.Contains(x.Name)).ToList();
|
||||
formState.AnnotationResults.Clear();
|
||||
foreach (var ann in remainAnnotations)
|
||||
formState.AnnotationResults.Add(ann);
|
||||
|
||||
var timedAnnsToRemove = mainWindow.TimedAnnotations
|
||||
var timedAnnotationsToRemove = mainWindow.TimedAnnotations
|
||||
.Where(x => namesSet.Contains(x.Value.Name))
|
||||
.Select(x => x.Value).ToList();
|
||||
mainWindow.TimedAnnotations.Remove(timedAnnsToRemove);
|
||||
mainWindow.TimedAnnotations.Remove(timedAnnotationsToRemove);
|
||||
|
||||
if (formState.AnnotationResults.Count == 0)
|
||||
{
|
||||
@@ -420,7 +426,10 @@ public class AnnotatorEventHandler(
|
||||
{
|
||||
mainWindow.Dispatcher.Invoke(() =>
|
||||
{
|
||||
mainWindow.AddAnnotation(e.Annotation);
|
||||
|
||||
var mediaInfo = (MediaFileInfo)mainWindow.LvFiles.SelectedItem;
|
||||
if ((mediaInfo?.FName ?? "") == e.Annotation.OriginalMediaName)
|
||||
mainWindow.AddAnnotation(e.Annotation);
|
||||
|
||||
var log = string.Join(Environment.NewLine, e.Annotation.Detections.Select(det =>
|
||||
$"Розпізнавання {e.Annotation.OriginalMediaName}: {annotationConfig.Value.DetectionClassesDict[det.ClassNumber].ShortName}: " +
|
||||
|
||||
Reference in New Issue
Block a user