move detection classes and other system values from local config to remote

forbid non validators to read from queue
create better visualization in detector control
make colors for detection classes more distinguishable
fix bug with removing detection (probably completely)
This commit is contained in:
Alex Bezdieniezhnykh
2025-04-02 19:53:03 +03:00
parent e182547dc8
commit 83ae6a0ae9
19 changed files with 209 additions and 169 deletions
+18 -7
View File
@@ -116,8 +116,8 @@ public partial class Annotator
_suspendLayout = true; _suspendLayout = true;
MainGrid.ColumnDefinitions.FirstOrDefault()!.Width = new GridLength(_appConfig.AnnotationConfig.LeftPanelWidth); MainGrid.ColumnDefinitions.FirstOrDefault()!.Width = new GridLength(_appConfig.UIConfig.LeftPanelWidth);
MainGrid.ColumnDefinitions.LastOrDefault()!.Width = new GridLength(_appConfig.AnnotationConfig.RightPanelWidth); MainGrid.ColumnDefinitions.LastOrDefault()!.Width = new GridLength(_appConfig.UIConfig.RightPanelWidth);
_suspendLayout = false; _suspendLayout = false;
TbFolder.Text = _appConfig.DirectoriesConfig.VideosDirectory; TbFolder.Text = _appConfig.DirectoriesConfig.VideosDirectory;
@@ -255,8 +255,8 @@ public partial class Annotator
if (_suspendLayout) if (_suspendLayout)
return; return;
_appConfig.AnnotationConfig.LeftPanelWidth = MainGrid.ColumnDefinitions.FirstOrDefault()!.Width.Value; _appConfig.UIConfig.LeftPanelWidth = MainGrid.ColumnDefinitions.FirstOrDefault()!.Width.Value;
_appConfig.AnnotationConfig.RightPanelWidth = MainGrid.ColumnDefinitions.LastOrDefault()!.Width.Value; _appConfig.UIConfig.RightPanelWidth = MainGrid.ColumnDefinitions.LastOrDefault()!.Width.Value;
await ThrottleExt.ThrottleRunFirst(() => await ThrottleExt.ThrottleRunFirst(() =>
{ {
@@ -329,8 +329,19 @@ public partial class Annotator
var existingResult = _formState.AnnotationResults.FirstOrDefault(x => x.Annotation.Time == time); var existingResult = _formState.AnnotationResults.FirstOrDefault(x => x.Annotation.Time == time);
if (existingResult != null) if (existingResult != null)
{ {
_logger.LogInformation($"remove annotation {existingResult.TimeStr} {existingResult.ClassName}!"); try
_formState.AnnotationResults.Remove(existingResult); {
_logger.LogInformation($"remove annotation {existingResult.TimeStr} {existingResult.ClassName}!");
_formState.AnnotationResults.Remove(existingResult);
_logger.LogInformation($"removed {existingResult.TimeStr} {existingResult.ClassName} sucessfully!");
}
catch (Exception e)
{
_logger.LogError(e, e.Message);
//Console.WriteLine(e);
throw;
}
} }
@@ -640,7 +651,7 @@ public class GradientStyleSelector : StyleSelector
}; };
var gradients = new List<GradientStop>(); var gradients = new List<GradientStop>();
if (result.Colors.Count != 0) if (result.Colors.Count == 0)
{ {
var color = (Color)ColorConverter.ConvertFromString("#40DDDDDD"); var color = (Color)ColorConverter.ConvertFromString("#40DDDDDD");
gradients = [new GradientStop(color, 0.99)]; gradients = [new GradientStop(color, 0.99)];
+1 -1
View File
@@ -275,7 +275,7 @@ public class AnnotatorEventHandler(
await NextMedia(ct: cancellationToken); await NextMedia(ct: cancellationToken);
} }
var annotation = await annotationService.SaveAnnotation(originalMediaName, time, currentDetections, SourceEnum.Manual, token: cancellationToken); var annotation = await annotationService.SaveAnnotation(originalMediaName, time, currentDetections, token: cancellationToken);
if (isVideo) if (isVideo)
mainWindow.AddAnnotation(annotation); mainWindow.AddAnnotation(annotation);
} }
+7 -10
View File
@@ -169,7 +169,8 @@ public class CanvasEditor : Canvas
Width = width, Width = width,
Height = height, Height = height,
X = Math.Min(endPos.X, _newAnnotationStartPos.X), X = Math.Min(endPos.X, _newAnnotationStartPos.X),
Y = Math.Min(endPos.Y, _newAnnotationStartPos.Y) Y = Math.Min(endPos.Y, _newAnnotationStartPos.Y),
Confidence = 1
}); });
} }
@@ -312,25 +313,21 @@ public class CanvasEditor : Canvas
{ {
foreach (var detection in detections) foreach (var detection in detections)
{ {
var annClass = DetectionClass.FromYoloId(detection.ClassNumber, detectionClasses); var detectionClass = DetectionClass.FromYoloId(detection.ClassNumber, detectionClasses);
var canvasLabel = new CanvasLabel(detection, RenderSize, videoSize, detection.Confidence); var canvasLabel = new CanvasLabel(detection, RenderSize, videoSize, detection.Confidence);
CreateDetectionControl(annClass, time, canvasLabel); CreateDetectionControl(detectionClass, time, canvasLabel);
} }
} }
private void CreateDetectionControl(DetectionClass annClass, TimeSpan time, CanvasLabel canvasLabel) private void CreateDetectionControl(DetectionClass detectionClass, TimeSpan time, CanvasLabel canvasLabel)
{ {
var detectionControl = new DetectionControl(annClass, time, AnnotationResizeStart, canvasLabel.Confidence) var detectionControl = new DetectionControl(detectionClass, time, AnnotationResizeStart, canvasLabel);
{
Width = canvasLabel.Width,
Height = canvasLabel.Height
};
detectionControl.MouseDown += AnnotationPositionStart; detectionControl.MouseDown += AnnotationPositionStart;
SetLeft(detectionControl, canvasLabel.X ); SetLeft(detectionControl, canvasLabel.X );
SetTop(detectionControl, canvasLabel.Y); SetTop(detectionControl, canvasLabel.Y);
Children.Add(detectionControl); Children.Add(detectionControl);
CurrentDetections.Add(detectionControl); CurrentDetections.Add(detectionControl);
_newAnnotationRect.Fill = new SolidColorBrush(annClass.Color); _newAnnotationRect.Fill = new SolidColorBrush(detectionClass.Color);
} }
#endregion #endregion
@@ -1,6 +1,8 @@
using System.Windows; using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using Azaion.Common.DTO; using Azaion.Common.DTO;
using Azaion.Common.Extensions;
namespace Azaion.Common.Controls; namespace Azaion.Common.Controls;
@@ -14,6 +16,7 @@ public partial class DetectionClasses
{ {
public event EventHandler<DetectionClassChangedEventArgs>? DetectionClassChanged; public event EventHandler<DetectionClassChangedEventArgs>? DetectionClassChanged;
private const int CaptionedMinWidth = 230; private const int CaptionedMinWidth = 230;
ObservableCollection<DetectionClass> _detectionClasses = new();
public DetectionClasses() public DetectionClasses()
{ {
@@ -37,7 +40,14 @@ public partial class DetectionClasses
public void Init(List<DetectionClass> detectionClasses) public void Init(List<DetectionClass> detectionClasses)
{ {
DetectionDataGrid.ItemsSource = detectionClasses; foreach (var dClass in detectionClasses)
{
var cl = (DetectionClass)dClass.Clone();
cl.Color = cl.Color.ToConfidenceColor();
_detectionClasses.Add(cl);
}
DetectionDataGrid.ItemsSource = _detectionClasses;
DetectionDataGrid.SelectedIndex = 0; DetectionDataGrid.SelectedIndex = 0;
} }
+53 -36
View File
@@ -4,6 +4,7 @@ using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Shapes; using System.Windows.Shapes;
using Azaion.Common.DTO; using Azaion.Common.DTO;
using Azaion.Common.Extensions;
using Label = System.Windows.Controls.Label; using Label = System.Windows.Controls.Label;
namespace Azaion.Common.Controls; namespace Azaion.Common.Controls;
@@ -11,22 +12,28 @@ namespace Azaion.Common.Controls;
public class DetectionControl : Border public class DetectionControl : Border
{ {
private readonly Action<object, MouseButtonEventArgs> _resizeStart; private readonly Action<object, MouseButtonEventArgs> _resizeStart;
private const double RESIZE_RECT_SIZE = 9; private const double RESIZE_RECT_SIZE = 12;
private readonly Grid _grid; private readonly Grid _grid;
private readonly TextBlock _classNameLabel; private readonly Label _detectionLabel;
private readonly Label _probabilityLabel;
public TimeSpan Time { get; set; } public TimeSpan Time { get; set; }
private readonly double _confidence;
private List<Rectangle> _resizedRectangles = new();
private DetectionClass _detectionClass = null!; private DetectionClass _detectionClass = null!;
public DetectionClass DetectionClass public DetectionClass DetectionClass
{ {
get => _detectionClass; get => _detectionClass;
set set
{ {
_grid.Background = value.ColorBrush; var brush = new SolidColorBrush(value.Color.ToConfidenceColor());
_probabilityLabel.Background = value.ColorBrush; BorderBrush = brush;
_classNameLabel.Text = value.UIName; BorderThickness = new Thickness(3);
foreach (var rect in _resizedRectangles)
rect.Stroke = brush;
_detectionLabel.Background = new SolidColorBrush(value.Color.ToConfidenceColor(_confidence));
_detectionLabel.Content = _detectionLabelText(value.UIName);
_detectionClass = value; _detectionClass = value;
} }
} }
@@ -43,29 +50,36 @@ public class DetectionControl : Border
_isSelected = value; _isSelected = value;
} }
} }
public DetectionControl(DetectionClass detectionClass, TimeSpan time, Action<object, MouseButtonEventArgs> resizeStart, double? probability = null) private string _detectionLabelText(string detectionClassName) =>
_confidence >= 0.995 ? detectionClassName : $"{detectionClassName}: {_confidence * 100:F0}%"; //double
public DetectionControl(DetectionClass detectionClass, TimeSpan time, Action<object, MouseButtonEventArgs> resizeStart, CanvasLabel canvasLabel)
{ {
Width = canvasLabel.Width;
Height = canvasLabel.Height;
Time = time; Time = time;
_resizeStart = resizeStart; _resizeStart = resizeStart;
_classNameLabel = new TextBlock _confidence = canvasLabel.Confidence;
var labelContainer = new Canvas
{ {
Text = detectionClass.UIName, HorizontalAlignment = HorizontalAlignment.Right,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Top, VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(0, 15, 0, 0), ClipToBounds = false,
FontSize = 14, Margin = new Thickness(0, 0, 32, 0)
Cursor = Cursors.SizeAll
}; };
_probabilityLabel = new Label _detectionLabel = new Label
{ {
Content = probability.HasValue ? $"{probability.Value*100:F0}%" : string.Empty, Content = _detectionLabelText(detectionClass.Name),
HorizontalAlignment = HorizontalAlignment.Right, HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Top, VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(0, -32, 0, 0), Margin = new Thickness(0, -32, 0, 0),
FontSize = 16, FontSize = 16,
Visibility = Visibility.Visible Visibility = Visibility.Visible
}; };
labelContainer.Children.Add(_detectionLabel);
_selectionFrame = new Rectangle _selectionFrame = new Rectangle
{ {
HorizontalAlignment = HorizontalAlignment.Stretch, HorizontalAlignment = HorizontalAlignment.Stretch,
@@ -74,43 +88,46 @@ public class DetectionControl : Border
StrokeThickness = 2, StrokeThickness = 2,
Visibility = Visibility.Collapsed Visibility = Visibility.Collapsed
}; };
_resizedRectangles =
[
CreateResizeRect("rLT", HorizontalAlignment.Left, VerticalAlignment.Top, Cursors.SizeNWSE),
CreateResizeRect("rCT", HorizontalAlignment.Center, VerticalAlignment.Top, Cursors.SizeNS),
CreateResizeRect("rRT", HorizontalAlignment.Right, VerticalAlignment.Top, Cursors.SizeNESW),
CreateResizeRect("rLC", HorizontalAlignment.Left, VerticalAlignment.Center, Cursors.SizeWE),
CreateResizeRect("rRC", HorizontalAlignment.Right, VerticalAlignment.Center, Cursors.SizeWE),
CreateResizeRect("rLB", HorizontalAlignment.Left, VerticalAlignment.Bottom, Cursors.SizeNESW),
CreateResizeRect("rCB", HorizontalAlignment.Center, VerticalAlignment.Bottom, Cursors.SizeNS),
CreateResizeRect("rRB", HorizontalAlignment.Right, VerticalAlignment.Bottom, Cursors.SizeNWSE)
];
_grid = new Grid _grid = new Grid
{ {
Background = Brushes.Transparent,
HorizontalAlignment = HorizontalAlignment.Stretch, HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch,
Children = Children = { _selectionFrame }
{
_selectionFrame,
_classNameLabel,
AddRect("rLT", HorizontalAlignment.Left, VerticalAlignment.Top, Cursors.SizeNWSE),
AddRect("rCT", HorizontalAlignment.Center, VerticalAlignment.Top, Cursors.SizeNS),
AddRect("rRT", HorizontalAlignment.Right, VerticalAlignment.Top, Cursors.SizeNESW),
AddRect("rLC", HorizontalAlignment.Left, VerticalAlignment.Center, Cursors.SizeWE),
AddRect("rRC", HorizontalAlignment.Right, VerticalAlignment.Center, Cursors.SizeWE),
AddRect("rLB", HorizontalAlignment.Left, VerticalAlignment.Bottom, Cursors.SizeNESW),
AddRect("rCB", HorizontalAlignment.Center, VerticalAlignment.Bottom, Cursors.SizeNS),
AddRect("rRB", HorizontalAlignment.Right, VerticalAlignment.Bottom, Cursors.SizeNWSE)
}
}; };
if (probability.HasValue) foreach (var rect in _resizedRectangles)
_grid.Children.Add(_probabilityLabel); _grid.Children.Add(rect);
_grid.Children.Add(labelContainer);
Child = _grid; Child = _grid;
Cursor = Cursors.SizeAll; Cursor = Cursors.SizeAll;
DetectionClass = detectionClass; DetectionClass = detectionClass;
} }
//small corners //small corners
private Rectangle AddRect(string name, HorizontalAlignment ha, VerticalAlignment va, Cursor crs) private Rectangle CreateResizeRect(string name, HorizontalAlignment ha, VerticalAlignment va, Cursor crs)
{ {
var rect = new Rectangle() // small rectangles at the corners and sides var rect = new Rectangle() // small rectangles at the corners and sides
{ {
ClipToBounds = false,
Margin = new Thickness(-RESIZE_RECT_SIZE),
HorizontalAlignment = ha, HorizontalAlignment = ha,
VerticalAlignment = va, VerticalAlignment = va,
Width = RESIZE_RECT_SIZE, Width = RESIZE_RECT_SIZE,
Height = RESIZE_RECT_SIZE, Height = RESIZE_RECT_SIZE,
Stroke = new SolidColorBrush(Color.FromArgb(230, 40, 40, 40)), // small rectangles color Stroke = new SolidColorBrush(Color.FromArgb(230, 20, 20, 20)), // small rectangles color
Fill = new SolidColorBrush(Color.FromArgb(1, 255, 255, 255)), Fill = new SolidColorBrush(Color.FromArgb(150, 80, 80, 80)),
Cursor = crs, Cursor = crs,
Name = name, Name = name,
}; };
@@ -36,7 +36,4 @@ public class AnnotationConfig
public List<string> ImageFormats { get; set; } = null!; public List<string> ImageFormats { get; set; } = null!;
public string AnnotationsDbFile { get; set; } = null!; public string AnnotationsDbFile { get; set; } = null!;
public double LeftPanelWidth { get; set; }
public double RightPanelWidth { get; set; }
} }
+12 -9
View File
@@ -18,6 +18,8 @@ public class AppConfig
public AnnotationConfig AnnotationConfig { get; set; } = null!; public AnnotationConfig AnnotationConfig { get; set; } = null!;
public UIConfig UIConfig { get; set; } = null!;
public AIRecognitionConfig AIRecognitionConfig { get; set; } = null!; public AIRecognitionConfig AIRecognitionConfig { get; set; } = null!;
public ThumbnailConfig ThumbnailConfig { get; set; } = null!; public ThumbnailConfig ThumbnailConfig { get; set; } = null!;
@@ -49,10 +51,13 @@ public class ConfigUpdater : IConfigUpdater
VideoFormats = Constants.DefaultVideoFormats, VideoFormats = Constants.DefaultVideoFormats,
ImageFormats = Constants.DefaultImageFormats, ImageFormats = Constants.DefaultImageFormats,
AnnotationsDbFile = Constants.DEFAULT_ANNOTATIONS_DB_FILE
},
UIConfig = new UIConfig
{
LeftPanelWidth = Constants.DEFAULT_LEFT_PANEL_WIDTH, LeftPanelWidth = Constants.DEFAULT_LEFT_PANEL_WIDTH,
RightPanelWidth = Constants.DEFAULT_RIGHT_PANEL_WIDTH, RightPanelWidth = Constants.DEFAULT_RIGHT_PANEL_WIDTH,
AnnotationsDbFile = Constants.DEFAULT_ANNOTATIONS_DB_FILE
}, },
DirectoriesConfig = new DirectoriesConfig DirectoriesConfig = new DirectoriesConfig
@@ -84,15 +89,13 @@ public class ConfigUpdater : IConfigUpdater
public void Save(AppConfig config) public void Save(AppConfig config)
{ {
//Save without sensitive info //Save only user's config
var publicConfig = new var publicConfig = new
{ {
InferenceClientConfig = config.InferenceClientConfig, config.InferenceClientConfig,
GpsDeniedClientConfig = config.GpsDeniedClientConfig, config.GpsDeniedClientConfig,
DirectoriesConfig = config.DirectoriesConfig, config.DirectoriesConfig,
AnnotationConfig = config.AnnotationConfig, config.UIConfig
AIRecognitionConfig = config.AIRecognitionConfig,
ThumbnailConfig = config.ThumbnailConfig
}; };
File.WriteAllText(SecurityConstants.CONFIG_PATH, JsonConvert.SerializeObject(publicConfig, Formatting.Indented), Encoding.UTF8); File.WriteAllText(SecurityConstants.CONFIG_PATH, JsonConvert.SerializeObject(publicConfig, Formatting.Indented), Encoding.UTF8);
+7
View File
@@ -0,0 +1,7 @@
namespace Azaion.Common.DTO.Config;
public class UIConfig
{
public double LeftPanelWidth { get; set; }
public double RightPanelWidth { get; set; }
}
+3 -1
View File
@@ -3,7 +3,7 @@ using Newtonsoft.Json;
namespace Azaion.Common.DTO; namespace Azaion.Common.DTO;
public class DetectionClass public class DetectionClass : ICloneable
{ {
public int Id { get; set; } public int Id { get; set; }
@@ -49,6 +49,8 @@ public class DetectionClass
detClass.PhotoMode = photoMode; detClass.PhotoMode = photoMode;
return detClass; return detClass;
} }
public object Clone() => MemberwiseClone();
} }
public enum PhotoMode public enum PhotoMode
+2 -2
View File
@@ -26,13 +26,13 @@ public class CanvasLabel : Label
public double Y { get; set; } public double Y { get; set; }
public double Width { get; set; } public double Width { get; set; }
public double Height { get; set; } public double Height { get; set; }
public double? Confidence { get; } public double Confidence { get; set; }
public CanvasLabel() public CanvasLabel()
{ {
} }
public CanvasLabel(int classNumber, double x, double y, double width, double height, double? confidence = null) : base(classNumber) public CanvasLabel(int classNumber, double x, double y, double width, double height, double confidence = 1) : base(classNumber)
{ {
X = x; X = x;
Y = y; Y = y;
+3 -3
View File
@@ -4,10 +4,10 @@ namespace Azaion.Common.Extensions;
public static class ColorExtensions public static class ColorExtensions
{ {
private const int MIN_ALPHA = 20; private const int MIN_ALPHA = 15;
private const int MAX_ALPHA = 100; private const int MAX_ALPHA = 150;
public static Color ToConfidenceColor(this Color color, double confidence ) public static Color ToConfidenceColor(this Color color, double confidence = 1)
{ {
color.A = (byte)(MIN_ALPHA + (int)Math.Round(confidence * (MAX_ALPHA - MIN_ALPHA))); color.A = (byte)(MIN_ALPHA + (int)Math.Round(confidence * (MAX_ALPHA - MIN_ALPHA)));
return color; return color;
+18 -10
View File
@@ -55,15 +55,20 @@ public class AnnotationService : INotificationHandler<AnnotationsDeletedEvent>
private async Task Init(CancellationToken cancellationToken = default) private async Task Init(CancellationToken cancellationToken = default)
{ {
if (!_authProvider.CurrentUser.Role.IsValidator())
return;
var consumerSystem = await StreamSystem.Create(new StreamSystemConfig var consumerSystem = await StreamSystem.Create(new StreamSystemConfig
{ {
Endpoints = new List<EndPoint>{new DnsEndPoint(_queueConfig.Host, _queueConfig.Port)}, Endpoints = new List<EndPoint>{new DnsEndPoint(_queueConfig.Host, _queueConfig.Port)},
UserName = _queueConfig.ConsumerUsername, UserName = _queueConfig.ConsumerUsername,
Password = _queueConfig.ConsumerPassword Password = _queueConfig.ConsumerPassword
}); });
var offset = (await _dbFactory.Run(db => db.QueueOffsets.FirstOrDefaultAsync( var offset = (await _dbFactory.Run(db => db.QueueOffsets.FirstOrDefaultAsync(
x => x.QueueName == Constants.MQ_ANNOTATIONS_QUEUE, token: cancellationToken)) x => x.QueueName == Constants.MQ_ANNOTATIONS_QUEUE, token: cancellationToken))
)?.Offset ?? 0; )?.Offset ?? 0;
_consumer = await Consumer.Create(new ConsumerConfig(consumerSystem, Constants.MQ_ANNOTATIONS_QUEUE) _consumer = await Consumer.Create(new ConsumerConfig(consumerSystem, Constants.MQ_ANNOTATIONS_QUEUE)
{ {
Reference = _hardwareService.GetHardware().Hash, Reference = _hardwareService.GetHardware().Hash,
@@ -71,7 +76,7 @@ public class AnnotationService : INotificationHandler<AnnotationsDeletedEvent>
MessageHandler = async (_, _, context, message) => MessageHandler = async (_, _, context, message) =>
{ {
var msg = MessagePackSerializer.Deserialize<AnnotationCreatedMessage>(message.Data.Contents); var msg = MessagePackSerializer.Deserialize<AnnotationCreatedMessage>(message.Data.Contents);
await _dbFactory.Run(async db => await db.QueueOffsets await _dbFactory.Run(async db => await db.QueueOffsets
.Where(x => x.QueueName == Constants.MQ_ANNOTATIONS_QUEUE) .Where(x => x.QueueName == Constants.MQ_ANNOTATIONS_QUEUE)
.Set(x => x.Offset, context.Offset) .Set(x => x.Offset, context.Offset)
.UpdateAsync(token: cancellationToken)); .UpdateAsync(token: cancellationToken));
@@ -95,28 +100,29 @@ public class AnnotationService : INotificationHandler<AnnotationsDeletedEvent>
msg.CreatedRole, msg.CreatedRole,
msg.CreatedEmail, msg.CreatedEmail,
generateThumbnail: true, generateThumbnail: true,
cancellationToken); fromQueue: true,
token: cancellationToken);
} }
}); });
} }
//AI //AI
public async Task<Annotation> SaveAnnotation(AnnotationImage a, CancellationToken cancellationToken = default) public async Task<Annotation> SaveAnnotation(AnnotationImage a, CancellationToken ct = default)
{ {
a.Time = TimeSpan.FromMilliseconds(a.Milliseconds); a.Time = TimeSpan.FromMilliseconds(a.Milliseconds);
return await SaveAnnotationInner(DateTime.Now, a.OriginalMediaName, a.Time, a.Detections.ToList(), return await SaveAnnotationInner(DateTime.Now, a.OriginalMediaName, a.Time, a.Detections.ToList(),
a.Source, new MemoryStream(a.Image), _authProvider.CurrentUser.Role, _authProvider.CurrentUser.Email, generateThumbnail: true, cancellationToken); SourceEnum.AI, new MemoryStream(a.Image), _authProvider.CurrentUser.Role, _authProvider.CurrentUser.Email, generateThumbnail: true, token: ct);
} }
//Manual //Manual
public async Task<Annotation> SaveAnnotation(string originalMediaName, TimeSpan time, List<Detection> detections, SourceEnum source, Stream? stream = null, CancellationToken token = default) => public async Task<Annotation> SaveAnnotation(string originalMediaName, TimeSpan time, List<Detection> detections, Stream? stream = null, CancellationToken token = default) =>
await SaveAnnotationInner(DateTime.UtcNow, originalMediaName, time, detections, source, stream, await SaveAnnotationInner(DateTime.UtcNow, originalMediaName, time, detections, SourceEnum.Manual, stream,
_authProvider.CurrentUser.Role, _authProvider.CurrentUser.Email, generateThumbnail: true, token); _authProvider.CurrentUser.Role, _authProvider.CurrentUser.Email, generateThumbnail: true, token: token);
//Manual Validate existing //Manual Validate existing
public async Task ValidateAnnotation(Annotation annotation, CancellationToken token = default) => public async Task ValidateAnnotation(Annotation annotation, CancellationToken token = default) =>
await SaveAnnotationInner(DateTime.UtcNow, annotation.OriginalMediaName, annotation.Time, annotation.Detections.ToList(), SourceEnum.Manual, null, await SaveAnnotationInner(DateTime.UtcNow, annotation.OriginalMediaName, annotation.Time, annotation.Detections.ToList(), SourceEnum.Manual, null,
_authProvider.CurrentUser.Role, _authProvider.CurrentUser.Email, generateThumbnail: false, token); _authProvider.CurrentUser.Role, _authProvider.CurrentUser.Email, token: token);
// Manual save from Validators -> Validated -> stream: azaion-annotations-confirm // Manual save from Validators -> Validated -> stream: azaion-annotations-confirm
// AI, Manual save from Operators -> Created -> stream: azaion-annotations // AI, Manual save from Operators -> Created -> stream: azaion-annotations
@@ -124,6 +130,7 @@ public class AnnotationService : INotificationHandler<AnnotationsDeletedEvent>
RoleEnum userRole, RoleEnum userRole,
string createdEmail, string createdEmail,
bool generateThumbnail = false, bool generateThumbnail = false,
bool fromQueue = false,
CancellationToken token = default) CancellationToken token = default)
{ {
@@ -179,7 +186,8 @@ public class AnnotationService : INotificationHandler<AnnotationsDeletedEvent>
if (generateThumbnail) if (generateThumbnail)
await _galleryService.CreateThumbnail(annotation, token); await _galleryService.CreateThumbnail(annotation, token);
await _producer.SendToInnerQueue(annotation, token); if (!fromQueue) //Send to queue only if we're not getting from queue already
await _producer.SendToInnerQueue(annotation, token);
await _mediator.Publish(new AnnotationCreatedEvent(annotation), token); await _mediator.Publish(new AnnotationCreatedEvent(annotation), token);
await ThrottleExt.ThrottleRunAfter(() => await ThrottleExt.ThrottleRunAfter(() =>
+1 -2
View File
@@ -255,8 +255,7 @@ public class GalleryService(
var color = _annotationConfig.DetectionClassesDict[label.ClassNumber].Color; var color = _annotationConfig.DetectionClassesDict[label.ClassNumber].Color;
var brush = new SolidBrush(Color.FromArgb(color.A, color.R, color.G, color.B)); 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.DrawRectangle(new Pen(brush, width: 3), (float)((label.X - frameX) / scale), (float)((label.Y - frameY) / scale), (float)(label.Width / scale), (float)(label.Height / scale));
g.FillRectangle(brush, rectangle);
} }
bitmap.Save(annotation.ThumbPath, ImageFormat.Jpeg); bitmap.Save(annotation.ThumbPath, ImageFormat.Jpeg);
@@ -67,7 +67,7 @@ public class DatasetExplorerEventHandler(
.Select(x => new Detection(a.Name, x.GetLabel(datasetExplorer.ExplorerEditor.RenderSize))) .Select(x => new Detection(a.Name, x.GetLabel(datasetExplorer.ExplorerEditor.RenderSize)))
.ToList(); .ToList();
var index = datasetExplorer.ThumbnailsView.SelectedIndex; var index = datasetExplorer.ThumbnailsView.SelectedIndex;
await annotationService.SaveAnnotation(a.OriginalMediaName, a.Time, detections, SourceEnum.Manual, token: cancellationToken); await annotationService.SaveAnnotation(a.OriginalMediaName, a.Time, detections, token: cancellationToken);
await datasetExplorer.EditAnnotation(index + 1); await datasetExplorer.EditAnnotation(index + 1);
break; break;
case PlaybackControlEnum.RemoveSelectedAnns: case PlaybackControlEnum.RemoveSelectedAnns:
+6 -2
View File
@@ -38,6 +38,7 @@ public partial class App
private IAuthProvider _authProvider = null!; private IAuthProvider _authProvider = null!;
private Stream _securedConfig = null!; private Stream _securedConfig = null!;
private Stream _systemConfig = null!;
private static readonly Guid KeyPressTaskId = Guid.NewGuid(); private static readonly Guid KeyPressTaskId = Guid.NewGuid();
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
@@ -95,7 +96,8 @@ public partial class App
{ {
credentials.Folder = secureAppConfig.InferenceClientConfig.ResourcesFolder; credentials.Folder = secureAppConfig.InferenceClientConfig.ResourcesFolder;
_authProvider.Login(credentials); _authProvider.Login(credentials);
_securedConfig = _resourceLoader.LoadFile("secured-config.json"); _securedConfig = _resourceLoader.LoadFile("config.secured.json");
_systemConfig = _resourceLoader.LoadFile("config.system.json");
AppDomain.CurrentDomain.AssemblyResolve += (_, a) => AppDomain.CurrentDomain.AssemblyResolve += (_, a) =>
{ {
@@ -146,7 +148,8 @@ public partial class App
.ConfigureAppConfiguration((context, config) => config .ConfigureAppConfiguration((context, config) => config
.AddCommandLine(Environment.GetCommandLineArgs()) .AddCommandLine(Environment.GetCommandLineArgs())
.AddJsonFile(SecurityConstants.CONFIG_PATH, optional: true, reloadOnChange: true) .AddJsonFile(SecurityConstants.CONFIG_PATH, optional: true, reloadOnChange: true)
.AddJsonStream(_securedConfig)) .AddJsonStream(_securedConfig)
.AddJsonStream(_systemConfig))
.UseSerilog() .UseSerilog()
.ConfigureServices((context, services) => .ConfigureServices((context, services) =>
{ {
@@ -159,6 +162,7 @@ public partial class App
services.ConfigureSection<AnnotationConfig>(context.Configuration); services.ConfigureSection<AnnotationConfig>(context.Configuration);
services.ConfigureSection<AIRecognitionConfig>(context.Configuration); services.ConfigureSection<AIRecognitionConfig>(context.Configuration);
services.ConfigureSection<ThumbnailConfig>(context.Configuration); services.ConfigureSection<ThumbnailConfig>(context.Configuration);
services.ConfigureSection<UIConfig>(context.Configuration);
#region External Services #region External Services
+1 -40
View File
@@ -20,47 +20,8 @@
"ResultsDirectory": "E:\\results", "ResultsDirectory": "E:\\results",
"ThumbnailsDirectory": "E:\\thumbnails" "ThumbnailsDirectory": "E:\\thumbnails"
}, },
"AnnotationConfig": { "UIConfig": {
"DetectionClasses": [
{ "Id": 0, "Name": "ArmorVehicle", "ShortName": "Броня", "Color": "#FF0000" },
{ "Id": 1, "Name": "Truck", "ShortName": "Вантаж.", "Color": "#00FF00" },
{ "Id": 2, "Name": "Vehicle", "ShortName": "Машина", "Color": "#0000FF" },
{ "Id": 3, "Name": "Atillery", "ShortName": "Арта", "Color": "#FFFF00" },
{ "Id": 4, "Name": "Shadow", "ShortName": "Тінь", "Color": "#FF00FF" },
{ "Id": 5, "Name": "Trenches", "ShortName": "Окопи", "Color": "#00FFFF" },
{ "Id": 6, "Name": "MilitaryMan", "ShortName": "Військов", "Color": "#188021" },
{ "Id": 7, "Name": "TyreTracks", "ShortName": "Накати", "Color": "#800000" },
{ "Id": 8, "Name": "AdditArmoredTank", "ShortName": "Танк.захист", "Color": "#008000" },
{ "Id": 9, "Name": "Smoke", "ShortName": "Дим", "Color": "#000080" },
{ "Id": 10, "Name": "Plane", "ShortName": "Літак", "Color": "#000080" },
{ "Id": 11, "Name": "Moto", "ShortName": "Мото", "Color": "#808000" },
{ "Id": 12, "Name": "CamouflageNnet", "ShortName": "Сітка", "Color": "#800080" },
{ "Id": 13, "Name": "CamouflageBranches", "ShortName": "Гілки", "Color": "#008080" },
{ "Id": 14, "Name": "Roof", "ShortName": "Дах", "Color": "#0050A0" },
{ "Id": 15, "Name": "Building", "ShortName": "Будівля", "Color": "#008080" }
],
"LastSelectedExplorerClass": null,
"VideoFormats": [ ".mp4", ".mov", ".avi" ],
"ImageFormats": [ ".jpg", ".jpeg", ".png", ".bmp" ],
"AnnotationsDbFile": "annotations.db",
"LeftPanelWidth": 220.0, "LeftPanelWidth": 220.0,
"RightPanelWidth": 230.0 "RightPanelWidth": 230.0
},
"AIRecognitionConfig": {
"FramePeriodRecognition": 4,
"FrameRecognitionSeconds": 2.0,
"ProbabilityThreshold": 0.25,
"TrackingDistanceConfidence": 0.15,
"TrackingProbabilityIncrease": 15.0,
"TrackingIntersectionThreshold": 0.8,
"ModelBatchSize": 4
},
"ThumbnailConfig": { "Size": "240,135", "Border": 10 },
"MapConfig":
{
"Service": "",
"ApiKey": ""
} }
} }
+8 -40
View File
@@ -3,11 +3,13 @@
"ZeroMqHost": "127.0.0.1", "ZeroMqHost": "127.0.0.1",
"ZeroMqPort": 5131, "ZeroMqPort": 5131,
"RetryCount": 25, "RetryCount": 25,
"TimeoutSeconds": 5 "TimeoutSeconds": 5,
"ResourcesFolder": ""
}, },
"GpsDeniedClientConfig": { "GpsDeniedClientConfig": {
"ZeroMqHost": "127.0.0.1", "ZeroMqHost": "127.0.0.1",
"ZeroMqPort": 5231, "ZeroMqPort": 5555,
"ZeroMqReceiverPort": 5556,
"RetryCount": 25, "RetryCount": 25,
"TimeoutSeconds": 5 "TimeoutSeconds": 5
}, },
@@ -18,42 +20,8 @@
"ResultsDirectory": "results", "ResultsDirectory": "results",
"ThumbnailsDirectory": "thumbnails" "ThumbnailsDirectory": "thumbnails"
}, },
"AnnotationConfig": { "UIConfig": {
"DetectionClasses": [ "LeftPanelWidth": 170.0,
{ "Id": 0, "Name": "ArmorVehicle", "ShortName": "Броня", "Color": "#80FF0000" }, "RightPanelWidth": 120.0
{ "Id": 1, "Name": "Truck", "ShortName": "Вантаж.", "Color": "#8000FF00" }, }
{ "Id": 2, "Name": "Vehicle", "ShortName": "Машина", "Color": "#800000FF" },
{ "Id": 3, "Name": "Atillery", "ShortName": "Арта", "Color": "#80FFFF00" },
{ "Id": 4, "Name": "Shadow", "ShortName": "Тінь", "Color": "#80FF00FF" },
{ "Id": 5, "Name": "Trenches", "ShortName": "Окопи", "Color": "#8000FFFF" },
{ "Id": 6, "Name": "MilitaryMan", "ShortName": "Військов", "Color": "#80188021" },
{ "Id": 7, "Name": "TyreTracks", "ShortName": "Накати", "Color": "#80800000" },
{ "Id": 8, "Name": "AdditArmoredTank", "ShortName": "Танк.захист", "Color": "#80008000" },
{ "Id": 9, "Name": "Smoke", "ShortName": "Дим", "Color": "#8080000080" },
{ "Id": 10, "Name": "Plane", "ShortName": "Літак", "Color": "#80000080" },
{ "Id": 11, "Name": "Moto", "ShortName": "Мото", "Color": "#80808000" },
{ "Id": 12, "Name": "CamouflageNnet", "ShortName": "Сітка", "Color": "#80800080" },
{ "Id": 13, "Name": "CamouflageBranches", "ShortName": "Гілки", "Color": "#80008080" },
{ "Id": 14, "Name": "Roof", "ShortName": "Дах", "Color": "#800050A0" },
{ "Id": 15, "Name": "Building", "ShortName": "Будівля", "Color": "#80008080" }
],
"LastSelectedExplorerClass": null,
"VideoFormats": [ ".mp4", ".mov", ".avi" ],
"ImageFormats": [ ".jpg", ".jpeg", ".png", ".bmp" ],
"AnnotationsDbFile": "annotations.db",
"LeftPanelWidth": 240.0,
"RightPanelWidth": 240.0
},
"AIRecognitionConfig": {
"FramePeriodRecognition": 6,
"FrameRecognitionSeconds": 2.0,
"ProbabilityThreshold": 0.25,
"TrackingDistanceConfidence": 0.15,
"TrackingProbabilityIncrease": 15.0,
"TrackingIntersectionThreshold": 0.8,
"ModelBatchSize": 2
},
"ThumbnailConfig": { "Size": "240,135", "Border": 10 }
} }
+19
View File
@@ -0,0 +1,19 @@
{
"QueueConfig":
{
"Host": "188.245.120.247",
"Port": 5552,
"CommandsPort": 5672,
"ProducerUsername" : "azaion_producer",
"ProducerPassword" : "Az12onPr00duccewr",
"ConsumerUsername" : "azaion_receiver",
"ConsumerPassword" : "Az1onRecce777ve2r"
},
"MapConfig":
{
"Service": "GoogleMaps",
"ApiKey": "AIzaSyAXRBDBOskC5QOHG6VJWzmVJwYKcu6WH8k"
}
}
+37
View File
@@ -0,0 +1,37 @@
{
"AnnotationConfig": {
"DetectionClasses": [
{ "Id": 0, "Name": "ArmorVehicle", "ShortName": "Броня", "Color": "#FF0000" },
{ "Id": 1, "Name": "Truck", "ShortName": "Вантаж.", "Color": "#00FF00" },
{ "Id": 2, "Name": "Vehicle", "ShortName": "Машина", "Color": "#0000FF" },
{ "Id": 3, "Name": "Atillery", "ShortName": "Арта", "Color": "#FFFF00" },
{ "Id": 4, "Name": "Shadow", "ShortName": "Тінь", "Color": "#FF00FF" },
{ "Id": 5, "Name": "Trenches", "ShortName": "Окопи", "Color": "#00FFFF" },
{ "Id": 6, "Name": "MilitaryMan", "ShortName": "Військов", "Color": "#188021" },
{ "Id": 7, "Name": "TyreTracks", "ShortName": "Накати", "Color": "#800000" },
{ "Id": 8, "Name": "AdditArmoredTank", "ShortName": "Танк.захист", "Color": "#008000" },
{ "Id": 9, "Name": "Smoke", "ShortName": "Дим", "Color": "#000080" },
{ "Id": 10, "Name": "Plane", "ShortName": "Літак", "Color": "#000080" },
{ "Id": 11, "Name": "Moto", "ShortName": "Мото", "Color": "#808000" },
{ "Id": 12, "Name": "CamouflageNnet", "ShortName": "Сітка", "Color": "#800080" },
{ "Id": 13, "Name": "CamouflageBranches", "ShortName": "Гілки", "Color": "#2f4f4f" },
{ "Id": 14, "Name": "Roof", "ShortName": "Дах", "Color": "#1e90ff" },
{ "Id": 15, "Name": "Building", "ShortName": "Будівля", "Color": "#ffb6c1" }
],
"VideoFormats": [ ".mp4", ".mov", ".avi" ],
"ImageFormats": [ ".jpg", ".jpeg", ".png", ".bmp" ],
"AnnotationsDbFile": "annotations.db"
},
"AIRecognitionConfig": {
"FramePeriodRecognition": 4,
"FrameRecognitionSeconds": 2.0,
"ProbabilityThreshold": 0.25,
"TrackingDistanceConfidence": 0.15,
"TrackingProbabilityIncrease": 15.0,
"TrackingIntersectionThreshold": 0.8,
"ModelBatchSize": 4
},
"ThumbnailConfig": { "Size": "240,135", "Border": 10 }
}