diff --git a/Azaion.Annotator/Annotator.xaml.cs b/Azaion.Annotator/Annotator.xaml.cs
index d952501..e570b93 100644
--- a/Azaion.Annotator/Annotator.xaml.cs
+++ b/Azaion.Annotator/Annotator.xaml.cs
@@ -102,9 +102,6 @@ public partial class Annotator
if (LvFiles.Items.IsEmpty)
BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.Initial]);
-
- if (_appConfig.WindowConfig.ShowHelpOnStart)
- _helpWindow.Show();
}
public void BlinkHelp(string helpText, int times = 2)
@@ -541,7 +538,6 @@ public partial class Annotator
_ = Task.Run(async () =>
{
- using var detector = new YOLODetector(_appConfig.AIRecognitionConfig);
Dispatcher.Invoke(() => _autoDetectDialog.Log("Ініціалізація AI..."));
var prevSeekTime = 0.0;
@@ -549,7 +545,7 @@ public partial class Annotator
{
try
{
- var detections = _aiDetector.Detect(timeframe.Stream);
+ var detections = await _aiDetector.Detect(timeframe.Stream, token);
if (timeframe.Time.TotalSeconds > prevSeekTime + 1)
{
Dispatcher.Invoke(() => SeekTo(timeframe.Time));
diff --git a/Azaion.Annotator/AnnotatorEventHandler.cs b/Azaion.Annotator/AnnotatorEventHandler.cs
index 6a7e0c2..b5e2412 100644
--- a/Azaion.Annotator/AnnotatorEventHandler.cs
+++ b/Azaion.Annotator/AnnotatorEventHandler.cs
@@ -5,6 +5,7 @@ using System.Windows.Input;
using System.Windows.Media;
using Azaion.Annotator.DTO;
using Azaion.Common.DTO;
+using Azaion.Common.DTO.Config;
using LibVLCSharp.Shared;
using MediatR;
using Microsoft.Extensions.Logging;
diff --git a/Azaion.Annotator/Azaion.Annotator.csproj b/Azaion.Annotator/Azaion.Annotator.csproj
index 28262f2..58e9046 100644
--- a/Azaion.Annotator/Azaion.Annotator.csproj
+++ b/Azaion.Annotator/Azaion.Annotator.csproj
@@ -10,8 +10,8 @@
-
-
+
+
@@ -26,16 +26,13 @@
-
+
-
- Always
-
diff --git a/Azaion.Annotator/DTO/AnnotationResult.cs b/Azaion.Annotator/DTO/AnnotationResult.cs
index 0d808a3..dff2929 100644
--- a/Azaion.Annotator/DTO/AnnotationResult.cs
+++ b/Azaion.Annotator/DTO/AnnotationResult.cs
@@ -1,8 +1,5 @@
using System.Windows.Media;
-using Azaion.Annotator.Extensions;
using Azaion.Common.DTO;
-using Azaion.Common.DTO.Config;
-using Azaion.Common.Extensions;
using Newtonsoft.Json;
namespace Azaion.Annotator.DTO;
diff --git a/Azaion.Annotator/HelpWindow.xaml.cs b/Azaion.Annotator/HelpWindow.xaml.cs
index b41b764..b9e3518 100644
--- a/Azaion.Annotator/HelpWindow.xaml.cs
+++ b/Azaion.Annotator/HelpWindow.xaml.cs
@@ -1,5 +1,6 @@
using System.Windows;
using Azaion.Common.DTO.Config;
+using Microsoft.Extensions.Options;
namespace Azaion.Annotator;
@@ -7,14 +8,19 @@ public partial class HelpWindow : Window
{
private readonly WindowConfig _windowConfig;
- public HelpWindow(WindowConfig windowConfig)
+ public HelpWindow(IOptions windowConfig)
{
- _windowConfig = windowConfig;
- Loaded += (_, _) => CbShowHelp.IsChecked = windowConfig.ShowHelpOnStart;
+ _windowConfig = windowConfig.Value;
+ Loaded += (_, _) => CbShowHelp.IsChecked = _windowConfig.ShowHelpOnStart;
+ Closing += (sender, args) =>
+ {
+ args.Cancel = true;
+ Visibility = Visibility.Hidden;
+ };
InitializeComponent();
}
- private void Close(object sender, RoutedEventArgs e) => Close();
private void CbShowHelp_OnChecked(object sender, RoutedEventArgs e) => _windowConfig.ShowHelpOnStart = true;
private void CbShowHelp_OnUnchecked(object sender, RoutedEventArgs e) => _windowConfig.ShowHelpOnStart = false;
+
}
\ No newline at end of file
diff --git a/Azaion.Annotator/YOLODetector.cs b/Azaion.Annotator/YOLODetector.cs
index c304007..4ef77b0 100644
--- a/Azaion.Annotator/YOLODetector.cs
+++ b/Azaion.Annotator/YOLODetector.cs
@@ -2,6 +2,8 @@
using Azaion.Annotator.DTO;
using Azaion.Annotator.Extensions;
using Azaion.Common.DTO;
+using Azaion.Common.DTO.Config;
+using Azaion.Common.Services;
using Compunet.YoloV8;
using Microsoft.Extensions.Options;
using SixLabors.ImageSharp;
@@ -12,18 +14,27 @@ namespace Azaion.Annotator;
public interface IAIDetector
{
- List Detect(Stream stream);
+ Task> Detect(Stream imageStream, CancellationToken cancellationToken = default);
}
-public class YOLODetector(AIRecognitionConfig recognitionConfig) : IAIDetector, IDisposable
+public class YOLODetector(IOptions recognitionConfig, IResourceLoader resourceLoader) : IAIDetector, IDisposable
{
- private readonly YoloPredictor _predictor = new(recognitionConfig.AIModelPath);
+ private readonly AIRecognitionConfig _recognitionConfig = recognitionConfig.Value;
+ private YoloPredictor? _predictor;
+ private const string YOLO_MODEL = "azaion.onnx";
- public List Detect(Stream stream)
+
+ public async Task> Detect(Stream imageStream, CancellationToken cancellationToken)
{
- stream.Seek(0, SeekOrigin.Begin);
- var image = Image.Load(stream);
- var result = _predictor.Detect(image);
+ if (_predictor == null)
+ {
+ await using var stream = await resourceLoader.Load(YOLO_MODEL, cancellationToken);
+ _predictor = new YoloPredictor(stream.ToArray());
+ }
+
+ imageStream.Seek(0, SeekOrigin.Begin);
+ var image = Image.Load(imageStream);
+ var result = await _predictor.DetectAsync(image);
var imageSize = new System.Windows.Size(image.Width, image.Height);
@@ -38,7 +49,7 @@ public class YOLODetector(AIRecognitionConfig recognitionConfig) : IAIDetector,
private List FilterOverlapping(List detections)
{
- var k = recognitionConfig.TrackingIntersectionThreshold;
+ var k = _recognitionConfig.TrackingIntersectionThreshold;
var filteredDetections = new List();
for (var i = 0; i < detections.Count; i++)
{
@@ -73,5 +84,5 @@ public class YOLODetector(AIRecognitionConfig recognitionConfig) : IAIDetector,
return filteredDetections;
}
- public void Dispose() => _predictor.Dispose();
+ public void Dispose() => _predictor?.Dispose();
}
diff --git a/Azaion.Annotator/config.json b/Azaion.Annotator/config.json
deleted file mode 100644
index 5dd0ea1..0000000
--- a/Azaion.Annotator/config.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "VideosDirectory": "E:\\Azaion1\\Videos",
- "LabelsDirectory": "E:\\labels",
- "ImagesDirectory": "E:\\images",
- "ThumbnailsDirectory": "E:\\thumbnails",
- "ResultsDirectory": "E:\\results",
- "UnknownImages": "E:\\unknown",
- "AnnotationClasses": [
- { "Id": 0, "Name": "Броньована техніка", "ShortName": "Бронь" },
- { "Id": 1, "Name": "Вантажівка", "ShortName": "Вантаж" },
- { "Id": 2, "Name": "Машина легкова", "ShortName": "Машина" },
- { "Id": 3, "Name": "Артилерія", "ShortName": "Арта" },
- { "Id": 4, "Name": "Тінь від техніки", "ShortName": "Тінь" },
- { "Id": 5, "Name": "Окопи", "ShortName": "Окопи" },
- { "Id": 6, "Name": "Військовий", "ShortName": "Військов" },
- { "Id": 7, "Name": "Накати", "ShortName": "Накати" },
- { "Id": 8, "Name": "Танк з захистом", "ShortName": "Танк захист" },
- { "Id": 9, "Name": "Дим", "ShortName": "Дим" },
- { "Id": 10, "Name": "Літак", "ShortName": "Літак" }
- ],
- "MainWindowConfig": {
- "WindowSize": "1920,1080",
- "WindowLocation": "50,50",
- "FullScreen": true
- },
- "DatasetExplorerConfig": {
- "WindowSize": "1920,1080",
- "WindowLocation": "50,50",
- "FullScreen": true
- },
- "ThumbnailConfig": {
- "Size": "480,270",
- "Border": 10
- },
- "LeftPanelWidth": 300,
- "RightPanelWidth": 300,
- "ShowHelpOnStart": false,
- "VideoFormats": ["mov", "mp4"],
- "ImageFormats": ["jpg", "jpeg", "png", "bmp", "gif"],
- "AIRecognitionConfig": {
- "AIModelPath": "azaion.onnx",
- "FrameRecognitionSeconds": 2,
- "TrackingDistanceConfidence": 0.15,
- "TrackingProbabilityIncrease": 15,
- "TrackingIntersectionThreshold": 0.8
- }
-}
\ No newline at end of file
diff --git a/Azaion.Common/Azaion.Common.csproj b/Azaion.Common/Azaion.Common.csproj
index b1c00d5..66fdfdf 100644
--- a/Azaion.Common/Azaion.Common.csproj
+++ b/Azaion.Common/Azaion.Common.csproj
@@ -10,6 +10,7 @@
+
diff --git a/Azaion.Common/Constants.cs b/Azaion.Common/Constants.cs
index 24028c1..f5295ec 100644
--- a/Azaion.Common/Constants.cs
+++ b/Azaion.Common/Constants.cs
@@ -5,9 +5,17 @@ namespace Azaion.Common;
public class Constants
{
- #region DefaultConfig
-
public const string CONFIG_PATH = "config.json";
+ public const string DEFAULT_DLL_CACHE_DIR = "DllCache";
+
+ #region ApiConfig
+
+ public const string DEFAULT_API_URL = "https://api.azaion.com/";
+ public const int DEFAULT_API_RETRY_COUNT = 3;
+ public const int DEFAULT_API_TIMEOUT_SECONDS = 40;
+ #endregion ApiConfig
+
+ #region DirectoriesConfig
public const string DEFAULT_VIDEO_DIR = "video";
public const string DEFAULT_LABELS_DIR = "labels";
@@ -15,24 +23,9 @@ public class Constants
public const string DEFAULT_RESULTS_DIR = "results";
public const string DEFAULT_THUMBNAILS_DIR = "thumbnails";
- public const int DEFAULT_THUMBNAIL_BORDER = 10;
- public const double DEFAULT_FRAME_RECOGNITION_SECONDS = 2;
- public const double TRACKING_DISTANCE_CONFIDENCE = 0.15;
- public const double TRACKING_PROBABILITY_INCREASE = 15;
- public const double TRACKING_INTERSECTION_THRESHOLD = 0.8;
-
- public static readonly Size DefaultWindowSize = new(1280, 720);
- public static readonly Point DefaultWindowLocation = new(100, 100);
- public static readonly Size DefaultThumbnailSize = new(240, 135);
-
#endregion
- #region Thumbnails
-
- public const string THUMBNAIL_PREFIX = "_thumb";
- public const string THUMBNAILS_CACHE_FILE = "thumbnails.cache";
-
- #endregion
+ #region AnnotatorConfig
public static readonly List DefaultAnnotationClasses =
[
@@ -52,6 +45,34 @@ public class Constants
public static readonly List DefaultVideoFormats = ["mp4", "mov", "avi"];
public static readonly List DefaultImageFormats = ["jpg", "jpeg", "png", "bmp"];
+ # endregion AnnotatorConfig
+
+ # region AIRecognitionConfig
+
+ public const double DEFAULT_FRAME_RECOGNITION_SECONDS = 2;
+ public const double TRACKING_DISTANCE_CONFIDENCE = 0.15;
+ public const double TRACKING_PROBABILITY_INCREASE = 15;
+ public const double TRACKING_INTERSECTION_THRESHOLD = 0.8;
+
+ # endregion AIRecognitionConfig
+
+ # region WindowConfig
+
+ public static readonly Size DefaultWindowSize = new(1280, 720);
+ public static readonly Point DefaultWindowLocation = new(100, 100);
+ public static readonly Size DefaultThumbnailSize = new(240, 135);
+
+ #endregion
+
+ #region Thumbnails
+
+ public const int DEFAULT_THUMBNAIL_BORDER = 10;
+
+ public const string THUMBNAIL_PREFIX = "_thumb";
+ public const string THUMBNAILS_CACHE_FILE = "thumbnails.cache";
+
+ #endregion
+
public static TimeSpan? GetTime(string imagePath)
{
var timeStr = imagePath.Split("_").LastOrDefault();
diff --git a/Azaion.Common/DTO/AnnotationClass.cs b/Azaion.Common/DTO/AnnotationClass.cs
index edda2bf..3411eab 100644
--- a/Azaion.Common/DTO/AnnotationClass.cs
+++ b/Azaion.Common/DTO/AnnotationClass.cs
@@ -1,6 +1,6 @@
-using System.Text.Json.Serialization;
-using System.Windows.Media;
+using System.Windows.Media;
using Azaion.Common.Extensions;
+using Newtonsoft.Json;
namespace Azaion.Common.DTO;
diff --git a/Azaion.Common/DTO/Config/AIRecognitionConfig.cs b/Azaion.Common/DTO/Config/AIRecognitionConfig.cs
index 5d7f5d4..fdcfe7d 100644
--- a/Azaion.Common/DTO/Config/AIRecognitionConfig.cs
+++ b/Azaion.Common/DTO/Config/AIRecognitionConfig.cs
@@ -1,8 +1,7 @@
-namespace Azaion.Annotator.DTO;
+namespace Azaion.Common.DTO.Config;
public class AIRecognitionConfig
{
- public string AIModelPath { get; set; } = null!;
public double FrameRecognitionSeconds { get; set; }
public double TrackingDistanceConfidence { get; set; }
public double TrackingProbabilityIncrease { get; set; }
diff --git a/Azaion.Common/DTO/Config/ApiConfig.cs b/Azaion.Common/DTO/Config/ApiConfig.cs
index 144cb68..607c68d 100644
--- a/Azaion.Common/DTO/Config/ApiConfig.cs
+++ b/Azaion.Common/DTO/Config/ApiConfig.cs
@@ -1,4 +1,4 @@
-namespace Azaion.Suite.Services.DTO;
+namespace Azaion.Common.DTO.Config;
public class ApiConfig
{
@@ -6,8 +6,3 @@ public class ApiConfig
public int RetryCount {get;set;}
public double TimeoutSeconds { get; set; }
}
-
-public class LocalFilesConfig
-{
- public string DllPath { get; set; } = null!;
-}
\ No newline at end of file
diff --git a/Azaion.Common/DTO/Config/AppConfig.cs b/Azaion.Common/DTO/Config/AppConfig.cs
index ae8bac6..84528cb 100644
--- a/Azaion.Common/DTO/Config/AppConfig.cs
+++ b/Azaion.Common/DTO/Config/AppConfig.cs
@@ -1,8 +1,5 @@
using System.IO;
using System.Text;
-using Azaion.Annotator.DTO;
-using Azaion.Suite.Services.DTO;
-using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
namespace Azaion.Common.DTO.Config;
@@ -11,14 +8,14 @@ public class AppConfig
{
public ApiConfig ApiConfig { get; set; } = null!;
+ public DirectoriesConfig DirectoriesConfig { get; set; } = null!;
+
public AnnotationConfig AnnotationConfig { get; set; } = null!;
public WindowConfig WindowConfig { get; set; } = null!;
public AIRecognitionConfig AIRecognitionConfig { get; set; } = null!;
- public DirectoriesConfig DirectoriesConfig { get; set; } = null!;
-
public ThumbnailConfig ThumbnailConfig { get; set; } = null!;
}
@@ -40,6 +37,13 @@ public class ConfigUpdater : IConfigUpdater
var appConfig = new AppConfig
{
+ ApiConfig = new ApiConfig
+ {
+ Url = Constants.DEFAULT_API_URL,
+ RetryCount = Constants.DEFAULT_API_RETRY_COUNT,
+ TimeoutSeconds = Constants.DEFAULT_API_TIMEOUT_SECONDS
+ },
+
AnnotationConfig = new AnnotationConfig
{
AnnotationClasses = Constants.DefaultAnnotationClasses,
@@ -51,7 +55,6 @@ public class ConfigUpdater : IConfigUpdater
{
WindowSize = Constants.DefaultWindowSize,
WindowLocation = Constants.DefaultWindowLocation,
- ShowHelpOnStart = true,
FullScreen = true,
LeftPanelWidth = 250,
RightPanelWidth = 250,
@@ -74,7 +77,6 @@ public class ConfigUpdater : IConfigUpdater
AIRecognitionConfig = new AIRecognitionConfig
{
- AIModelPath = "azaion.onnx",
FrameRecognitionSeconds = Constants.DEFAULT_FRAME_RECOGNITION_SECONDS,
TrackingDistanceConfidence = Constants.TRACKING_DISTANCE_CONFIDENCE,
TrackingProbabilityIncrease = Constants.TRACKING_PROBABILITY_INCREASE,
diff --git a/Azaion.Common/DTO/Config/DirectoriesConfig.cs b/Azaion.Common/DTO/Config/DirectoriesConfig.cs
index 5bdd95c..7813875 100644
--- a/Azaion.Common/DTO/Config/DirectoriesConfig.cs
+++ b/Azaion.Common/DTO/Config/DirectoriesConfig.cs
@@ -1,4 +1,4 @@
-namespace Azaion.Common.DTO;
+namespace Azaion.Common.DTO.Config;
public class DirectoriesConfig
{
diff --git a/Azaion.Suite/HardwareInfo.cs b/Azaion.Common/DTO/HardwareInfo.cs
similarity index 72%
rename from Azaion.Suite/HardwareInfo.cs
rename to Azaion.Common/DTO/HardwareInfo.cs
index 1e7fe91..b219de8 100644
--- a/Azaion.Suite/HardwareInfo.cs
+++ b/Azaion.Common/DTO/HardwareInfo.cs
@@ -1,9 +1,10 @@
-namespace Azaion.Suite;
+namespace Azaion.Common.DTO;
public class HardwareInfo
{
public string CPU { get; set; } = null!;
public string GPU { get; set; } = null!;
+ public string MacAddress { get; set; } = null!;
public string Memory { get; set; } = null!;
public string Hash { get; set; } = null!;
diff --git a/Azaion.Common/Extensions/ServiceCollectionExtensions.cs b/Azaion.Common/Extensions/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..582c7d5
--- /dev/null
+++ b/Azaion.Common/Extensions/ServiceCollectionExtensions.cs
@@ -0,0 +1,10 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Azaion.Common.Extensions;
+
+public static class ServiceCollectionExtensions
+{
+ public static IServiceCollection ConfigureSection(this IServiceCollection services, IConfiguration config) where T: class =>
+ services.Configure(config.GetSection(typeof(T).Name));
+}
\ No newline at end of file
diff --git a/Azaion.Suite/Services/AzaionApiClient.cs b/Azaion.Common/Services/AzaionApiClient.cs
similarity index 87%
rename from Azaion.Suite/Services/AzaionApiClient.cs
rename to Azaion.Common/Services/AzaionApiClient.cs
index ccc3f6a..3e91363 100644
--- a/Azaion.Suite/Services/AzaionApiClient.cs
+++ b/Azaion.Common/Services/AzaionApiClient.cs
@@ -4,12 +4,12 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Security;
using System.Text;
-using Azaion.Suite.Services.DTO;
+using Azaion.Common.DTO;
using Newtonsoft.Json;
-namespace Azaion.Suite.Services;
+namespace Azaion.Common.Services;
-public class AzaionApiClient(HttpClient httpClient)
+public class AzaionApiClient(HttpClient httpClient) : IDisposable
{
const string JSON_MEDIA = "application/json";
@@ -26,11 +26,11 @@ public class AzaionApiClient(HttpClient httpClient)
Password = password.ToSecureString();
}
- public async Task GetResource(string password, HardwareInfo hardware, ResourceEnum resourceEnum)
+ public async Task GetResource(string fileName, string password, HardwareInfo hardware)
{
var response = await Send(httpClient, new HttpRequestMessage(HttpMethod.Post, "/resources/get")
{
- Content = new StringContent(JsonConvert.SerializeObject(new { password, hardware, resourceEnum }), Encoding.UTF8, JSON_MEDIA)
+ Content = new StringContent(JsonConvert.SerializeObject(new { fileName, password, hardware }), Encoding.UTF8, JSON_MEDIA)
});
return await response.Content.ReadAsStreamAsync();
}
@@ -82,4 +82,10 @@ public class AzaionApiClient(HttpClient httpClient)
var result = await response.Content.ReadAsStringAsync();
throw new Exception($"Failed: {response.StatusCode}! Result: {result}");
}
+
+ public void Dispose()
+ {
+ httpClient.Dispose();
+ Password.Dispose();
+ }
}
diff --git a/Azaion.Suite/Services/HardwareService.cs b/Azaion.Common/Services/HardwareService.cs
similarity index 98%
rename from Azaion.Suite/Services/HardwareService.cs
rename to Azaion.Common/Services/HardwareService.cs
index 8adfbb1..2e6de21 100644
--- a/Azaion.Suite/Services/HardwareService.cs
+++ b/Azaion.Common/Services/HardwareService.cs
@@ -2,8 +2,9 @@
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;
+using Azaion.Common.DTO;
-namespace Azaion.Suite.Services;
+namespace Azaion.Common.Services;
public interface IHardwareService
{
diff --git a/Azaion.Common/Services/ResourceLoader.cs b/Azaion.Common/Services/ResourceLoader.cs
new file mode 100644
index 0000000..515eaac
--- /dev/null
+++ b/Azaion.Common/Services/ResourceLoader.cs
@@ -0,0 +1,22 @@
+using System.IO;
+
+namespace Azaion.Common.Services;
+
+public interface IResourceLoader
+{
+ Task Load(string fileName, CancellationToken cancellationToken = default);
+}
+
+public class ResourceLoader(string email, string password, AzaionApiClient api, IHardwareService hardwareService) : IResourceLoader
+{
+ public async Task Load(string fileName, CancellationToken cancellationToken = default)
+ {
+ var hardwareInfo = await hardwareService.GetHardware();
+ var encryptedStream = await api.GetResource(fileName, password, hardwareInfo);
+
+ var key = Security.MakeEncryptionKey(email, password, hardwareInfo.Hash);
+ var stream = new MemoryStream();
+ await encryptedStream.DecryptTo(stream, key, cancellationToken);
+ return stream;
+ }
+}
\ No newline at end of file
diff --git a/Azaion.Suite/Services/Security.cs b/Azaion.Common/Services/Security.cs
similarity index 98%
rename from Azaion.Suite/Services/Security.cs
rename to Azaion.Common/Services/Security.cs
index f06e678..cc0ec33 100644
--- a/Azaion.Suite/Services/Security.cs
+++ b/Azaion.Common/Services/Security.cs
@@ -4,7 +4,7 @@ using System.Security;
using System.Security.Cryptography;
using System.Text;
-namespace Azaion.Suite.Services;
+namespace Azaion.Common.Services;
public static class Security
{
diff --git a/Azaion.Dataset/DatasetExplorerEventHandler.cs b/Azaion.Dataset/DatasetExplorerEventHandler.cs
index 0b8e39d..72eff92 100644
--- a/Azaion.Dataset/DatasetExplorerEventHandler.cs
+++ b/Azaion.Dataset/DatasetExplorerEventHandler.cs
@@ -1,6 +1,7 @@
using System.IO;
using System.Windows.Input;
using Azaion.Common.DTO;
+using Azaion.Common.DTO.Config;
using MediatR;
using Microsoft.Extensions.Options;
diff --git a/Azaion.Launcher/App.xaml b/Azaion.Launcher/App.xaml
new file mode 100644
index 0000000..1af3995
--- /dev/null
+++ b/Azaion.Launcher/App.xaml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/Azaion.Launcher/App.xaml.cs b/Azaion.Launcher/App.xaml.cs
new file mode 100644
index 0000000..d26963a
--- /dev/null
+++ b/Azaion.Launcher/App.xaml.cs
@@ -0,0 +1,3 @@
+namespace Azaion.Launcher;
+
+public partial class App;
\ No newline at end of file
diff --git a/Azaion.Launcher/AssemblyInfo.cs b/Azaion.Launcher/AssemblyInfo.cs
new file mode 100644
index 0000000..4a05c7d
--- /dev/null
+++ b/Azaion.Launcher/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
\ No newline at end of file
diff --git a/Azaion.Launcher/Azaion.Launcher.csproj b/Azaion.Launcher/Azaion.Launcher.csproj
new file mode 100644
index 0000000..b2660a7
--- /dev/null
+++ b/Azaion.Launcher/Azaion.Launcher.csproj
@@ -0,0 +1,19 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ enable
+ true
+
+
+
+
+ MSBuild:Compile
+ Wpf
+ Designer
+
+
+
+
diff --git a/Azaion.Suite/Loader.xaml b/Azaion.Launcher/Loader.xaml
similarity index 99%
rename from Azaion.Suite/Loader.xaml
rename to Azaion.Launcher/Loader.xaml
index 493f826..18bbea9 100644
--- a/Azaion.Suite/Loader.xaml
+++ b/Azaion.Launcher/Loader.xaml
@@ -1,4 +1,4 @@
- Close();
+
+ private void MainMouseMove(object sender, MouseEventArgs e)
+ {
+ if (e.OriginalSource is Button || e.OriginalSource is TextBox)
+ return;
+
+ if (e.LeftButton == MouseButtonState.Pressed)
+ DragMove();
+ }
+}
diff --git a/Azaion.Annotator.sln b/Azaion.Suite.sln
similarity index 84%
rename from Azaion.Annotator.sln
rename to Azaion.Suite.sln
index 92e8a40..a9c8b4f 100644
--- a/Azaion.Annotator.sln
+++ b/Azaion.Suite.sln
@@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Common", "Azaion.Com
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Dataset", "Azaion.Dataset\Azaion.Dataset.csproj", "{01A5CA37-A62E-4EF3-8678-D72CD9525677}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Launcher", "Azaion.Launcher\Azaion.Launcher.csproj", "{00CC9AFE-2952-4943-BCBA-976AE03DE841}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -36,5 +38,9 @@ Global
{01A5CA37-A62E-4EF3-8678-D72CD9525677}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01A5CA37-A62E-4EF3-8678-D72CD9525677}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01A5CA37-A62E-4EF3-8678-D72CD9525677}.Release|Any CPU.Build.0 = Release|Any CPU
+ {00CC9AFE-2952-4943-BCBA-976AE03DE841}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {00CC9AFE-2952-4943-BCBA-976AE03DE841}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {00CC9AFE-2952-4943-BCBA-976AE03DE841}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {00CC9AFE-2952-4943-BCBA-976AE03DE841}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/Azaion.Suite/App.xaml.cs b/Azaion.Suite/App.xaml.cs
index 75f6436..3265eb1 100644
--- a/Azaion.Suite/App.xaml.cs
+++ b/Azaion.Suite/App.xaml.cs
@@ -1,16 +1,21 @@
-using System.Reflection;
+using System.IO;
+using System.Net.Http;
+using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Azaion.Annotator;
using Azaion.Annotator.DTO;
using Azaion.Annotator.Extensions;
+using Azaion.Common;
using Azaion.Common.DTO;
using Azaion.Common.DTO.Config;
using Azaion.Common.Extensions;
+using Azaion.Common.Services;
using Azaion.Suite.Services;
-using Azaion.Suite.Services.DTO;
using Azaion.Dataset;
+using Azaion.Suite.Services.DTO;
+using CommandLine;
using LibVLCSharp.Shared;
using MediatR;
using Microsoft.Extensions.Configuration;
@@ -18,16 +23,53 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
using Serilog;
namespace Azaion.Suite;
-public partial class App : Application
+public partial class App
{
private readonly IHost _host;
private readonly ILogger _logger;
private readonly IMediator _mediator;
+ private static readonly List EncryptedResources =
+ [
+ "Azaion.Annotator.dll",
+ "Azaion.Dataset.dll"
+ ];
+
+ private static readonly IResourceLoader? ResourceLoader;
+
+ static App()
+ {
+ var result = Parser.Default.ParseArguments(Environment.GetCommandLineArgs());
+ if (result.Errors.Any())
+ return;
+
+ var configStr = File.ReadAllText(Constants.CONFIG_PATH);
+ var apiConfig = JsonConvert.DeserializeObject(configStr)!.ApiConfig;
+ var api = new AzaionApiClient(new HttpClient
+ {
+ BaseAddress = new Uri(apiConfig.Url),
+ Timeout = TimeSpan.FromSeconds(apiConfig.TimeoutSeconds)
+ });
+ var email = result.Value.Email;
+ var password = result.Value.Password;
+
+ api.Login(email, password);
+
+ ResourceLoader = new ResourceLoader(email, password, api, new HardwareService());
+ foreach (var resource in EncryptedResources)
+ {
+ var stream = ResourceLoader.Load(resource).GetAwaiter().GetResult();
+ Assembly.Load(stream.ToArray());
+ }
+
+ new ConfigUpdater().CheckConfig();
+ }
+
public App()
{
Log.Logger = new LoggerConfiguration()
@@ -42,21 +84,30 @@ public partial class App : Application
_host = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, config) => config
.AddCommandLine(Environment.GetCommandLineArgs())
- .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true))
+ .AddJsonFile(Constants.CONFIG_PATH, optional: true, reloadOnChange: true))
.ConfigureServices((context, services) =>
{
- services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton(ResourceLoader!);
+
+ services.Configure(context.Configuration);
+ services.ConfigureSection(context.Configuration);
+ services.ConfigureSection(context.Configuration);
+ services.ConfigureSection(context.Configuration);
+ services.ConfigureSection(context.Configuration);
+ services.ConfigureSection(context.Configuration);
+ services.ConfigureSection(context.Configuration);
- services.Configure(context.Configuration.GetSection(nameof(ApiConfig)));
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
- services.AddMediatR(c => c.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
+ services.AddMediatR(c => c.RegisterServicesFromAssemblies(
+ typeof(Annotator.Annotator).Assembly,
+ typeof(DatasetExplorer).Assembly));
services.AddSingleton(_ => new LibVLC());
services.AddSingleton();
services.AddSingleton(sp =>
@@ -93,7 +144,7 @@ public partial class App : Application
{
EventManager.RegisterClassHandler(typeof(UIElement), UIElement.KeyDownEvent, new RoutedEventHandler(GlobalClick));
await _host.StartAsync();
- _host.Services.GetRequiredService().Show();
+ _host.Services.GetRequiredService().Show();
base.OnStartup(e);
}
diff --git a/Azaion.Suite/Azaion.Suite.csproj b/Azaion.Suite/Azaion.Suite.csproj
index 2c21c5a..da1416d 100644
--- a/Azaion.Suite/Azaion.Suite.csproj
+++ b/Azaion.Suite/Azaion.Suite.csproj
@@ -10,6 +10,9 @@
+
+
+
@@ -19,13 +22,7 @@
-
-
-
-
-
- PreserveNewest
-
+
diff --git a/Azaion.Suite/DynamicAssemblyLoader.cs b/Azaion.Suite/DynamicAssemblyLoader.cs
deleted file mode 100644
index 847849b..0000000
--- a/Azaion.Suite/DynamicAssemblyLoader.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.IO;
-using System.Reflection;
-using System.Runtime.Loader;
-using Azaion.Suite.Services.DTO;
-using Microsoft.Extensions.Options;
-
-namespace Azaion.Suite;
-
-public class DynamicAssemblyLoader(IOptions localFilesConfig) : AssemblyLoadContext
-{
- private static readonly Dictionary LoadedAssemblies = new();
-
- static DynamicAssemblyLoader()
- {
- LoadedAssemblies = Default.Assemblies.ToDictionary(a => a.GetName().Name, a => a);
- }
-
- protected override Assembly Load(AssemblyName assemblyName)
- {
- var assembly = LoadedAssemblies.GetValueOrDefault(assemblyName.Name);
- if (assembly != null)
- return assembly;
-
- var currentLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
-
- var asm = Assembly.LoadFile(Path.Combine(currentLocation, localFilesConfig.Value.DllPath, $"{assemblyName.Name!}.dll"));
- return asm;
- }
-}
\ No newline at end of file
diff --git a/Azaion.Suite/Loader.xaml.cs b/Azaion.Suite/Loader.xaml.cs
deleted file mode 100644
index ecb2890..0000000
--- a/Azaion.Suite/Loader.xaml.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System.IO;
-using System.Reflection;
-using System.Runtime.Loader;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using Azaion.Suite.Services.DTO;
-using Microsoft.Extensions.Options;
-
-namespace Azaion.Suite;
-
-public partial class Loader : Window
-{
- private readonly IResourceLoader _resourceLoader;
- private readonly IOptions _localFilesConfig;
-
- public Loader(IResourceLoader resourceLoader, IOptions localFilesConfig)
- {
- _resourceLoader = resourceLoader;
- _localFilesConfig = localFilesConfig;
- InitializeComponent();
- }
-
- private async void RunClick(object sender, RoutedEventArgs e)
- {
- var stream = new MemoryStream();
- await _resourceLoader.LoadAnnotator(TbEmail.Text, TbPassword.Password, stream);
- stream.Seek(0, SeekOrigin.Begin);
- var loader = new AssemblyLoadContext("DynamicContext", isCollectible: true);
- var annotatorAssembly = loader.LoadFromStream(stream);
-
- var appType = annotatorAssembly.GetType("Azaion.Annotator.App");
- var appInstance = Activator.CreateInstance(appType);
- var runMethod = appType.GetMethod("Run", BindingFlags.Public | BindingFlags.Instance);
- if (runMethod != null)
- {
- runMethod.Invoke(appInstance, null);
- }
-
- // var entryPoint = annotatorAssembly.EntryPoint;
- // if (entryPoint == null)
- // return;
- //
- // var o = annotatorAssembly.CreateInstance(entryPoint.Name);
- // entryPoint.Invoke(o, null);
- }
-
- private void CloseClick(object sender, RoutedEventArgs e) => Close();
-
- private void MainMouseMove(object sender, MouseEventArgs e)
- {
- if (e.OriginalSource is Button || e.OriginalSource is TextBox)
- return;
-
- if (e.LeftButton == MouseButtonState.Pressed)
- DragMove();
- }
-}
diff --git a/Azaion.Suite/MainSuite.xaml b/Azaion.Suite/MainSuite.xaml
index 10485a8..4479a5f 100644
--- a/Azaion.Suite/MainSuite.xaml
+++ b/Azaion.Suite/MainSuite.xaml
@@ -3,10 +3,13 @@
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.Suite"
mc:Ignorable="d"
Title="MainSuite" Height="450" Width="800">
-
+
+
diff --git a/Azaion.Suite/MainSuite.xaml.cs b/Azaion.Suite/MainSuite.xaml.cs
index 8f6158d..b8c1060 100644
--- a/Azaion.Suite/MainSuite.xaml.cs
+++ b/Azaion.Suite/MainSuite.xaml.cs
@@ -1,19 +1,25 @@
using System.IO;
using System.Windows;
+using System.Windows.Controls;
using Azaion.Annotator.Extensions;
using Azaion.Common.DTO.Config;
+using Azaion.Dataset;
using Microsoft.Extensions.Options;
namespace Azaion.Suite;
-public partial class MainSuite : Window
+public partial class MainSuite
{
private readonly AppConfig _appConfig;
private readonly IConfigUpdater _configUpdater;
+ private readonly Annotator.Annotator _annotator;
+ private readonly DatasetExplorer _datasetExplorer;
- public MainSuite(IOptions appConfig, IConfigUpdater configUpdater)
+ public MainSuite(IOptions appConfig, IConfigUpdater configUpdater, Annotator.Annotator annotator, DatasetExplorer datasetExplorer)
{
_configUpdater = configUpdater;
+ _annotator = annotator;
+ _datasetExplorer = datasetExplorer;
_appConfig = appConfig.Value;
InitializeComponent();
Loaded += OnLoaded;
@@ -41,6 +47,17 @@ public partial class MainSuite : Window
if (_appConfig.WindowConfig.FullScreen)
WindowState = WindowState.Maximized;
+
+ MainTabControl.Items.Add(new TabItem
+ {
+ Header = "Annotator",
+ Content = _annotator.Content
+ });
+ MainTabControl.Items.Add(new TabItem
+ {
+ Header = "Dataset Explorer",
+ Content = _datasetExplorer.Content
+ });
}
private async Task SaveUserSettings()
diff --git a/Azaion.Suite/ResourceLoader.cs b/Azaion.Suite/ResourceLoader.cs
deleted file mode 100644
index 44b2337..0000000
--- a/Azaion.Suite/ResourceLoader.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System.IO;
-using System.Reflection;
-using Azaion.Suite.Services;
-using Azaion.Suite.Services.DTO;
-using Microsoft.Extensions.Options;
-
-namespace Azaion.Suite;
-
-public interface IResourceLoader
-{
- Task LoadAnnotator(string email, string password, Stream outStream, CancellationToken cancellationToken = default);
- Assembly LoadAssembly(string name, CancellationToken cancellationToken = default);
-}
-
-public class ResourceLoader(AzaionApiClient azaionApi, IHardwareService hardwareService, IOptions localFilesConfig) : IResourceLoader
-{
- public async Task LoadAnnotator(string email, string password, Stream outStream, CancellationToken cancellationToken = default)
- {
- var hardwareInfo = await hardwareService.GetHardware();
- azaionApi.Login(email, password);
- var key = Security.MakeEncryptionKey(email, password, hardwareInfo.Hash);
-
- var encryptedStream = await azaionApi.GetResource(password, hardwareInfo, ResourceEnum.AnnotatorDll);
-
- await encryptedStream.DecryptTo(outStream, key, cancellationToken);
- //return Assembly.Load(stream.ToArray());
- }
-
- public Assembly LoadAssembly(string name, CancellationToken cancellationToken = default)
- {
- var dllValues = name.Split(",");
- var dllName = $"{dllValues[0]}.dll";
- var currentLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
-
- var asm = Assembly.LoadFile(Path.Combine(currentLocation, localFilesConfig.Value.DllPath, dllName));
- return asm;
- }
-}
\ No newline at end of file
diff --git a/Azaion.Suite/Services/DTO/ResourceEnum.cs b/Azaion.Suite/Services/DTO/ResourceEnum.cs
deleted file mode 100644
index 3ba3058..0000000
--- a/Azaion.Suite/Services/DTO/ResourceEnum.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Azaion.Suite.Services.DTO;
-
-public enum ResourceEnum
-{
- None = 0,
- AnnotatorDll = 10,
- AIModelRKNN = 20,
- AIModelONNX = 30,
-}
diff --git a/Azaion.Suite/SuiteCommandLineOptions.cs b/Azaion.Suite/SuiteCommandLineOptions.cs
new file mode 100644
index 0000000..b825694
--- /dev/null
+++ b/Azaion.Suite/SuiteCommandLineOptions.cs
@@ -0,0 +1,12 @@
+using CommandLine;
+
+namespace Azaion.Suite.Services.DTO;
+
+public class SuiteCommandLineOptions
+{
+ [Option('e', "email", Required = true, HelpText = "The email for authorization.")]
+ public string Email { get; set; } = null!;
+
+ [Option('p', "password", Required = true, HelpText = "The password for authorization.")]
+ public string Password { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/Azaion.Suite/appsettings.json b/Azaion.Suite/appsettings.json
deleted file mode 100644
index d385971..0000000
--- a/Azaion.Suite/appsettings.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "ApiConfig": {
- "Url": "https://api.azaion.com",
- "TimeoutSeconds": 20,
- "RetryCount": 3
- },
- "LocalFilesConfig": {
- "DllPath": "AzaionSuite"
- }
-}
\ No newline at end of file
diff --git a/Azaion.Suite/appsettings_manual.json b/Azaion.Suite/appsettings_manual.json
new file mode 100644
index 0000000..493ee7d
--- /dev/null
+++ b/Azaion.Suite/appsettings_manual.json
@@ -0,0 +1,57 @@
+{
+ "ApiConfig": {
+ "Url": "https://api.azaion.com",
+ "TimeoutSeconds": 20,
+ "RetryCount": 3
+ },
+
+ "DirectoriesConfig": {
+ "VideosDirectory" : "E:\\Azaion1\\Videos",
+ "LabelsDirectory" : "E:\\labels",
+ "ImagesDirectory" : "E:\\images",
+ "ResultsDirectory" : "E:\\results",
+ "ThumbnailsDirectory" : "E:\\thumbnails",
+
+ "DllCacheDirectory" : "Cache"
+ },
+
+ "AnnotationConfig" : {
+ "AnnotationClasses": [
+ { "Id": 0, "Name": "Броньована техніка", "ShortName": "Бронь" },
+ { "Id": 1, "Name": "Вантажівка", "ShortName": "Вантаж" },
+ { "Id": 2, "Name": "Машина легкова", "ShortName": "Машина" },
+ { "Id": 3, "Name": "Артилерія", "ShortName": "Арта" },
+ { "Id": 4, "Name": "Тінь від техніки", "ShortName": "Тінь" },
+ { "Id": 5, "Name": "Окопи", "ShortName": "Окопи" },
+ { "Id": 6, "Name": "Військовий", "ShortName": "Військов" },
+ { "Id": 7, "Name": "Накати", "ShortName": "Накати" },
+ { "Id": 8, "Name": "Танк з захистом", "ShortName": "Танк захист" },
+ { "Id": 9, "Name": "Дим", "ShortName": "Дим" },
+ { "Id": 10, "Name": "Літак", "ShortName": "Літак" }
+ ],
+ "LastSelectedExplorerClass": 1,
+ "VideoFormats": ["mov", "mp4"],
+ "ImageFormats": ["jpg", "jpeg", "png", "bmp", "gif"]
+ },
+
+ "WindowConfig": {
+ "WindowSize": "1920,1080",
+ "WindowLocation": "50,50",
+ "FullScreen": true,
+ "LeftPanelWidth": 220,
+ "RightPanelWidth": 220,
+ "ShowHelpOnStart": false
+ },
+
+ "AIRecognitionConfig": {
+ "FrameRecognitionSeconds" : 2,
+ "TrackingDistanceConfidence" : 0.15,
+ "TrackingProbabilityIncrease" : 15,
+ "TrackingIntersectionThreshold" : 0.8
+ },
+
+ "ThumbnailConfig": {
+ "Size" : "240,135",
+ "Border" : 10
+ }
+}
\ No newline at end of file
diff --git a/Azaion.Test/HardwareServiceTest.cs b/Azaion.Test/HardwareServiceTest.cs
index b13497e..b299bb6 100644
--- a/Azaion.Test/HardwareServiceTest.cs
+++ b/Azaion.Test/HardwareServiceTest.cs
@@ -1,4 +1,5 @@
-using Azaion.Suite;
+using Azaion.Common.Services;
+using Azaion.Suite;
using Azaion.Suite.Services;
using Xunit;