mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 11:16:30 +00:00
fix initialization, throttle operations
day/winter/night switcher fixes
This commit is contained in:
@@ -51,7 +51,7 @@ public partial class Annotator
|
|||||||
|
|
||||||
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 static readonly Guid SaveConfigTaskId = Guid.NewGuid();
|
||||||
|
|
||||||
public ObservableCollection<MediaFileInfo> AllMediaFiles { get; set; } = new();
|
public ObservableCollection<MediaFileInfo> AllMediaFiles { get; set; } = new();
|
||||||
public ObservableCollection<MediaFileInfo> FilteredMediaFiles { get; set; } = new();
|
public ObservableCollection<MediaFileInfo> FilteredMediaFiles { get; set; } = new();
|
||||||
@@ -72,6 +72,7 @@ public partial class Annotator
|
|||||||
IInferenceService inferenceService)
|
IInferenceService inferenceService)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
_appConfig = appConfig.Value;
|
_appConfig = appConfig.Value;
|
||||||
_configUpdater = configUpdater;
|
_configUpdater = configUpdater;
|
||||||
_libVLC = libVLC;
|
_libVLC = libVLC;
|
||||||
@@ -119,9 +120,7 @@ public partial class Annotator
|
|||||||
_suspendLayout = false;
|
_suspendLayout = false;
|
||||||
TbFolder.Text = _appConfig.DirectoriesConfig.VideosDirectory;
|
TbFolder.Text = _appConfig.DirectoriesConfig.VideosDirectory;
|
||||||
|
|
||||||
AnnotationClasses = new ObservableCollection<DetectionClass>(_appConfig.AnnotationConfig.AnnotationClasses);
|
LvClasses.Init(_appConfig.AnnotationConfig.DetectionClasses);
|
||||||
LvClasses.DetectionDataGrid.ItemsSource = AnnotationClasses;
|
|
||||||
LvClasses.SelectNum(0);
|
|
||||||
|
|
||||||
if (LvFiles.Items.IsEmpty)
|
if (LvFiles.Items.IsEmpty)
|
||||||
BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.Initial]);
|
BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.Initial]);
|
||||||
@@ -261,7 +260,7 @@ public partial class Annotator
|
|||||||
{
|
{
|
||||||
_configUpdater.Save(_appConfig);
|
_configUpdater.Save(_appConfig);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}, TimeSpan.FromSeconds(5));
|
}, SaveConfigTaskId, TimeSpan.FromSeconds(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowTimeAnnotations(TimeSpan time)
|
private void ShowTimeAnnotations(TimeSpan time)
|
||||||
@@ -295,7 +294,7 @@ public partial class Annotator
|
|||||||
}
|
}
|
||||||
foreach (var detection in annotation.Detections)
|
foreach (var detection in annotation.Detections)
|
||||||
{
|
{
|
||||||
var annClass = _appConfig.AnnotationConfig.AnnotationClasses[detection.ClassNumber];
|
var annClass = _appConfig.AnnotationConfig.DetectionClasses[detection.ClassNumber];
|
||||||
var canvasLabel = new CanvasLabel(detection, canvasSize, videoSize, detection.Probability);
|
var canvasLabel = new CanvasLabel(detection, canvasSize, videoSize, detection.Probability);
|
||||||
Editor.CreateDetectionControl(annClass, annotation.Time, canvasLabel);
|
Editor.CreateDetectionControl(annClass, annotation.Time, canvasLabel);
|
||||||
}
|
}
|
||||||
@@ -312,6 +311,7 @@ public partial class Annotator
|
|||||||
var annotations = await _dbFactory.Run(async db =>
|
var annotations = await _dbFactory.Run(async db =>
|
||||||
await db.Annotations.LoadWith(x => x.Detections)
|
await db.Annotations.LoadWith(x => x.Detections)
|
||||||
.Where(x => x.OriginalMediaName == _formState.VideoName)
|
.Where(x => x.OriginalMediaName == _formState.VideoName)
|
||||||
|
.OrderBy(x => x.Time)
|
||||||
.ToListAsync(token: MainCancellationSource.Token));
|
.ToListAsync(token: MainCancellationSource.Token));
|
||||||
|
|
||||||
TimedAnnotations.Clear();
|
TimedAnnotations.Clear();
|
||||||
@@ -425,12 +425,6 @@ public partial class Annotator
|
|||||||
private void SeekTo(TimeSpan time) =>
|
private void SeekTo(TimeSpan time) =>
|
||||||
SeekTo((long)time.TotalMilliseconds);
|
SeekTo((long)time.TotalMilliseconds);
|
||||||
|
|
||||||
// private void AddClassBtnClick(object sender, RoutedEventArgs e)
|
|
||||||
// {
|
|
||||||
// LvClasses.IsReadOnly = false;
|
|
||||||
// DetectionClasses.Add(new DetectionClass(DetectionClasses.Count));
|
|
||||||
// LvClasses.SelectedIndex = DetectionClasses.Count - 1;
|
|
||||||
// }
|
|
||||||
private async void OpenFolderItemClick(object sender, RoutedEventArgs e) => await OpenFolder();
|
private async void OpenFolderItemClick(object sender, RoutedEventArgs e) => await OpenFolder();
|
||||||
private async void OpenFolderButtonClick(object sender, RoutedEventArgs e) => await OpenFolder();
|
private async void OpenFolderButtonClick(object sender, RoutedEventArgs e) => await OpenFolder();
|
||||||
|
|
||||||
@@ -521,8 +515,8 @@ public partial class Annotator
|
|||||||
{
|
{
|
||||||
//Take not annotataed medias
|
//Take not annotataed medias
|
||||||
files = (LvFiles.ItemsSource as IEnumerable<MediaFileInfo>)?.Skip(LvFiles.SelectedIndex)
|
files = (LvFiles.ItemsSource as IEnumerable<MediaFileInfo>)?.Skip(LvFiles.SelectedIndex)
|
||||||
.Take(Constants.DETECTION_BATCH_SIZE)
|
|
||||||
.Where(x => !x.HasAnnotations)
|
.Where(x => !x.HasAnnotations)
|
||||||
|
.Take(Constants.DETECTION_BATCH_SIZE)
|
||||||
.Select(x => x.Path)
|
.Select(x => x.Path)
|
||||||
.ToList() ?? [];
|
.ToList() ?? [];
|
||||||
if (files.Count != 0)
|
if (files.Count != 0)
|
||||||
|
|||||||
@@ -51,7 +51,8 @@
|
|||||||
CanUserResizeRows="False"
|
CanUserResizeRows="False"
|
||||||
CanUserResizeColumns="False"
|
CanUserResizeColumns="False"
|
||||||
SelectionChanged="DetectionDataGrid_SelectionChanged"
|
SelectionChanged="DetectionDataGrid_SelectionChanged"
|
||||||
x:FieldModifier="public">
|
x:FieldModifier="public"
|
||||||
|
>
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTemplateColumn Width="50" Header="Клавіша" CanUserSort="False">
|
<DataGridTemplateColumn Width="50" Header="Клавіша" CanUserSort="False">
|
||||||
<DataGridTemplateColumn.HeaderStyle>
|
<DataGridTemplateColumn.HeaderStyle>
|
||||||
@@ -90,13 +91,13 @@
|
|||||||
IsChecked="True"
|
IsChecked="True"
|
||||||
Style="{StaticResource ButtonRadioButtonStyle}"/>
|
Style="{StaticResource ButtonRadioButtonStyle}"/>
|
||||||
<RadioButton x:Name="EveningModeRadioButton"
|
<RadioButton x:Name="EveningModeRadioButton"
|
||||||
Tag="1"
|
Tag="20"
|
||||||
Content="Зима"
|
Content="Зима"
|
||||||
GroupName="Mode"
|
GroupName="Mode"
|
||||||
Checked="ModeRadioButton_Checked" Margin="10,0,0,0"
|
Checked="ModeRadioButton_Checked" Margin="10,0,0,0"
|
||||||
Style="{StaticResource ButtonRadioButtonStyle}"/>
|
Style="{StaticResource ButtonRadioButtonStyle}"/>
|
||||||
<RadioButton x:Name="NightModeRadioButton"
|
<RadioButton x:Name="NightModeRadioButton"
|
||||||
Tag="2"
|
Tag="40"
|
||||||
Content="Ніч"
|
Content="Ніч"
|
||||||
GroupName="Mode"
|
GroupName="Mode"
|
||||||
Checked="ModeRadioButton_Checked" Margin="10,0,0,0"
|
Checked="ModeRadioButton_Checked" Margin="10,0,0,0"
|
||||||
|
|||||||
@@ -4,16 +4,26 @@ using Azaion.Common.DTO;
|
|||||||
|
|
||||||
namespace Azaion.Common.Controls;
|
namespace Azaion.Common.Controls;
|
||||||
|
|
||||||
public class DetectionClassChangedEventArgs(DetectionClass? detectionClass, int classNumber) : EventArgs
|
public class DetectionClassChangedEventArgs(DetectionClass detectionClass, int classNumber) : EventArgs
|
||||||
{
|
{
|
||||||
public DetectionClass? DetectionClass { get; } = detectionClass;
|
public DetectionClass DetectionClass { get; } = detectionClass;
|
||||||
public int ClassNumber { get; } = classNumber;
|
public int ClassNumber { get; } = classNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class DetectionClasses
|
public partial class DetectionClasses
|
||||||
{
|
{
|
||||||
public event EventHandler<DetectionClassChangedEventArgs>? DetectionClassChanged;
|
public event EventHandler<DetectionClassChangedEventArgs>? DetectionClassChanged;
|
||||||
public DetectionClasses() => InitializeComponent();
|
|
||||||
|
public DetectionClasses()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init(List<DetectionClass> detectionClasses)
|
||||||
|
{
|
||||||
|
DetectionDataGrid.ItemsSource = detectionClasses;
|
||||||
|
DetectionDataGrid.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
public int CurrentClassNumber { get; private set; } = 0;
|
public int CurrentClassNumber { get; private set; } = 0;
|
||||||
public DetectionClass? CurrentDetectionClass { get; set; }
|
public DetectionClass? CurrentDetectionClass { get; set; }
|
||||||
@@ -27,26 +37,25 @@ public partial class DetectionClasses
|
|||||||
private void RaiseDetectionClassChanged()
|
private void RaiseDetectionClassChanged()
|
||||||
{
|
{
|
||||||
var detClass = (DetectionClass)DetectionDataGrid.SelectedItem;
|
var detClass = (DetectionClass)DetectionDataGrid.SelectedItem;
|
||||||
var baseClassNumber = detClass?.Id ?? 0;
|
if (detClass == null)
|
||||||
|
return;
|
||||||
|
|
||||||
var modeAmplifier = 0;
|
var modeAmplifier = 0;
|
||||||
foreach (var child in ModeSwitcherPanel.Children)
|
foreach (var child in ModeSwitcherPanel.Children)
|
||||||
if (child is RadioButton { IsChecked: true } rb)
|
if (child is RadioButton { IsChecked: true } rb)
|
||||||
if (int.TryParse(rb.Tag?.ToString(), out int modeIndex))
|
if (int.TryParse(rb.Tag?.ToString(), out int modeIndex))
|
||||||
{
|
{
|
||||||
if (detClass != null)
|
|
||||||
detClass.PhotoMode = (PhotoMode)modeIndex;
|
detClass.PhotoMode = (PhotoMode)modeIndex;
|
||||||
modeAmplifier += modeIndex * 20;
|
modeAmplifier += modeIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentDetectionClass = detClass;
|
CurrentDetectionClass = detClass;
|
||||||
CurrentClassNumber = baseClassNumber + modeAmplifier;
|
CurrentClassNumber = detClass.Id + modeAmplifier;
|
||||||
|
|
||||||
DetectionClassChanged?.Invoke(this, new DetectionClassChangedEventArgs(detClass, CurrentClassNumber));
|
DetectionClassChanged?.Invoke(this, new DetectionClassChangedEventArgs(detClass, CurrentClassNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void SelectNum(int keyNumber)
|
public void SelectNum(int keyNumber)
|
||||||
{
|
{
|
||||||
DetectionDataGrid.SelectedIndex = keyNumber;
|
DetectionDataGrid.SelectedIndex = keyNumber;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public class DetectionControl : Border
|
|||||||
_resizeStart = resizeStart;
|
_resizeStart = resizeStart;
|
||||||
_classNameLabel = new TextBlock
|
_classNameLabel = new TextBlock
|
||||||
{
|
{
|
||||||
Text = detectionClass.Name,
|
Text = detectionClass.UIName,
|
||||||
HorizontalAlignment = HorizontalAlignment.Center,
|
HorizontalAlignment = HorizontalAlignment.Center,
|
||||||
VerticalAlignment = VerticalAlignment.Top,
|
VerticalAlignment = VerticalAlignment.Top,
|
||||||
Margin = new Thickness(0, 15, 0, 0),
|
Margin = new Thickness(0, 15, 0, 0),
|
||||||
@@ -120,7 +120,7 @@ public class DetectionControl : Border
|
|||||||
|
|
||||||
public YoloLabel GetLabel(Size canvasSize, Size? videoSize = null)
|
public YoloLabel GetLabel(Size canvasSize, Size? videoSize = null)
|
||||||
{
|
{
|
||||||
var label = new CanvasLabel(DetectionClass.Id, Canvas.GetLeft(this), Canvas.GetTop(this), Width, Height);
|
var label = new CanvasLabel(DetectionClass.YoloId, Canvas.GetLeft(this), Canvas.GetTop(this), Width, Height);
|
||||||
return new YoloLabel(label, canvasSize, videoSize);
|
return new YoloLabel(label, canvasSize, videoSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ public class AnnotationResult
|
|||||||
var detectionClasses = detections.Select(x => x.ClassNumber).Distinct().ToList();
|
var detectionClasses = detections.Select(x => x.ClassNumber).Distinct().ToList();
|
||||||
|
|
||||||
ClassName = detectionClasses.Count > 1
|
ClassName = detectionClasses.Count > 1
|
||||||
? string.Join(", ", detectionClasses.Select(x => allDetectionClasses[x].ShortName))
|
? string.Join(", ", detectionClasses.Select(x => allDetectionClasses[x].UIName))
|
||||||
: allDetectionClasses[detectionClasses.FirstOrDefault()].Name;
|
: allDetectionClasses[detectionClasses.FirstOrDefault()].UIName;
|
||||||
|
|
||||||
ClassColor0 = GetAnnotationClass(detectionClasses, 0);
|
ClassColor0 = GetAnnotationClass(detectionClasses, 0);
|
||||||
ClassColor1 = GetAnnotationClass(detectionClasses, 1);
|
ClassColor1 = GetAnnotationClass(detectionClasses, 1);
|
||||||
|
|||||||
@@ -4,12 +4,32 @@ namespace Azaion.Common.DTO.Config;
|
|||||||
|
|
||||||
public class AnnotationConfig
|
public class AnnotationConfig
|
||||||
{
|
{
|
||||||
public List<DetectionClass> AnnotationClasses { get; set; } = null!;
|
public List<DetectionClass> DetectionClasses { get; set; } = null!;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
private Dictionary<int, DetectionClass>? _detectionClassesDict;
|
private Dictionary<int, DetectionClass>? _detectionClassesDict;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Dictionary<int, DetectionClass> DetectionClassesDict => _detectionClassesDict ??= AnnotationClasses.ToDictionary(x => x.Id);
|
public Dictionary<int, DetectionClass> DetectionClassesDict
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_detectionClassesDict != null)
|
||||||
|
return _detectionClassesDict;
|
||||||
|
|
||||||
|
var photoModes = Enum.GetValues(typeof(PhotoMode)).Cast<PhotoMode>().ToList();
|
||||||
|
|
||||||
|
_detectionClassesDict = DetectionClasses.SelectMany(cls => photoModes.Select(mode => new DetectionClass
|
||||||
|
{
|
||||||
|
Id = cls.Id,
|
||||||
|
Name = cls.Name,
|
||||||
|
ShortName = cls.ShortName,
|
||||||
|
PhotoMode = mode
|
||||||
|
}))
|
||||||
|
.ToDictionary(x => x.YoloId, x => x);
|
||||||
|
return _detectionClassesDict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<string> VideoFormats { get; set; } = null!;
|
public List<string> VideoFormats { get; set; } = null!;
|
||||||
public List<string> ImageFormats { get; set; } = null!;
|
public List<string> ImageFormats { get; set; } = null!;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class ConfigUpdater : IConfigUpdater
|
|||||||
{
|
{
|
||||||
AnnotationConfig = new AnnotationConfig
|
AnnotationConfig = new AnnotationConfig
|
||||||
{
|
{
|
||||||
AnnotationClasses = Constants.DefaultAnnotationClasses,
|
DetectionClasses = Constants.DefaultAnnotationClasses,
|
||||||
VideoFormats = Constants.DefaultVideoFormats,
|
VideoFormats = Constants.DefaultVideoFormats,
|
||||||
ImageFormats = Constants.DefaultImageFormats,
|
ImageFormats = Constants.DefaultImageFormats,
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,23 @@ public class DetectionClass
|
|||||||
public string Name { get; set; } = null!;
|
public string Name { get; set; } = null!;
|
||||||
public string ShortName { get; set; } = null!;
|
public string ShortName { get; set; } = null!;
|
||||||
|
|
||||||
|
public string UIName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var mode = PhotoMode switch
|
||||||
|
{
|
||||||
|
PhotoMode.Night => "(ніч)",
|
||||||
|
PhotoMode.Winter => "(зим)",
|
||||||
|
PhotoMode.Regular => "",
|
||||||
|
_ => ""
|
||||||
|
};
|
||||||
|
return ShortName + mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
public PhotoMode PhotoMode { get; set; }
|
public PhotoMode PhotoMode { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
@@ -19,6 +36,9 @@ public class DetectionClass
|
|||||||
[JsonIgnore] //For UI
|
[JsonIgnore] //For UI
|
||||||
public int ClassNumber => Id + 1;
|
public int ClassNumber => Id + 1;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public int YoloId => (int)PhotoMode + Id;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public SolidColorBrush ColorBrush => new(Color);
|
public SolidColorBrush ColorBrush => new(Color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,55 @@
|
|||||||
namespace Azaion.Common.Extensions;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace Azaion.Common.Extensions;
|
||||||
|
|
||||||
public static class ThrottleExt
|
public static class ThrottleExt
|
||||||
{
|
{
|
||||||
private static bool _throttleRunFirstOn;
|
private static ConcurrentDictionary<Guid, bool> _taskStates = new();
|
||||||
public static async Task ThrottleRunFirst(this Func<Task> func, TimeSpan? throttleTime = null, CancellationToken cancellationToken = default)
|
|
||||||
|
public static async Task ThrottleRunFirst(Func<Task> func, Guid actionId, TimeSpan? throttleTime = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (_throttleRunFirstOn)
|
if (_taskStates.ContainsKey(actionId) && _taskStates[actionId])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_throttleRunFirstOn = true;
|
_taskStates[actionId] = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
await func();
|
await func();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await Task.Delay(throttleTime ?? TimeSpan.FromMilliseconds(500), cancellationToken);
|
await Task.Delay(throttleTime ?? TimeSpan.FromMilliseconds(500), cancellationToken);
|
||||||
_throttleRunFirstOn = false;
|
_taskStates[actionId] = false;
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool _throttleRunAfter;
|
public static async Task ThrottleRunAfter(Func<Task> func, Guid actionId, TimeSpan? throttleTime = null, CancellationToken cancellationToken = default)
|
||||||
public static async Task ThrottleRunAfter(this Func<Task> func, TimeSpan? throttleTime = null, CancellationToken cancellationToken = default)
|
|
||||||
{
|
{
|
||||||
if (_throttleRunAfter)
|
if (_taskStates.ContainsKey(actionId) && _taskStates[actionId])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_throttleRunAfter = true;
|
_taskStates[actionId] = true;
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
await Task.Delay(throttleTime ?? TimeSpan.FromMilliseconds(500), cancellationToken);
|
await Task.Delay(throttleTime ?? TimeSpan.FromMilliseconds(500), cancellationToken);
|
||||||
await func();
|
await func();
|
||||||
_throttleRunAfter = false;
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_taskStates[actionId] = false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_taskStates[actionId] = false;
|
||||||
|
}
|
||||||
|
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ public class AnnotationService : INotificationHandler<AnnotationsDeletedEvent>
|
|||||||
private readonly IAuthProvider _authProvider;
|
private readonly IAuthProvider _authProvider;
|
||||||
private readonly QueueConfig _queueConfig;
|
private readonly QueueConfig _queueConfig;
|
||||||
private Consumer _consumer = null!;
|
private Consumer _consumer = null!;
|
||||||
|
private static readonly Guid SaveTaskId = Guid.NewGuid();
|
||||||
|
|
||||||
public AnnotationService(
|
public AnnotationService(
|
||||||
IResourceLoader resourceLoader,
|
IResourceLoader resourceLoader,
|
||||||
@@ -95,7 +96,7 @@ public class AnnotationService : INotificationHandler<AnnotationsDeletedEvent>
|
|||||||
{
|
{
|
||||||
_dbFactory.SaveToDisk();
|
_dbFactory.SaveToDisk();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}, TimeSpan.FromSeconds(3), cancellationToken);
|
}, SaveTaskId, TimeSpan.FromSeconds(3), cancellationToken);
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -184,7 +185,7 @@ public class AnnotationService : INotificationHandler<AnnotationsDeletedEvent>
|
|||||||
{
|
{
|
||||||
_dbFactory.SaveToDisk();
|
_dbFactory.SaveToDisk();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}, TimeSpan.FromSeconds(5), token);
|
}, SaveTaskId, TimeSpan.FromSeconds(5), token);
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class PythonResourceLoader : IResourceLoader, IAuthProvider
|
|||||||
|
|
||||||
public PythonResourceLoader(PythonConfig config)
|
public PythonResourceLoader(PythonConfig config)
|
||||||
{
|
{
|
||||||
//StartPython();
|
StartPython();
|
||||||
_dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N"));
|
_dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N"));
|
||||||
_dealer.Connect($"tcp://{config.ZeroMqHost}:{config.ZeroMqPort}");
|
_dealer.Connect($"tcp://{config.ZeroMqHost}:{config.ZeroMqPort}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,16 +57,13 @@
|
|||||||
<RowDefinition Height="32"></RowDefinition>
|
<RowDefinition Height="32"></RowDefinition>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="150" />
|
<ColumnDefinition Width="200" />
|
||||||
<ColumnDefinition Width="4"/>
|
<ColumnDefinition Width="4"/>
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<controls:DetectionClasses
|
<controls:DetectionClasses x:Name="LvClasses"
|
||||||
x:Name="LvClasses"
|
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.Row="0">
|
Grid.Row="0" />
|
||||||
</controls:DetectionClasses>
|
|
||||||
|
|
||||||
<TabControl
|
<TabControl
|
||||||
Name="Switcher"
|
Name="Switcher"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ public partial class DatasetExplorer
|
|||||||
private readonly AnnotationConfig _annotationConfig;
|
private readonly AnnotationConfig _annotationConfig;
|
||||||
private readonly DirectoriesConfig _directoriesConfig;
|
private readonly DirectoriesConfig _directoriesConfig;
|
||||||
|
|
||||||
private Dictionary<int, List<Annotation>> _annotationsDict = new();
|
private readonly Dictionary<int, List<Annotation>> _annotationsDict;
|
||||||
private readonly CancellationTokenSource _cts = new();
|
private readonly CancellationTokenSource _cts = new();
|
||||||
|
|
||||||
public ObservableCollection<DetectionClass> AllDetectionClasses { get; set; } = new();
|
public List<DetectionClass> AllDetectionClasses { get; set; }
|
||||||
public ObservableCollection<AnnotationThumbnail> SelectedAnnotations { get; set; } = new();
|
public ObservableCollection<AnnotationThumbnail> SelectedAnnotations { get; set; } = new();
|
||||||
public readonly Dictionary<string, AnnotationThumbnail> SelectedAnnotationDict = new();
|
public readonly Dictionary<string, AnnotationThumbnail> SelectedAnnotationDict = new();
|
||||||
|
|
||||||
@@ -34,6 +34,8 @@ public partial class DatasetExplorer
|
|||||||
private readonly IDbFactory _dbFactory;
|
private readonly IDbFactory _dbFactory;
|
||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
|
|
||||||
|
public readonly List<DetectionClass> AnnotationsClasses;
|
||||||
|
|
||||||
|
|
||||||
public bool ThumbnailLoading { get; set; }
|
public bool ThumbnailLoading { get; set; }
|
||||||
|
|
||||||
@@ -48,6 +50,8 @@ public partial class DatasetExplorer
|
|||||||
IDbFactory dbFactory,
|
IDbFactory dbFactory,
|
||||||
IMediator mediator)
|
IMediator mediator)
|
||||||
{
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
_directoriesConfig = directoriesConfig.Value;
|
_directoriesConfig = directoriesConfig.Value;
|
||||||
_annotationConfig = annotationConfig.Value;
|
_annotationConfig = annotationConfig.Value;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -55,7 +59,13 @@ public partial class DatasetExplorer
|
|||||||
_dbFactory = dbFactory;
|
_dbFactory = dbFactory;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
|
|
||||||
InitializeComponent();
|
var photoModes = Enum.GetValues(typeof(PhotoMode)).Cast<PhotoMode>().ToList();
|
||||||
|
_annotationsDict = _annotationConfig.DetectionClasses.SelectMany(cls => photoModes.Select(mode => (int)mode + cls.Id))
|
||||||
|
.ToDictionary(x => x, _ => new List<Annotation>());
|
||||||
|
_annotationsDict.Add(-1, []);
|
||||||
|
|
||||||
|
AnnotationsClasses = annotationConfig.Value.DetectionClasses;
|
||||||
|
|
||||||
Loaded += OnLoaded;
|
Loaded += OnLoaded;
|
||||||
Activated += (_, _) => formState.ActiveWindow = WindowEnum.DatasetExplorer;
|
Activated += (_, _) => formState.ActiveWindow = WindowEnum.DatasetExplorer;
|
||||||
|
|
||||||
@@ -86,15 +96,29 @@ public partial class DatasetExplorer
|
|||||||
Dispatcher.Invoke(() => RefreshThumbBar.Value = thumbnailsPercentage);
|
Dispatcher.Invoke(() => RefreshThumbBar.Value = thumbnailsPercentage);
|
||||||
};
|
};
|
||||||
Closing += (_, _) => _cts.Cancel();
|
Closing += (_, _) => _cts.Cancel();
|
||||||
|
|
||||||
|
AllDetectionClasses = new List<DetectionClass>(
|
||||||
|
new List<DetectionClass> { new() {Id = -1, Name = "All", ShortName = "All"}}
|
||||||
|
.Concat(_annotationConfig.DetectionClasses));
|
||||||
|
LvClasses.Init(AllDetectionClasses);
|
||||||
|
|
||||||
|
_dbFactory.Run(async db =>
|
||||||
|
{
|
||||||
|
var allAnnotations = await db.Annotations
|
||||||
|
.LoadWith(x => x.Detections)
|
||||||
|
.OrderBy(x => x.AnnotationStatus)
|
||||||
|
.ThenByDescending(x => x.CreatedDate)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
foreach (var annotation in allAnnotations)
|
||||||
|
AddAnnotationToDict(annotation);
|
||||||
|
}).GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
DataContext = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnLoaded(object sender, RoutedEventArgs e)
|
private async void OnLoaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
AllDetectionClasses = new ObservableCollection<DetectionClass>(
|
|
||||||
new List<DetectionClass> { new() {Id = -1, Name = "All", ShortName = "All"}}
|
|
||||||
.Concat(_annotationConfig.AnnotationClasses));
|
|
||||||
LvClasses.DetectionDataGrid.ItemsSource = AllDetectionClasses;
|
|
||||||
|
|
||||||
LvClasses.DetectionClassChanged += async (_, args) =>
|
LvClasses.DetectionClassChanged += async (_, args) =>
|
||||||
{
|
{
|
||||||
ExplorerEditor.CurrentAnnClass = args.DetectionClass;
|
ExplorerEditor.CurrentAnnClass = args.DetectionClass;
|
||||||
@@ -106,20 +130,8 @@ public partial class DatasetExplorer
|
|||||||
ann.DetectionClass = args.DetectionClass;
|
ann.DetectionClass = args.DetectionClass;
|
||||||
};
|
};
|
||||||
|
|
||||||
ExplorerEditor.CurrentAnnClass = (DetectionClass)LvClasses.CurrentDetectionClass;
|
ExplorerEditor.CurrentAnnClass = LvClasses.CurrentDetectionClass ?? _annotationConfig.DetectionClasses.First();
|
||||||
|
|
||||||
await _dbFactory.Run(async db =>
|
|
||||||
{
|
|
||||||
var allAnnotations = await db.Annotations
|
|
||||||
.LoadWith(x => x.Detections)
|
|
||||||
.OrderBy(x => x.AnnotationStatus)
|
|
||||||
.ThenByDescending(x => x.CreatedDate)
|
|
||||||
.ToListAsync();
|
|
||||||
_annotationsDict = AllDetectionClasses.ToDictionary(x => x.Id, _ => new List<Annotation>());
|
|
||||||
|
|
||||||
foreach (var annotation in allAnnotations)
|
|
||||||
AddAnnotationToDict(annotation);
|
|
||||||
});
|
|
||||||
await ReloadThumbnails();
|
await ReloadThumbnails();
|
||||||
await LoadClassDistribution();
|
await LoadClassDistribution();
|
||||||
|
|
||||||
@@ -239,7 +251,7 @@ public partial class DatasetExplorer
|
|||||||
AnnotationsTab.Visibility = Visibility.Collapsed;
|
AnnotationsTab.Visibility = Visibility.Collapsed;
|
||||||
EditorTab.Visibility = Visibility.Visible;
|
EditorTab.Visibility = Visibility.Visible;
|
||||||
_tempSelectedClassIdx = LvClasses.CurrentClassNumber;
|
_tempSelectedClassIdx = LvClasses.CurrentClassNumber;
|
||||||
LvClasses.DetectionDataGrid.ItemsSource = _annotationConfig.AnnotationClasses;
|
LvClasses.DetectionDataGrid.ItemsSource = _annotationConfig.DetectionClasses;
|
||||||
|
|
||||||
Switcher.SelectedIndex = 1;
|
Switcher.SelectedIndex = 1;
|
||||||
LvClasses.SelectNum(Math.Max(0, _tempSelectedClassIdx - 1));
|
LvClasses.SelectNum(Math.Max(0, _tempSelectedClassIdx - 1));
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public partial class App
|
|||||||
|
|
||||||
private PythonResourceLoader _resourceLoader = null!;
|
private PythonResourceLoader _resourceLoader = null!;
|
||||||
private Stream _securedConfig = null!;
|
private Stream _securedConfig = null!;
|
||||||
|
private static readonly Guid KeyPressTaskId = Guid.NewGuid();
|
||||||
|
|
||||||
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -199,7 +200,7 @@ public partial class App
|
|||||||
{
|
{
|
||||||
var args = (KeyEventArgs)e;
|
var args = (KeyEventArgs)e;
|
||||||
var keyEvent = new KeyEvent(sender, args, _formState.ActiveWindow);
|
var keyEvent = new KeyEvent(sender, args, _formState.ActiveWindow);
|
||||||
_ = ThrottleExt.ThrottleRunFirst(() => _mediator.Publish(keyEvent), TimeSpan.FromMilliseconds(50));
|
_ = ThrottleExt.ThrottleRunFirst(() => _mediator.Publish(keyEvent), KeyPressTaskId, TimeSpan.FromMilliseconds(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async void OnExit(ExitEventArgs e)
|
protected override async void OnExit(ExitEventArgs e)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using Azaion.Common.DTO.Config;
|
|||||||
using Azaion.Common.Extensions;
|
using Azaion.Common.Extensions;
|
||||||
using Azaion.Common.Services;
|
using Azaion.Common.Services;
|
||||||
using Azaion.CommonSecurity.Services;
|
using Azaion.CommonSecurity.Services;
|
||||||
using Azaion.Dataset;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using SharpVectors.Converters;
|
using SharpVectors.Converters;
|
||||||
@@ -26,6 +25,7 @@ public partial class MainSuite
|
|||||||
private readonly IDbFactory _dbFactory;
|
private readonly IDbFactory _dbFactory;
|
||||||
private readonly Dictionary<WindowEnum, Window> _openedWindows = new();
|
private readonly Dictionary<WindowEnum, Window> _openedWindows = new();
|
||||||
private readonly IResourceLoader _resourceLoader;
|
private readonly IResourceLoader _resourceLoader;
|
||||||
|
private static readonly Guid SaveConfigTaskId = Guid.NewGuid();
|
||||||
|
|
||||||
public MainSuite(IOptions<AppConfig> appConfig,
|
public MainSuite(IOptions<AppConfig> appConfig,
|
||||||
IConfigUpdater configUpdater,
|
IConfigUpdater configUpdater,
|
||||||
@@ -89,6 +89,7 @@ public partial class MainSuite
|
|||||||
};
|
};
|
||||||
lvItem.MouseUp += (lv, _) => OpenWindow((lv as ListViewItem)!);
|
lvItem.MouseUp += (lv, _) => OpenWindow((lv as ListViewItem)!);
|
||||||
ListView.Items.Add(lvItem);
|
ListView.Items.Add(lvItem);
|
||||||
|
_ = _sp.GetRequiredService(azaionModule.MainWindowType) as Window;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = Task.Run(async () => await _galleryService.RefreshThumbnails());
|
_ = Task.Run(async () => await _galleryService.RefreshThumbnails());
|
||||||
@@ -126,7 +127,7 @@ public partial class MainSuite
|
|||||||
{
|
{
|
||||||
_configUpdater.Save(_appConfig);
|
_configUpdater.Save(_appConfig);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}, TimeSpan.FromSeconds(2));
|
}, SaveConfigTaskId, TimeSpan.FromSeconds(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFormClosed(object? sender, EventArgs e)
|
private void OnFormClosed(object? sender, EventArgs e)
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
"ZeroMqHost": "127.0.0.1",
|
"ZeroMqHost": "127.0.0.1",
|
||||||
"ZeroMqPort": 5128,
|
"ZeroMqPort": 5128,
|
||||||
"RetryCount": 25,
|
"RetryCount": 25,
|
||||||
"TimeoutSeconds": 5
|
"TimeoutSeconds": 5,
|
||||||
|
"ResourcesFolder": "stage"
|
||||||
},
|
},
|
||||||
"DirectoriesConfig": {
|
"DirectoriesConfig": {
|
||||||
"VideosDirectory": "E:\\Azaion6",
|
"VideosDirectory": "E:\\Azaion6",
|
||||||
@@ -13,16 +14,16 @@
|
|||||||
"ThumbnailsDirectory": "E:\\thumbnails"
|
"ThumbnailsDirectory": "E:\\thumbnails"
|
||||||
},
|
},
|
||||||
"AnnotationConfig": {
|
"AnnotationConfig": {
|
||||||
"AnnotationClasses": [
|
"DetectionClasses": [
|
||||||
{ "Id": 0, "Name": "Броньована техніка", "ShortName": "Бронь" },
|
{ "Id": 0, "Name": "Броньована техніка", "ShortName": "Броня" },
|
||||||
{ "Id": 1, "Name": "Вантажівка", "ShortName": "Вантаж" },
|
{ "Id": 1, "Name": "Вантажівка", "ShortName": "Вантаж." },
|
||||||
{ "Id": 2, "Name": "Машина легкова", "ShortName": "Машина" },
|
{ "Id": 2, "Name": "Машина легкова", "ShortName": "Машина" },
|
||||||
{ "Id": 3, "Name": "Артилерія", "ShortName": "Арта" },
|
{ "Id": 3, "Name": "Артилерія", "ShortName": "Арта" },
|
||||||
{ "Id": 4, "Name": "Тінь від техніки", "ShortName": "Тінь" },
|
{ "Id": 4, "Name": "Тінь від техніки", "ShortName": "Тінь" },
|
||||||
{ "Id": 5, "Name": "Окопи", "ShortName": "Окопи" },
|
{ "Id": 5, "Name": "Окопи", "ShortName": "Окопи" },
|
||||||
{ "Id": 6, "Name": "Військовий", "ShortName": "Військов" },
|
{ "Id": 6, "Name": "Військовий", "ShortName": "Військов" },
|
||||||
{ "Id": 7, "Name": "Накати", "ShortName": "Накати" },
|
{ "Id": 7, "Name": "Накати", "ShortName": "Накати" },
|
||||||
{ "Id": 8, "Name": "Танк з захистом", "ShortName": "Танк захист" },
|
{ "Id": 8, "Name": "Танк з захистом", "ShortName": "Танк.захист" },
|
||||||
{ "Id": 9, "Name": "Дим", "ShortName": "Дим" },
|
{ "Id": 9, "Name": "Дим", "ShortName": "Дим" },
|
||||||
{ "Id": 10, "Name": "Літак", "ShortName": "Літак" },
|
{ "Id": 10, "Name": "Літак", "ShortName": "Літак" },
|
||||||
{ "Id": 11, "Name": "Мотоцикл", "ShortName": "Мото" }
|
{ "Id": 11, "Name": "Мотоцикл", "ShortName": "Мото" }
|
||||||
|
|||||||
+6
-2
@@ -1,6 +1,5 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
|
|
||||||
cd Azaion.Suite
|
cd Azaion.Suite
|
||||||
echo Build .net app
|
echo Build .net app
|
||||||
dotnet build -c Release
|
dotnet build -c Release
|
||||||
@@ -17,7 +16,7 @@ move dist\Azaion.Annotator.dll dist\dummy\
|
|||||||
move dist\Azaion.Dataset.dll dist\dummy\
|
move dist\Azaion.Dataset.dll dist\dummy\
|
||||||
|
|
||||||
echo Build Cython app
|
echo Build Cython app
|
||||||
cd Azaion.Inference\
|
cd Azaion.Inference
|
||||||
call ".\venv\Scripts\activate.bat"
|
call ".\venv\Scripts\activate.bat"
|
||||||
pyinstaller --onefile ^
|
pyinstaller --onefile ^
|
||||||
--collect-all jwt ^
|
--collect-all jwt ^
|
||||||
@@ -43,3 +42,8 @@ pyinstaller --onefile ^
|
|||||||
--hidden-import remote_command_handler ^
|
--hidden-import remote_command_handler ^
|
||||||
start.py
|
start.py
|
||||||
move dist\start.exe ..\dist\azaion-inference.exe
|
move dist\start.exe ..\dist\azaion-inference.exe
|
||||||
|
copy config.yaml ..\dist
|
||||||
|
|
||||||
|
echo Copy ico
|
||||||
|
cd ..
|
||||||
|
copy logo.ico dist\
|
||||||
Reference in New Issue
Block a user