mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 23:56:30 +00:00
gallery manager WIP
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using System.Reflection;
|
using System.Data;
|
||||||
|
using System.Reflection;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
@@ -35,6 +36,8 @@ public partial class App : Application
|
|||||||
{
|
{
|
||||||
services.AddSingleton<MainWindow>();
|
services.AddSingleton<MainWindow>();
|
||||||
services.AddSingleton<HelpWindow>();
|
services.AddSingleton<HelpWindow>();
|
||||||
|
services.AddSingleton<DatasetExplorer>();
|
||||||
|
services.AddSingleton<IGalleryManager, GalleryManager>();
|
||||||
services.AddMediatR(c => c.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
|
services.AddMediatR(c => c.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
|
||||||
services.AddSingleton<LibVLC>(_ => new LibVLC());
|
services.AddSingleton<LibVLC>(_ => new LibVLC());
|
||||||
services.AddSingleton<FormState>();
|
services.AddSingleton<FormState>();
|
||||||
|
|||||||
@@ -10,10 +10,13 @@ namespace Azaion.Annotator.DTO;
|
|||||||
|
|
||||||
public class Config
|
public class Config
|
||||||
{
|
{
|
||||||
|
public const string ThumbnailPrefix = "_thumb";
|
||||||
|
|
||||||
public string VideosDirectory { get; set; }
|
public string VideosDirectory { get; set; }
|
||||||
public string LabelsDirectory { get; set; }
|
public string LabelsDirectory { get; set; }
|
||||||
public string ImagesDirectory { get; set; }
|
public string ImagesDirectory { get; set; }
|
||||||
public string ResultsDirectory { get; set; }
|
public string ResultsDirectory { get; set; }
|
||||||
|
public string ThumbnailsDirectory { get; set; }
|
||||||
|
|
||||||
public List<AnnotationClass> AnnotationClasses { get; set; } = [];
|
public List<AnnotationClass> AnnotationClasses { get; set; } = [];
|
||||||
|
|
||||||
@@ -31,6 +34,14 @@ public class Config
|
|||||||
|
|
||||||
public List<string> VideoFormats { get; set; }
|
public List<string> VideoFormats { get; set; }
|
||||||
public List<string> ImageFormats { get; set; }
|
public List<string> ImageFormats { get; set; }
|
||||||
|
|
||||||
|
public ThumbnailConfig ThumbnailConfig { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ThumbnailConfig
|
||||||
|
{
|
||||||
|
public Size Size { get; set; }
|
||||||
|
public int Border { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IConfigRepository
|
public interface IConfigRepository
|
||||||
@@ -48,9 +59,11 @@ public class FileConfigRepository(ILogger<FileConfigRepository> logger) : IConfi
|
|||||||
private const string DEFAULT_LABELS_DIR = "labels";
|
private const string DEFAULT_LABELS_DIR = "labels";
|
||||||
private const string DEFAULT_IMAGES_DIR = "images";
|
private const string DEFAULT_IMAGES_DIR = "images";
|
||||||
private const string DEFAULT_RESULTS_DIR = "results";
|
private const string DEFAULT_RESULTS_DIR = "results";
|
||||||
|
private const string DEFAULT_THUMBNAILS_DIR = "thumbnails";
|
||||||
|
|
||||||
private static readonly Size DefaultWindowSize = new(1280, 720);
|
private static readonly Size DefaultWindowSize = new(1280, 720);
|
||||||
private static readonly Point DefaultWindowLocation = new(100, 100);
|
private static readonly Point DefaultWindowLocation = new(100, 100);
|
||||||
|
private static readonly Size DefaultThumbnailSize = new(240, 135);
|
||||||
|
|
||||||
private static readonly List<string> DefaultVideoFormats = ["mp4", "mov", "avi"];
|
private static readonly List<string> DefaultVideoFormats = ["mp4", "mov", "avi"];
|
||||||
private static readonly List<string> DefaultImageFormats = ["jpg", "jpeg", "png", "bmp"];
|
private static readonly List<string> DefaultImageFormats = ["jpg", "jpeg", "png", "bmp"];
|
||||||
@@ -68,13 +81,19 @@ public class FileConfigRepository(ILogger<FileConfigRepository> logger) : IConfi
|
|||||||
LabelsDirectory = Path.Combine(exePath, DEFAULT_LABELS_DIR),
|
LabelsDirectory = Path.Combine(exePath, DEFAULT_LABELS_DIR),
|
||||||
ImagesDirectory = Path.Combine(exePath, DEFAULT_IMAGES_DIR),
|
ImagesDirectory = Path.Combine(exePath, DEFAULT_IMAGES_DIR),
|
||||||
ResultsDirectory = Path.Combine(exePath, DEFAULT_RESULTS_DIR),
|
ResultsDirectory = Path.Combine(exePath, DEFAULT_RESULTS_DIR),
|
||||||
|
ThumbnailsDirectory = Path.Combine(exePath, DEFAULT_THUMBNAILS_DIR),
|
||||||
|
|
||||||
WindowLocation = DefaultWindowLocation,
|
WindowLocation = DefaultWindowLocation,
|
||||||
WindowSize = DefaultWindowSize,
|
WindowSize = DefaultWindowSize,
|
||||||
ShowHelpOnStart = true,
|
ShowHelpOnStart = true,
|
||||||
|
|
||||||
VideoFormats = DefaultVideoFormats,
|
VideoFormats = DefaultVideoFormats,
|
||||||
ImageFormats = DefaultImageFormats
|
ImageFormats = DefaultImageFormats,
|
||||||
|
ThumbnailConfig = new ThumbnailConfig
|
||||||
|
{
|
||||||
|
Size = DefaultThumbnailSize,
|
||||||
|
Border = 10
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
var str = File.ReadAllText(CONFIG_PATH);
|
var str = File.ReadAllText(CONFIG_PATH);
|
||||||
|
|||||||
@@ -9,11 +9,14 @@ public class FormState
|
|||||||
public SelectionState SelectionState { get; set; } = SelectionState.None;
|
public SelectionState SelectionState { get; set; } = SelectionState.None;
|
||||||
|
|
||||||
public MediaFileInfo? CurrentMedia { get; set; }
|
public MediaFileInfo? CurrentMedia { get; set; }
|
||||||
public Size CurrentVideoSize { get; set; }
|
|
||||||
public string VideoName => string.IsNullOrEmpty(CurrentMedia?.Name)
|
public string VideoName => string.IsNullOrEmpty(CurrentMedia?.Name)
|
||||||
? ""
|
? ""
|
||||||
: Path.GetFileNameWithoutExtension(CurrentMedia.Name).Replace(" ", "");
|
: Path.GetFileNameWithoutExtension(CurrentMedia.Name).Replace(" ", "");
|
||||||
|
|
||||||
|
public string CurrentMrl { get; set; }
|
||||||
|
public Size CurrentVideoSize { get; set; }
|
||||||
public TimeSpan CurrentVideoLength { get; set; }
|
public TimeSpan CurrentVideoLength { get; set; }
|
||||||
|
|
||||||
public int CurrentVolume { get; set; } = 100;
|
public int CurrentVolume { get; set; } = 100;
|
||||||
public ObservableCollection<AnnotationResult> AnnotationResults { get; set; } = [];
|
public ObservableCollection<AnnotationResult> AnnotationResults { get; set; } = [];
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
@@ -147,6 +148,16 @@ public class YoloLabel : Label
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<List<YoloLabel>> ReadFromFile(string filename, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var str = await File.ReadAllTextAsync(filename, cancellationToken);
|
||||||
|
|
||||||
|
return str.Split(Environment.NewLine)
|
||||||
|
.Select(Parse)
|
||||||
|
.Where(ann => ann != null)
|
||||||
|
.ToList()!;
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString() => $"{ClassNumber} {CenterX:F5} {CenterY:F5} {Width:F5} {Height:F5}".Replace(',', '.');
|
public override string ToString() => $"{ClassNumber} {CenterX:F5} {CenterY:F5} {Width:F5} {Height:F5}".Replace(',', '.');
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<Window x:Class="Azaion.Annotator.DatasetExplorer"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:Azaion.Annotator"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="Браузер анотацій" Height="450" Width="800">
|
||||||
|
<Grid
|
||||||
|
Name="MainGrid"
|
||||||
|
ShowGridLines="False"
|
||||||
|
Background="Black"
|
||||||
|
HorizontalAlignment="Stretch">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"></RowDefinition>
|
||||||
|
<RowDefinition Height="32"></RowDefinition>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="150" />
|
||||||
|
<ColumnDefinition Width="4"/>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="4"/>
|
||||||
|
<ColumnDefinition Width="200" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Azaion.Annotator;
|
||||||
|
|
||||||
|
public partial class DatasetExplorer : Window
|
||||||
|
{
|
||||||
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
|
||||||
|
public DatasetExplorer(IGalleryManager galleryManager)
|
||||||
|
{
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
Loaded += (sender, args) =>
|
||||||
|
{
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
while (!_cancellationTokenSource.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await galleryManager.RefreshThumbnails(_cancellationTokenSource.Token);
|
||||||
|
await Task.Delay(30000, _cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Closing += (sender, args) => _cancellationTokenSource.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,7 +10,10 @@ public static class ThrottleExt
|
|||||||
|
|
||||||
_throttleOn = true;
|
_throttleOn = true;
|
||||||
await func();
|
await func();
|
||||||
await Task.Delay(throttleTime ?? TimeSpan.FromMilliseconds(500));
|
_ = Task.Run(() =>
|
||||||
|
{
|
||||||
|
Task.Delay(throttleTime ?? TimeSpan.FromMilliseconds(500));
|
||||||
_throttleOn = false;
|
_throttleOn = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,129 @@
|
|||||||
namespace Azaion.Annotator;
|
using System.Drawing;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.IO;
|
||||||
|
using Azaion.Annotator.DTO;
|
||||||
|
using Color = System.Drawing.Color;
|
||||||
|
using Size = System.Windows.Size;
|
||||||
|
|
||||||
public class GalleryManager
|
namespace Azaion.Annotator;
|
||||||
|
|
||||||
|
public class GalleryManager : IGalleryManager
|
||||||
{
|
{
|
||||||
|
private readonly Config _config;
|
||||||
|
|
||||||
public void CreateThumbnails()
|
public int ThumbnailsCount { get; set; }
|
||||||
|
public int ImagesCount { get; set; }
|
||||||
|
|
||||||
|
public GalleryManager(Config config)
|
||||||
{
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RefreshThumbnails(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var dir = new DirectoryInfo(_config.ThumbnailsDirectory);
|
||||||
|
if (!dir.Exists)
|
||||||
|
Directory.CreateDirectory(_config.ThumbnailsDirectory);
|
||||||
|
|
||||||
|
var prefixLen = Config.ThumbnailPrefix.Length;
|
||||||
|
var thumbnailsDir = new DirectoryInfo(_config.ThumbnailsDirectory);
|
||||||
|
|
||||||
|
var thumbnails = thumbnailsDir.GetFiles()
|
||||||
|
.Select(x => Path.GetFileNameWithoutExtension(x.Name)[..^prefixLen])
|
||||||
|
.GroupBy(x => x)
|
||||||
|
.Select(gr => gr.Key)
|
||||||
|
.ToHashSet();
|
||||||
|
ThumbnailsCount = thumbnails.Count;
|
||||||
|
|
||||||
|
var files = new DirectoryInfo(_config.ImagesDirectory).GetFiles();
|
||||||
|
ImagesCount = files.Length;
|
||||||
|
|
||||||
|
foreach (var img in files)
|
||||||
|
{
|
||||||
|
var imgName = Path.GetFileNameWithoutExtension(img.Name);
|
||||||
|
if (thumbnails.Contains(imgName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var bitmap = await GenerateThumbnail(img, cancellationToken);
|
||||||
|
var thumbnailName = Path.Combine(thumbnailsDir.FullName, $"{imgName}{Config.ThumbnailPrefix}.jpg");
|
||||||
|
bitmap.Save(thumbnailName, ImageFormat.Jpeg);
|
||||||
|
|
||||||
|
ThumbnailsCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<Bitmap> GenerateThumbnail(FileInfo img, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var width = (int)_config.ThumbnailConfig.Size.Width;
|
||||||
|
var height = (int)_config.ThumbnailConfig.Size.Height;
|
||||||
|
|
||||||
|
var imgName = Path.GetFileNameWithoutExtension(img.Name);
|
||||||
|
var labelName = Path.Combine(_config.LabelsDirectory, $"{imgName}.txt");
|
||||||
|
|
||||||
|
var originalImage = Image.FromFile(img.FullName);
|
||||||
|
|
||||||
|
var bitmap = new Bitmap(width, height);
|
||||||
|
|
||||||
|
using var g = Graphics.FromImage(bitmap);
|
||||||
|
g.CompositingQuality = CompositingQuality.HighSpeed;
|
||||||
|
g.SmoothingMode = SmoothingMode.HighSpeed;
|
||||||
|
g.InterpolationMode = InterpolationMode.Default;
|
||||||
|
|
||||||
|
var size = new Size(originalImage.Width, originalImage.Height);
|
||||||
|
var labels = (await YoloLabel.ReadFromFile(labelName, cancellationToken))
|
||||||
|
.Select(x => new CanvasLabel(x, size, size))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var thumbWhRatio = width / (float)height;
|
||||||
|
var border = _config.ThumbnailConfig.Border;
|
||||||
|
|
||||||
|
var labelsMinX = labels.Any() ? labels.Min(x => x.X);
|
||||||
|
var labelsMaxX = labels.Max(x => x.X + x.Width);
|
||||||
|
|
||||||
|
var labelsMinY = labels.Min(x => x.Y);
|
||||||
|
var labelsMaxY = labels.Max(x => x.Y + x.Height);
|
||||||
|
|
||||||
|
var labelsHeight = labelsMaxY - labelsMinY + 2 * border;
|
||||||
|
var labelsWidth = labelsMaxX - labelsMinX + 2 * border;
|
||||||
|
|
||||||
|
var frameHeight = 0.0;
|
||||||
|
var frameWidth = 0.0;
|
||||||
|
var frameX = 0.0;
|
||||||
|
var frameY = 0.0;
|
||||||
|
if (labelsWidth / labelsHeight > thumbWhRatio)
|
||||||
|
{
|
||||||
|
frameWidth = labelsWidth;
|
||||||
|
frameHeight = Math.Min(labelsWidth / thumbWhRatio, size.Height);
|
||||||
|
frameX = Math.Max(0, labelsMinX - border);
|
||||||
|
frameY = Math.Max(0, 0.5 * (labelsMinY + labelsMaxY - frameHeight) - border);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frameHeight = labelsHeight;
|
||||||
|
frameWidth = Math.Min(labelsHeight * thumbWhRatio, size.Width);
|
||||||
|
frameY = Math.Max(0, labelsMinY - border);
|
||||||
|
frameX = Math.Max(0, 0.5 * (labelsMinX + labelsMaxX - frameWidth) - border);
|
||||||
|
}
|
||||||
|
|
||||||
|
var scale = frameHeight / height;
|
||||||
|
g.DrawImage(originalImage, new Rectangle(0, 0, width, height), new RectangleF((float)frameX, (float)frameY, (float)frameWidth, (float)frameHeight), GraphicsUnit.Pixel);
|
||||||
|
|
||||||
|
foreach (var label in labels)
|
||||||
|
{
|
||||||
|
var color = _config.AnnotationClassesDict[label.ClassNumber].Color;
|
||||||
|
var brush = new SolidBrush(Color.FromArgb(color.A, color.R, color.G, color.B));
|
||||||
|
|
||||||
|
var rectangle = new RectangleF((float)((label.X - frameX) / scale), (float)((label.Y - frameY) / scale), (float)(label.Width / scale), (float)(label.Height / scale));
|
||||||
|
g.FillRectangle(brush, rectangle);
|
||||||
|
}
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IGalleryManager
|
||||||
|
{
|
||||||
|
int ThumbnailsCount { get; set; }
|
||||||
|
int ImagesCount { get; set; }
|
||||||
|
Task RefreshThumbnails(CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
Background="Black"
|
Background="Black"
|
||||||
HorizontalAlignment="Stretch">
|
HorizontalAlignment="Stretch">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="28"></RowDefinition>
|
||||||
<RowDefinition Height="28"></RowDefinition>
|
<RowDefinition Height="28"></RowDefinition>
|
||||||
<RowDefinition Height="28"></RowDefinition>
|
<RowDefinition Height="28"></RowDefinition>
|
||||||
<RowDefinition Height="*"></RowDefinition>
|
<RowDefinition Height="*"></RowDefinition>
|
||||||
@@ -75,6 +76,9 @@
|
|||||||
<MenuItem x:Name="OpenFolderItem"
|
<MenuItem x:Name="OpenFolderItem"
|
||||||
Foreground="Black"
|
Foreground="Black"
|
||||||
IsEnabled="True" Header="Відкрити папку..." Click="OpenFolderItemClick"/>
|
IsEnabled="True" Header="Відкрити папку..." Click="OpenFolderItemClick"/>
|
||||||
|
<MenuItem x:Name="OpenDataExplorerItem"
|
||||||
|
Foreground="Black"
|
||||||
|
IsEnabled="True" Header="Відкрити браузер анотацій..." Click="OpenDataExplorerItemClick"/>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="Допомога" Foreground="#FFBDBCBC" Margin="0,3,0,0">
|
<MenuItem Header="Допомога" Foreground="#FFBDBCBC" Margin="0,3,0,0">
|
||||||
<MenuItem x:Name="OpenHelpWindow"
|
<MenuItem x:Name="OpenHelpWindow"
|
||||||
@@ -105,7 +109,32 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<ListView Grid.Row="2"
|
<Grid
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="2">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="50" />
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Label
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Margin="1"
|
||||||
|
Foreground="LightGray"
|
||||||
|
Content="Фільтр: "/>
|
||||||
|
<TextBox
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.Row="0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Margin="1"
|
||||||
|
x:Name="TbFilter"
|
||||||
|
TextChanged="TbFilter_OnTextChanged">
|
||||||
|
</TextBox>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<ListView Grid.Row="3"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Name="LvFiles"
|
Name="LvFiles"
|
||||||
Background="Black"
|
Background="Black"
|
||||||
@@ -133,7 +162,7 @@
|
|||||||
</ListView>
|
</ListView>
|
||||||
<DataGrid x:Name="LvClasses"
|
<DataGrid x:Name="LvClasses"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Background="Black"
|
Background="Black"
|
||||||
RowBackground="#252525"
|
RowBackground="#252525"
|
||||||
Foreground="White"
|
Foreground="White"
|
||||||
@@ -181,7 +210,7 @@
|
|||||||
ResizeDirection="Columns"
|
ResizeDirection="Columns"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.RowSpan="3"
|
Grid.RowSpan="4"
|
||||||
ResizeBehavior="PreviousAndNext"
|
ResizeBehavior="PreviousAndNext"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
@@ -190,7 +219,7 @@
|
|||||||
<wpf:VideoView
|
<wpf:VideoView
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Grid.RowSpan="3"
|
Grid.RowSpan="4"
|
||||||
x:Name="VideoView">
|
x:Name="VideoView">
|
||||||
<controls:CanvasEditor x:Name="Editor"
|
<controls:CanvasEditor x:Name="Editor"
|
||||||
Background="#01000000"
|
Background="#01000000"
|
||||||
@@ -202,7 +231,7 @@
|
|||||||
ResizeDirection="Columns"
|
ResizeDirection="Columns"
|
||||||
Grid.Column="3"
|
Grid.Column="3"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.RowSpan="3"
|
Grid.RowSpan="4"
|
||||||
ResizeBehavior="PreviousAndNext"
|
ResizeBehavior="PreviousAndNext"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
@@ -211,7 +240,7 @@
|
|||||||
<DataGrid x:Name="DgAnnotations"
|
<DataGrid x:Name="DgAnnotations"
|
||||||
Grid.Column="4"
|
Grid.Column="4"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.RowSpan="3"
|
Grid.RowSpan="6"
|
||||||
Background="Black"
|
Background="Black"
|
||||||
RowBackground="#252525"
|
RowBackground="#252525"
|
||||||
Foreground="White"
|
Foreground="White"
|
||||||
@@ -265,14 +294,14 @@
|
|||||||
|
|
||||||
<controls:UpdatableProgressBar x:Name="VideoSlider"
|
<controls:UpdatableProgressBar x:Name="VideoSlider"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.ColumnSpan="4"
|
Grid.ColumnSpan="4"
|
||||||
Background="#252525"
|
Background="#252525"
|
||||||
Foreground="LightBlue">
|
Foreground="LightBlue">
|
||||||
|
|
||||||
</controls:UpdatableProgressBar>
|
</controls:UpdatableProgressBar>
|
||||||
<Grid
|
<Grid
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Background="Black"
|
Background="Black"
|
||||||
>
|
>
|
||||||
@@ -455,7 +484,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<StatusBar
|
<StatusBar
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Grid.ColumnSpan="2"
|
Grid.ColumnSpan="2"
|
||||||
Background="#252525"
|
Background="#252525"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public partial class MainWindow
|
|||||||
private readonly IConfigRepository _configRepository;
|
private readonly IConfigRepository _configRepository;
|
||||||
private readonly HelpWindow _helpWindow;
|
private readonly HelpWindow _helpWindow;
|
||||||
private readonly ILogger<MainWindow> _logger;
|
private readonly ILogger<MainWindow> _logger;
|
||||||
|
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
public ObservableCollection<AnnotationClass> AnnotationClasses { get; set; } = new();
|
public ObservableCollection<AnnotationClass> AnnotationClasses { get; set; } = new();
|
||||||
private bool _suspendLayout;
|
private bool _suspendLayout;
|
||||||
@@ -34,6 +35,10 @@ public partial class MainWindow
|
|||||||
private readonly TimeSpan _thresholdBefore = TimeSpan.FromMilliseconds(100);
|
private readonly TimeSpan _thresholdBefore = TimeSpan.FromMilliseconds(100);
|
||||||
private readonly TimeSpan _thresholdAfter = TimeSpan.FromMilliseconds(300);
|
private readonly TimeSpan _thresholdAfter = TimeSpan.FromMilliseconds(300);
|
||||||
private readonly Config _config;
|
private readonly Config _config;
|
||||||
|
private readonly DatasetExplorer _datasetExplorer;
|
||||||
|
|
||||||
|
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<YoloLabel>> Annotations { get; set; } = new();
|
||||||
|
|
||||||
@@ -42,6 +47,7 @@ public partial class MainWindow
|
|||||||
FormState formState,
|
FormState formState,
|
||||||
IConfigRepository configRepository,
|
IConfigRepository configRepository,
|
||||||
HelpWindow helpWindow,
|
HelpWindow helpWindow,
|
||||||
|
DatasetExplorer datasetExplorer,
|
||||||
ILogger<MainWindow> logger)
|
ILogger<MainWindow> logger)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -52,6 +58,7 @@ public partial class MainWindow
|
|||||||
_configRepository = configRepository;
|
_configRepository = configRepository;
|
||||||
_config = _configRepository.Get();
|
_config = _configRepository.Get();
|
||||||
_helpWindow = helpWindow;
|
_helpWindow = helpWindow;
|
||||||
|
_datasetExplorer = datasetExplorer;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
VideoView.Loaded += VideoView_Loaded;
|
VideoView.Loaded += VideoView_Loaded;
|
||||||
@@ -115,12 +122,16 @@ public partial class MainWindow
|
|||||||
|
|
||||||
_mediaPlayer.Playing += async (sender, args) =>
|
_mediaPlayer.Playing += async (sender, args) =>
|
||||||
{
|
{
|
||||||
|
if (_formState.CurrentMrl == _mediaPlayer.Media?.Mrl)
|
||||||
|
return; //already loaded all the info
|
||||||
|
|
||||||
|
_formState.CurrentMrl = _mediaPlayer.Media?.Mrl ?? "";
|
||||||
uint vw = 0, vh = 0;
|
uint vw = 0, vh = 0;
|
||||||
_mediaPlayer.Size(0, ref vw, ref vh);
|
_mediaPlayer.Size(0, ref vw, ref vh);
|
||||||
_formState.CurrentVideoSize = new Size(vw, vh);
|
_formState.CurrentVideoSize = new Size(vw, vh);
|
||||||
_formState.CurrentVideoLength = TimeSpan.FromMilliseconds(_mediaPlayer.Length);
|
_formState.CurrentVideoLength = TimeSpan.FromMilliseconds(_mediaPlayer.Length);
|
||||||
|
|
||||||
await Dispatcher.Invoke(async () => await ReloadAnnotations());
|
await Dispatcher.Invoke(async () => await ReloadAnnotations(_cancellationTokenSource.Token));
|
||||||
if (_formState.CurrentMedia?.MediaType != MediaTypes.Image)
|
if (_formState.CurrentMedia?.MediaType != MediaTypes.Image)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -195,7 +206,7 @@ public partial class MainWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReloadAnnotations()
|
public async Task ReloadAnnotations(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_formState.AnnotationResults.Clear();
|
_formState.AnnotationResults.Clear();
|
||||||
Annotations.Clear();
|
Annotations.Clear();
|
||||||
@@ -211,12 +222,7 @@ public partial class MainWindow
|
|||||||
var name = Path.GetFileNameWithoutExtension(file.Name);
|
var name = Path.GetFileNameWithoutExtension(file.Name);
|
||||||
var time = _formState.GetTime(name)!.Value;
|
var time = _formState.GetTime(name)!.Value;
|
||||||
|
|
||||||
var str = await File.ReadAllTextAsync(file.FullName);
|
await AddAnnotation(time, await YoloLabel.ReadFromFile(file.FullName, cancellationToken));
|
||||||
var annotations = str.Split(Environment.NewLine).Select(YoloLabel.Parse)
|
|
||||||
.Where(ann => ann != null)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
await AddAnnotation(time, annotations!);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,11 +277,11 @@ public partial class MainWindow
|
|||||||
HasAnnotations = labelNames.ContainsKey(Path.GetFileNameWithoutExtension(x.Name).Replace(" ", ""))
|
HasAnnotations = labelNames.ContainsKey(Path.GetFileNameWithoutExtension(x.Name).Replace(" ", ""))
|
||||||
});
|
});
|
||||||
|
|
||||||
var mediaFiles = videoFiles.Concat(imageFiles).ToList();
|
AllMediaFiles = new ObservableCollection<MediaFileInfo>(videoFiles.Concat(imageFiles).ToList());
|
||||||
LvFiles.ItemsSource = new ObservableCollection<MediaFileInfo>(mediaFiles);
|
LvFiles.ItemsSource = AllMediaFiles;
|
||||||
TbFolder.Text = _config.VideosDirectory;
|
TbFolder.Text = _config.VideosDirectory;
|
||||||
|
|
||||||
BlinkHelp(mediaFiles.Count == 0
|
BlinkHelp(AllMediaFiles.Count == 0
|
||||||
? HelpTexts.HelpTextsDict[HelpTextEnum.Initial]
|
? HelpTexts.HelpTextsDict[HelpTextEnum.Initial]
|
||||||
: HelpTexts.HelpTextsDict[HelpTextEnum.PlayVideo]);
|
: HelpTexts.HelpTextsDict[HelpTextEnum.PlayVideo]);
|
||||||
}
|
}
|
||||||
@@ -296,9 +302,9 @@ public partial class MainWindow
|
|||||||
// AnnotationClasses.Add(new AnnotationClass(AnnotationClasses.Count));
|
// AnnotationClasses.Add(new AnnotationClass(AnnotationClasses.Count));
|
||||||
// LvClasses.SelectedIndex = AnnotationClasses.Count - 1;
|
// LvClasses.SelectedIndex = AnnotationClasses.Count - 1;
|
||||||
// }
|
// }
|
||||||
private void OpenFolderItemClick(object sender, RoutedEventArgs e) => OpenFolder();
|
private async void OpenFolderItemClick(object sender, RoutedEventArgs e) => await OpenFolder();
|
||||||
private void OpenFolderButtonClick(object sender, RoutedEventArgs e) => OpenFolder();
|
private async void OpenFolderButtonClick(object sender, RoutedEventArgs e) => await OpenFolder();
|
||||||
private void OpenFolder()
|
private async Task OpenFolder()
|
||||||
{
|
{
|
||||||
var dlg = new CommonOpenFileDialog
|
var dlg = new CommonOpenFileDialog
|
||||||
{
|
{
|
||||||
@@ -310,10 +316,25 @@ public partial class MainWindow
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(dlg.FileName))
|
if (!string.IsNullOrEmpty(dlg.FileName))
|
||||||
|
{
|
||||||
_config.VideosDirectory = dlg.FileName;
|
_config.VideosDirectory = dlg.FileName;
|
||||||
|
await SaveUserSettings();
|
||||||
|
}
|
||||||
|
|
||||||
ReloadFiles();
|
ReloadFiles();
|
||||||
}
|
}
|
||||||
|
private void TbFilter_OnTextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
FilteredMediaFiles = new ObservableCollection<MediaFileInfo>(AllMediaFiles.Where(x => x.Name.ToLower().Contains(TbFilter.Text.ToLower())).ToList());
|
||||||
|
LvFiles.ItemsSource = FilteredMediaFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenDataExplorerItemClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_datasetExplorer.Show();
|
||||||
|
_datasetExplorer.Activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void PlayClick(object sender, RoutedEventArgs e)
|
private void PlayClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -354,4 +375,3 @@ public partial class MainWindow
|
|||||||
|
|
||||||
private void Thumb_OnDragCompleted(object sender, DragCompletedEventArgs e) => _ = SaveUserSettings();
|
private void Thumb_OnDragCompleted(object sender, DragCompletedEventArgs e) => _ = SaveUserSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,17 +8,19 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Azaion.Annotator;
|
namespace Azaion.Annotator;
|
||||||
|
|
||||||
public class PlayerControlHandler(LibVLC libVLC,
|
public class PlayerControlHandler :
|
||||||
MediaPlayer mediaPlayer,
|
|
||||||
MainWindow mainWindow,
|
|
||||||
FormState formState,
|
|
||||||
Config config,
|
|
||||||
ILogger<PlayerControlHandler> logger) :
|
|
||||||
INotificationHandler<KeyEvent>,
|
INotificationHandler<KeyEvent>,
|
||||||
INotificationHandler<AnnClassSelectedEvent>,
|
INotificationHandler<AnnClassSelectedEvent>,
|
||||||
INotificationHandler<PlaybackControlEvent>,
|
INotificationHandler<PlaybackControlEvent>,
|
||||||
INotificationHandler<VolumeChangedEvent>
|
INotificationHandler<VolumeChangedEvent>
|
||||||
{
|
{
|
||||||
|
private readonly LibVLC _libVLC;
|
||||||
|
private readonly MediaPlayer _mediaPlayer;
|
||||||
|
private readonly MainWindow _mainWindow;
|
||||||
|
private readonly FormState _formState;
|
||||||
|
private readonly Config _config;
|
||||||
|
private readonly ILogger<PlayerControlHandler> _logger;
|
||||||
|
|
||||||
private const int STEP = 20;
|
private const int STEP = 20;
|
||||||
private const int LARGE_STEP = 5000;
|
private const int LARGE_STEP = 5000;
|
||||||
private const int RESULT_WIDTH = 1280;
|
private const int RESULT_WIDTH = 1280;
|
||||||
@@ -35,6 +37,21 @@ public class PlayerControlHandler(LibVLC libVLC,
|
|||||||
{ Key.PageDown, PlaybackControlEnum.Next },
|
{ Key.PageDown, PlaybackControlEnum.Next },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public PlayerControlHandler(LibVLC libVLC,
|
||||||
|
MediaPlayer mediaPlayer,
|
||||||
|
MainWindow mainWindow,
|
||||||
|
FormState formState,
|
||||||
|
Config config,
|
||||||
|
ILogger<PlayerControlHandler> logger)
|
||||||
|
{
|
||||||
|
_libVLC = libVLC;
|
||||||
|
_mediaPlayer = mediaPlayer;
|
||||||
|
_mainWindow = mainWindow;
|
||||||
|
_formState = formState;
|
||||||
|
_config = config;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Handle(AnnClassSelectedEvent notification, CancellationToken cancellationToken)
|
public async Task Handle(AnnClassSelectedEvent notification, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
SelectClass(notification.AnnotationClass);
|
SelectClass(notification.AnnotationClass);
|
||||||
@@ -44,15 +61,15 @@ public class PlayerControlHandler(LibVLC libVLC,
|
|||||||
|
|
||||||
private void SelectClass(AnnotationClass annClass)
|
private void SelectClass(AnnotationClass annClass)
|
||||||
{
|
{
|
||||||
mainWindow.Editor.CurrentAnnClass = annClass;
|
_mainWindow.Editor.CurrentAnnClass = annClass;
|
||||||
foreach (var ann in mainWindow.Editor.CurrentAnns.Where(x => x.IsSelected))
|
foreach (var ann in _mainWindow.Editor.CurrentAnns.Where(x => x.IsSelected))
|
||||||
ann.AnnotationClass = annClass;
|
ann.AnnotationClass = annClass;
|
||||||
mainWindow.LvClasses.SelectedIndex = annClass.Id;
|
_mainWindow.LvClasses.SelectedIndex = annClass.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Handle(KeyEvent notification, CancellationToken cancellationToken)
|
public async Task Handle(KeyEvent notification, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
logger.LogInformation($"Catch {notification.Args.Key} by {notification.Sender.GetType().Name}");
|
_logger.LogInformation($"Catch {notification.Args.Key} by {notification.Sender.GetType().Name}");
|
||||||
|
|
||||||
var key = notification.Args.Key;
|
var key = notification.Args.Key;
|
||||||
var keyNumber = (int?)null;
|
var keyNumber = (int?)null;
|
||||||
@@ -62,7 +79,7 @@ public class PlayerControlHandler(LibVLC libVLC,
|
|||||||
if ((int)key >= (int)Key.NumPad1 && (int)key <= (int)Key.NumPad9)
|
if ((int)key >= (int)Key.NumPad1 && (int)key <= (int)Key.NumPad9)
|
||||||
keyNumber = key - Key.NumPad1;
|
keyNumber = key - Key.NumPad1;
|
||||||
if (keyNumber.HasValue)
|
if (keyNumber.HasValue)
|
||||||
SelectClass(mainWindow.AnnotationClasses[keyNumber.Value]);
|
SelectClass(_mainWindow.AnnotationClasses[keyNumber.Value]);
|
||||||
|
|
||||||
if (_keysControlEnumDict.TryGetValue(key, out var value))
|
if (_keysControlEnumDict.TryGetValue(key, out var value))
|
||||||
await ControlPlayback(value);
|
await ControlPlayback(value);
|
||||||
@@ -74,7 +91,7 @@ public class PlayerControlHandler(LibVLC libVLC,
|
|||||||
{
|
{
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case Key.VolumeMute when mediaPlayer.Volume == 0:
|
case Key.VolumeMute when _mediaPlayer.Volume == 0:
|
||||||
await ControlPlayback(PlaybackControlEnum.TurnOnVolume);
|
await ControlPlayback(PlaybackControlEnum.TurnOnVolume);
|
||||||
break;
|
break;
|
||||||
case Key.VolumeMute:
|
case Key.VolumeMute:
|
||||||
@@ -82,15 +99,15 @@ public class PlayerControlHandler(LibVLC libVLC,
|
|||||||
break;
|
break;
|
||||||
case Key.Up:
|
case Key.Up:
|
||||||
case Key.VolumeUp:
|
case Key.VolumeUp:
|
||||||
var vUp = Math.Min(100, mediaPlayer.Volume + 5);
|
var vUp = Math.Min(100, _mediaPlayer.Volume + 5);
|
||||||
ChangeVolume(vUp);
|
ChangeVolume(vUp);
|
||||||
mainWindow.Volume.Value = vUp;
|
_mainWindow.Volume.Value = vUp;
|
||||||
break;
|
break;
|
||||||
case Key.Down:
|
case Key.Down:
|
||||||
case Key.VolumeDown:
|
case Key.VolumeDown:
|
||||||
var vDown = Math.Max(0, mediaPlayer.Volume - 5);
|
var vDown = Math.Max(0, _mediaPlayer.Volume - 5);
|
||||||
ChangeVolume(vDown);
|
ChangeVolume(vDown);
|
||||||
mainWindow.Volume.Value = vDown;
|
_mainWindow.Volume.Value = vDown;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,7 +115,7 @@ public class PlayerControlHandler(LibVLC libVLC,
|
|||||||
public async Task Handle(PlaybackControlEvent notification, CancellationToken cancellationToken)
|
public async Task Handle(PlaybackControlEvent notification, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await ControlPlayback(notification.PlaybackControl);
|
await ControlPlayback(notification.PlaybackControl);
|
||||||
mainWindow.VideoView.Focus();
|
_mainWindow.VideoView.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ControlPlayback(PlaybackControlEnum controlEnum)
|
private async Task ControlPlayback(PlaybackControlEnum controlEnum)
|
||||||
@@ -114,42 +131,42 @@ public class PlayerControlHandler(LibVLC libVLC,
|
|||||||
await Play();
|
await Play();
|
||||||
break;
|
break;
|
||||||
case PlaybackControlEnum.Pause:
|
case PlaybackControlEnum.Pause:
|
||||||
mediaPlayer.Pause();
|
_mediaPlayer.Pause();
|
||||||
if (!mediaPlayer.IsPlaying)
|
if (!_mediaPlayer.IsPlaying)
|
||||||
mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.AnnotationHelp]);
|
_mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.AnnotationHelp]);
|
||||||
break;
|
break;
|
||||||
case PlaybackControlEnum.Stop:
|
case PlaybackControlEnum.Stop:
|
||||||
mediaPlayer.Stop();
|
_mediaPlayer.Stop();
|
||||||
break;
|
break;
|
||||||
case PlaybackControlEnum.PreviousFrame:
|
case PlaybackControlEnum.PreviousFrame:
|
||||||
mediaPlayer.SetPause(true);
|
_mediaPlayer.SetPause(true);
|
||||||
mediaPlayer.Time -= step;
|
_mediaPlayer.Time -= step;
|
||||||
mainWindow.VideoSlider.Value = mediaPlayer.Position * 100;
|
_mainWindow.VideoSlider.Value = _mediaPlayer.Position * 100;
|
||||||
break;
|
break;
|
||||||
case PlaybackControlEnum.NextFrame:
|
case PlaybackControlEnum.NextFrame:
|
||||||
mediaPlayer.SetPause(true);
|
_mediaPlayer.SetPause(true);
|
||||||
mediaPlayer.Time += step;
|
_mediaPlayer.Time += step;
|
||||||
mainWindow.VideoSlider.Value = mediaPlayer.Position * 100;
|
_mainWindow.VideoSlider.Value = _mediaPlayer.Position * 100;
|
||||||
break;
|
break;
|
||||||
case PlaybackControlEnum.SaveAnnotations:
|
case PlaybackControlEnum.SaveAnnotations:
|
||||||
await SaveAnnotations();
|
await SaveAnnotations();
|
||||||
break;
|
break;
|
||||||
case PlaybackControlEnum.RemoveSelectedAnns:
|
case PlaybackControlEnum.RemoveSelectedAnns:
|
||||||
mainWindow.Editor.RemoveSelectedAnns();
|
_mainWindow.Editor.RemoveSelectedAnns();
|
||||||
break;
|
break;
|
||||||
case PlaybackControlEnum.RemoveAllAnns:
|
case PlaybackControlEnum.RemoveAllAnns:
|
||||||
mainWindow.Editor.RemoveAllAnns();
|
_mainWindow.Editor.RemoveAllAnns();
|
||||||
break;
|
break;
|
||||||
case PlaybackControlEnum.TurnOnVolume:
|
case PlaybackControlEnum.TurnOnVolume:
|
||||||
mainWindow.TurnOnVolumeBtn.Visibility = Visibility.Collapsed;
|
_mainWindow.TurnOnVolumeBtn.Visibility = Visibility.Collapsed;
|
||||||
mainWindow.TurnOffVolumeBtn.Visibility = Visibility.Visible;
|
_mainWindow.TurnOffVolumeBtn.Visibility = Visibility.Visible;
|
||||||
mediaPlayer.Volume = formState.CurrentVolume;
|
_mediaPlayer.Volume = _formState.CurrentVolume;
|
||||||
break;
|
break;
|
||||||
case PlaybackControlEnum.TurnOffVolume:
|
case PlaybackControlEnum.TurnOffVolume:
|
||||||
mainWindow.TurnOffVolumeBtn.Visibility = Visibility.Collapsed;
|
_mainWindow.TurnOffVolumeBtn.Visibility = Visibility.Collapsed;
|
||||||
mainWindow.TurnOnVolumeBtn.Visibility = Visibility.Visible;
|
_mainWindow.TurnOnVolumeBtn.Visibility = Visibility.Visible;
|
||||||
formState.CurrentVolume = mediaPlayer.Volume;
|
_formState.CurrentVolume = _mediaPlayer.Volume;
|
||||||
mediaPlayer.Volume = 0;
|
_mediaPlayer.Volume = 0;
|
||||||
break;
|
break;
|
||||||
case PlaybackControlEnum.Previous:
|
case PlaybackControlEnum.Previous:
|
||||||
await NextMedia(isPrevious: true);
|
await NextMedia(isPrevious: true);
|
||||||
@@ -173,11 +190,11 @@ public class PlayerControlHandler(LibVLC libVLC,
|
|||||||
private async Task NextMedia(bool isPrevious = false)
|
private async Task NextMedia(bool isPrevious = false)
|
||||||
{
|
{
|
||||||
var increment = isPrevious ? -1 : 1;
|
var increment = isPrevious ? -1 : 1;
|
||||||
var check = isPrevious ? -1 : mainWindow.LvFiles.Items.Count;
|
var check = isPrevious ? -1 : _mainWindow.LvFiles.Items.Count;
|
||||||
if (mainWindow.LvFiles.SelectedIndex + increment == check)
|
if (_mainWindow.LvFiles.SelectedIndex + increment == check)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mainWindow.LvFiles.SelectedIndex += increment;
|
_mainWindow.LvFiles.SelectedIndex += increment;
|
||||||
await Play();
|
await Play();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,63 +206,63 @@ public class PlayerControlHandler(LibVLC libVLC,
|
|||||||
|
|
||||||
private void ChangeVolume(int volume)
|
private void ChangeVolume(int volume)
|
||||||
{
|
{
|
||||||
formState.CurrentVolume = volume;
|
_formState.CurrentVolume = volume;
|
||||||
mediaPlayer.Volume = volume;
|
_mediaPlayer.Volume = volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Play()
|
private async Task Play()
|
||||||
{
|
{
|
||||||
if (mainWindow.LvFiles.SelectedItem == null)
|
if (_mainWindow.LvFiles.SelectedItem == null)
|
||||||
return;
|
return;
|
||||||
var mediaInfo = (MediaFileInfo)mainWindow.LvFiles.SelectedItem;
|
var mediaInfo = (MediaFileInfo)_mainWindow.LvFiles.SelectedItem;
|
||||||
|
|
||||||
formState.CurrentMedia = mediaInfo;
|
_formState.CurrentMedia = mediaInfo;
|
||||||
mediaPlayer.Stop();
|
_mediaPlayer.Stop();
|
||||||
mainWindow.Title = $"Azaion Annotator - {mediaInfo.Name}";
|
_mainWindow.Title = $"Azaion Annotator - {mediaInfo.Name}";
|
||||||
mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.PauseForAnnotations]);
|
_mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.PauseForAnnotations]);
|
||||||
mediaPlayer.Play(new Media(libVLC, mediaInfo.Path));
|
_mediaPlayer.Play(new Media(_libVLC, mediaInfo.Path));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveAnnotations()
|
private async Task SaveAnnotations()
|
||||||
{
|
{
|
||||||
if (formState.CurrentMedia == null)
|
if (_formState.CurrentMedia == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var time = TimeSpan.FromMilliseconds(mediaPlayer.Time);
|
var time = TimeSpan.FromMilliseconds(_mediaPlayer.Time);
|
||||||
var fName = formState.GetTimeName(time);
|
var fName = _formState.GetTimeName(time);
|
||||||
|
|
||||||
var currentAnns = mainWindow.Editor.CurrentAnns
|
var currentAnns = _mainWindow.Editor.CurrentAnns
|
||||||
.Select(x => new YoloLabel(x.Info, mainWindow.Editor.RenderSize, formState.CurrentVideoSize))
|
.Select(x => new YoloLabel(x.Info, _mainWindow.Editor.RenderSize, _formState.CurrentVideoSize))
|
||||||
.ToList();
|
.ToList();
|
||||||
var labels = string.Join(Environment.NewLine, currentAnns.Select(x => x.ToString()));
|
var labels = string.Join(Environment.NewLine, currentAnns.Select(x => x.ToString()));
|
||||||
|
|
||||||
if (!Directory.Exists(config.LabelsDirectory))
|
if (!Directory.Exists(_config.LabelsDirectory))
|
||||||
Directory.CreateDirectory(config.LabelsDirectory);
|
Directory.CreateDirectory(_config.LabelsDirectory);
|
||||||
if (!Directory.Exists(config.ImagesDirectory))
|
if (!Directory.Exists(_config.ImagesDirectory))
|
||||||
Directory.CreateDirectory(config.ImagesDirectory);
|
Directory.CreateDirectory(_config.ImagesDirectory);
|
||||||
if (!Directory.Exists(config.ResultsDirectory))
|
if (!Directory.Exists(_config.ResultsDirectory))
|
||||||
Directory.CreateDirectory(config.ResultsDirectory);
|
Directory.CreateDirectory(_config.ResultsDirectory);
|
||||||
|
|
||||||
await File.WriteAllTextAsync(Path.Combine(config.LabelsDirectory, $"{fName}.txt"), labels);
|
await File.WriteAllTextAsync(Path.Combine(_config.LabelsDirectory, $"{fName}.txt"), labels);
|
||||||
var resultHeight = (uint)Math.Round(RESULT_WIDTH / formState.CurrentVideoSize.Width * formState.CurrentVideoSize.Height);
|
var resultHeight = (uint)Math.Round(RESULT_WIDTH / _formState.CurrentVideoSize.Width * _formState.CurrentVideoSize.Height);
|
||||||
|
|
||||||
await mainWindow.AddAnnotation(time, currentAnns);
|
await _mainWindow.AddAnnotation(time, currentAnns);
|
||||||
|
|
||||||
formState.CurrentMedia.HasAnnotations = mainWindow.Annotations.Count != 0;
|
_formState.CurrentMedia.HasAnnotations = _mainWindow.Annotations.Count != 0;
|
||||||
mainWindow.LvFiles.Items.Refresh();
|
_mainWindow.LvFiles.Items.Refresh();
|
||||||
|
|
||||||
var isVideo = formState.CurrentMedia.MediaType == MediaTypes.Video;
|
var isVideo = _formState.CurrentMedia.MediaType == MediaTypes.Video;
|
||||||
var destinationPath = Path.Combine(config.ImagesDirectory, $"{fName}{(isVideo ? ".jpg" : Path.GetExtension(formState.CurrentMedia.Path))}");
|
var destinationPath = Path.Combine(_config.ImagesDirectory, $"{fName}{(isVideo ? ".jpg" : Path.GetExtension(_formState.CurrentMedia.Path))}");
|
||||||
|
|
||||||
mainWindow.Editor.RemoveAllAnns();
|
_mainWindow.Editor.RemoveAllAnns();
|
||||||
if (isVideo)
|
if (isVideo)
|
||||||
{
|
{
|
||||||
mediaPlayer.TakeSnapshot(0, destinationPath, RESULT_WIDTH, resultHeight);
|
_mediaPlayer.TakeSnapshot(0, destinationPath, RESULT_WIDTH, resultHeight);
|
||||||
mediaPlayer.Play();
|
_mediaPlayer.Play();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
File.Copy(formState.CurrentMedia.Path, destinationPath, overwrite: true);
|
File.Copy(_formState.CurrentMedia.Path, destinationPath, overwrite: true);
|
||||||
await NextMedia();
|
await NextMedia();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,60 +3,25 @@
|
|||||||
"LabelsDirectory": "E:\\labels",
|
"LabelsDirectory": "E:\\labels",
|
||||||
"ImagesDirectory": "E:\\images",
|
"ImagesDirectory": "E:\\images",
|
||||||
"ResultsDirectory": "E:\\results",
|
"ResultsDirectory": "E:\\results",
|
||||||
|
"ThumbnailsDirectory": "E:\\thumbnails",
|
||||||
"AnnotationClasses": [
|
"AnnotationClasses": [
|
||||||
{
|
{ "Id": 0, "Name": "Броньована техніка", "Color": "#40FF0000" },
|
||||||
"Id": 0,
|
{ "Id": 1, "Name": "Вантажівка", "Color": "#4000FF00" },
|
||||||
"Name": "Броньована техніка",
|
{ "Id": 2, "Name": "Машина легкова", "Color": "#400000FF" },
|
||||||
"Color": "#40FF0000"
|
{ "Id": 3, "Name": "Артилерія", "Color": "#40FFFF00" },
|
||||||
},
|
{ "Id": 4, "Name": "Тінь від техніки", "Color": "#40FF00FF" },
|
||||||
{
|
{ "Id": 5, "Name": "Окопи", "Color": "#4000FFFF" },
|
||||||
"Id": 1,
|
{ "Id": 6, "Name": "Військовий", "Color": "#40000000" },
|
||||||
"Name": "Вантажівка",
|
{ "Id": 7, "Name": "Накати", "Color": "#40800000" },
|
||||||
"Color": "#4000FF00"
|
{ "Id": 8, "Name": "Танк з захистом", "Color": "#40008000" },
|
||||||
},
|
{ "Id": 9, "Name": "Дим", "Color": "#40000080" }
|
||||||
{
|
|
||||||
"Id": 2,
|
|
||||||
"Name": "Машина легкова",
|
|
||||||
"Color": "#400000FF"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": 3,
|
|
||||||
"Name": "Артилерія",
|
|
||||||
"Color": "#40FFFF00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": 4,
|
|
||||||
"Name": "Тінь від техніки",
|
|
||||||
"Color": "#40FF00FF"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": 5,
|
|
||||||
"Name": "Окопи",
|
|
||||||
"Color": "#4000FFFF"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": 6,
|
|
||||||
"Name": "Військовий",
|
|
||||||
"Color": "#40000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": 7,
|
|
||||||
"Name": "Накати",
|
|
||||||
"Color": "#40800000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": 8,
|
|
||||||
"Name": "Танк з захистом",
|
|
||||||
"Color": "#40008000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": 9,
|
|
||||||
"Name": "Дим",
|
|
||||||
"Color": "#40000080"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"WindowSize": "1920,1080",
|
"WindowSize": "1920,1080",
|
||||||
"WindowLocation": "200,121",
|
"WindowLocation": "200,121",
|
||||||
|
"ThumbnailConfig": {
|
||||||
|
"Size": "480,270",
|
||||||
|
"Border": 10
|
||||||
|
},
|
||||||
"FullScreen": true,
|
"FullScreen": true,
|
||||||
"LeftPanelWidth": 300,
|
"LeftPanelWidth": 300,
|
||||||
"RightPanelWidth": 300,
|
"RightPanelWidth": 300,
|
||||||
|
|||||||
Reference in New Issue
Block a user