mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 09:36:30 +00:00
292 lines
11 KiB
C#
292 lines
11 KiB
C#
using System.IO;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media;
|
|
using Azaion.Annotator.DTO;
|
|
using LibVLCSharp.Shared;
|
|
using MediatR;
|
|
using Microsoft.Extensions.Logging;
|
|
using MediaPlayer = LibVLCSharp.Shared.MediaPlayer;
|
|
|
|
namespace Azaion.Annotator;
|
|
|
|
public class MainWindowEventHandler :
|
|
INotificationHandler<KeyEvent>,
|
|
INotificationHandler<AnnClassSelectedEvent>,
|
|
INotificationHandler<PlaybackControlEvent>,
|
|
INotificationHandler<VolumeChangedEvent>
|
|
{
|
|
private readonly LibVLC _libVLC;
|
|
private readonly MediaPlayer _mediaPlayer;
|
|
private readonly MainWindow _mainWindow;
|
|
private readonly FormState _formState;
|
|
private readonly Config _config;
|
|
private readonly IMediator _mediator;
|
|
private readonly IGalleryManager _galleryManager;
|
|
private readonly DatasetExplorer _datasetExplorer;
|
|
private readonly ILogger<MainWindowEventHandler> _logger;
|
|
|
|
private const int STEP = 20;
|
|
private const int LARGE_STEP = 5000;
|
|
private const int RESULT_WIDTH = 1280;
|
|
|
|
private readonly Dictionary<Key, PlaybackControlEnum> _keysControlEnumDict = new()
|
|
{
|
|
{ Key.Space, PlaybackControlEnum.Pause },
|
|
{ Key.Left, PlaybackControlEnum.PreviousFrame },
|
|
{ Key.Right, PlaybackControlEnum.NextFrame },
|
|
{ Key.Enter, PlaybackControlEnum.SaveAnnotations },
|
|
{ Key.Delete, PlaybackControlEnum.RemoveSelectedAnns },
|
|
{ Key.X, PlaybackControlEnum.RemoveAllAnns },
|
|
{ Key.PageUp, PlaybackControlEnum.Previous },
|
|
{ Key.PageDown, PlaybackControlEnum.Next },
|
|
};
|
|
|
|
public MainWindowEventHandler(LibVLC libVLC,
|
|
MediaPlayer mediaPlayer,
|
|
MainWindow mainWindow,
|
|
FormState formState,
|
|
Config config,
|
|
IMediator mediator,
|
|
IGalleryManager galleryManager,
|
|
DatasetExplorer datasetExplorer,
|
|
ILogger<MainWindowEventHandler> logger)
|
|
{
|
|
_libVLC = libVLC;
|
|
_mediaPlayer = mediaPlayer;
|
|
_mainWindow = mainWindow;
|
|
_formState = formState;
|
|
_config = config;
|
|
_mediator = mediator;
|
|
_galleryManager = galleryManager;
|
|
_datasetExplorer = datasetExplorer;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task Handle(AnnClassSelectedEvent notification, CancellationToken cancellationToken)
|
|
{
|
|
SelectClass(notification.AnnotationClass);
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
|
|
private void SelectClass(AnnotationClass annClass)
|
|
{
|
|
_mainWindow.Editor.CurrentAnnClass = annClass;
|
|
foreach (var ann in _mainWindow.Editor.CurrentAnns.Where(x => x.IsSelected))
|
|
ann.AnnotationClass = annClass;
|
|
_mainWindow.LvClasses.SelectedIndex = annClass.Id;
|
|
}
|
|
|
|
public async Task Handle(KeyEvent notification, CancellationToken cancellationToken)
|
|
{
|
|
if (_formState.ActiveWindow != WindowsEnum.Main)
|
|
return;
|
|
|
|
var key = notification.Args.Key;
|
|
var keyNumber = (int?)null;
|
|
|
|
if ((int)key >= (int)Key.D1 && (int)key <= (int)Key.D9)
|
|
keyNumber = key - Key.D1;
|
|
if ((int)key >= (int)Key.NumPad1 && (int)key <= (int)Key.NumPad9)
|
|
keyNumber = key - Key.NumPad1;
|
|
if (keyNumber.HasValue)
|
|
SelectClass((AnnotationClass)_mainWindow.LvClasses.Items[keyNumber.Value]);
|
|
|
|
if (_keysControlEnumDict.TryGetValue(key, out var value))
|
|
await ControlPlayback(value);
|
|
|
|
if (key == Key.A)
|
|
_mainWindow.AutoDetect(null!, null!);
|
|
|
|
await VolumeControl(key);
|
|
}
|
|
|
|
private async Task VolumeControl(Key key)
|
|
{
|
|
switch (key)
|
|
{
|
|
case Key.VolumeMute when _mediaPlayer.Volume == 0:
|
|
await ControlPlayback(PlaybackControlEnum.TurnOnVolume);
|
|
break;
|
|
case Key.VolumeMute:
|
|
await ControlPlayback(PlaybackControlEnum.TurnOffVolume);
|
|
break;
|
|
case Key.Up:
|
|
case Key.VolumeUp:
|
|
var vUp = Math.Min(100, _mediaPlayer.Volume + 5);
|
|
ChangeVolume(vUp);
|
|
_mainWindow.Volume.Value = vUp;
|
|
break;
|
|
case Key.Down:
|
|
case Key.VolumeDown:
|
|
var vDown = Math.Max(0, _mediaPlayer.Volume - 5);
|
|
ChangeVolume(vDown);
|
|
_mainWindow.Volume.Value = vDown;
|
|
break;
|
|
}
|
|
}
|
|
|
|
public async Task Handle(PlaybackControlEvent notification, CancellationToken cancellationToken)
|
|
{
|
|
await ControlPlayback(notification.PlaybackControl);
|
|
_mainWindow.VideoView.Focus();
|
|
}
|
|
|
|
private async Task ControlPlayback(PlaybackControlEnum controlEnum)
|
|
{
|
|
try
|
|
{
|
|
var isCtrlPressed = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
|
|
var step = isCtrlPressed ? LARGE_STEP : STEP;
|
|
|
|
switch (controlEnum)
|
|
{
|
|
case PlaybackControlEnum.Play:
|
|
Play();
|
|
break;
|
|
case PlaybackControlEnum.Pause:
|
|
_mediaPlayer.Pause();
|
|
if (!_mediaPlayer.IsPlaying)
|
|
_mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.AnnotationHelp]);
|
|
if (_formState.BackgroundShown)
|
|
{
|
|
_mainWindow.Editor.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
|
|
_formState.BackgroundShown = false;
|
|
}
|
|
break;
|
|
case PlaybackControlEnum.Stop:
|
|
_mediaPlayer.Stop();
|
|
break;
|
|
case PlaybackControlEnum.PreviousFrame:
|
|
_mainWindow.SeekTo(_mediaPlayer.Time - step);
|
|
break;
|
|
case PlaybackControlEnum.NextFrame:
|
|
_mainWindow.SeekTo(_mediaPlayer.Time + step);
|
|
break;
|
|
case PlaybackControlEnum.SaveAnnotations:
|
|
await SaveAnnotations();
|
|
break;
|
|
case PlaybackControlEnum.RemoveSelectedAnns:
|
|
|
|
_mainWindow.Editor.RemoveSelectedAnns();
|
|
break;
|
|
case PlaybackControlEnum.RemoveAllAnns:
|
|
_mainWindow.Editor.RemoveAllAnns();
|
|
break;
|
|
case PlaybackControlEnum.TurnOnVolume:
|
|
_mainWindow.TurnOnVolumeBtn.Visibility = Visibility.Collapsed;
|
|
_mainWindow.TurnOffVolumeBtn.Visibility = Visibility.Visible;
|
|
_mediaPlayer.Volume = _formState.CurrentVolume;
|
|
break;
|
|
case PlaybackControlEnum.TurnOffVolume:
|
|
_mainWindow.TurnOffVolumeBtn.Visibility = Visibility.Collapsed;
|
|
_mainWindow.TurnOnVolumeBtn.Visibility = Visibility.Visible;
|
|
_formState.CurrentVolume = _mediaPlayer.Volume;
|
|
_mediaPlayer.Volume = 0;
|
|
break;
|
|
case PlaybackControlEnum.Previous:
|
|
NextMedia(isPrevious: true);
|
|
break;
|
|
case PlaybackControlEnum.Next:
|
|
NextMedia();
|
|
break;
|
|
case PlaybackControlEnum.None:
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(controlEnum), controlEnum, null);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError(e, e.Message);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private void NextMedia(bool isPrevious = false)
|
|
{
|
|
var increment = isPrevious ? -1 : 1;
|
|
var check = isPrevious ? -1 : _mainWindow.LvFiles.Items.Count;
|
|
if (_mainWindow.LvFiles.SelectedIndex + increment == check)
|
|
return;
|
|
|
|
_mainWindow.LvFiles.SelectedIndex += increment;
|
|
Play();
|
|
}
|
|
|
|
public async Task Handle(VolumeChangedEvent notification, CancellationToken cancellationToken)
|
|
{
|
|
ChangeVolume(notification.Volume);
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
private void ChangeVolume(int volume)
|
|
{
|
|
_formState.CurrentVolume = volume;
|
|
_mediaPlayer.Volume = volume;
|
|
}
|
|
|
|
private void Play()
|
|
{
|
|
if (_mainWindow.LvFiles.SelectedItem == null)
|
|
return;
|
|
var mediaInfo = (MediaFileInfo)_mainWindow.LvFiles.SelectedItem;
|
|
|
|
_formState.CurrentMedia = mediaInfo;
|
|
_mediaPlayer.Stop();
|
|
_mainWindow.Title = $"Azaion Annotator - {mediaInfo.Name}";
|
|
_mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.PauseForAnnotations]);
|
|
_mediaPlayer.Play(new Media(_libVLC, mediaInfo.Path));
|
|
}
|
|
|
|
private async Task SaveAnnotations()
|
|
{
|
|
if (_formState.CurrentMedia == null)
|
|
return;
|
|
|
|
var time = TimeSpan.FromMilliseconds(_mediaPlayer.Time);
|
|
var fName = _formState.GetTimeName(time);
|
|
|
|
var currentAnns = _mainWindow.Editor.CurrentAnns
|
|
.Select(x => new YoloLabel(x.Info, _mainWindow.Editor.RenderSize, _formState.BackgroundShown ? _mainWindow.Editor.RenderSize : _formState.CurrentVideoSize))
|
|
.ToList();
|
|
|
|
await YoloLabel.WriteToFile(currentAnns, Path.Combine(_config.LabelsDirectory, $"{fName}.txt"));
|
|
await _mainWindow.AddAnnotations(time, currentAnns);
|
|
|
|
_formState.CurrentMedia.HasAnnotations = _mainWindow.Annotations.Count != 0;
|
|
_mainWindow.LvFiles.Items.Refresh();
|
|
|
|
var isVideo = _formState.CurrentMedia.MediaType == MediaTypes.Video;
|
|
var destinationPath = Path.Combine(_config.ImagesDirectory, $"{fName}{(isVideo ? ".jpg" : Path.GetExtension(_formState.CurrentMedia.Path))}");
|
|
|
|
_mainWindow.Editor.RemoveAllAnns();
|
|
if (isVideo)
|
|
{
|
|
if (_formState.BackgroundShown)
|
|
{
|
|
//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;
|
|
}
|
|
else
|
|
{
|
|
var resultHeight = (uint)Math.Round(RESULT_WIDTH / _formState.CurrentVideoSize.Width * _formState.CurrentVideoSize.Height);
|
|
_mediaPlayer.TakeSnapshot(0, destinationPath, RESULT_WIDTH, resultHeight);
|
|
}
|
|
|
|
_mediaPlayer.Play();
|
|
}
|
|
else
|
|
{
|
|
File.Copy(_formState.CurrentMedia.Path, destinationPath, overwrite: true);
|
|
NextMedia();
|
|
}
|
|
|
|
var thumbnailDto = await _galleryManager.CreateThumbnail(destinationPath);
|
|
if (thumbnailDto != null)
|
|
_datasetExplorer.AddThumbnail(thumbnailDto, currentAnns.Select(x => x.ClassNumber));
|
|
}
|
|
} |