mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 23:56:30 +00:00
update AI initializing
rework AIAvailabilityStatus events to mediatr
This commit is contained in:
@@ -14,6 +14,7 @@ using Azaion.Common.DTO.Config;
|
||||
using Azaion.Common.Events;
|
||||
using Azaion.Common.Extensions;
|
||||
using Azaion.Common.Services;
|
||||
using Azaion.Common.Services.Inference;
|
||||
using LibVLCSharp.Shared;
|
||||
using MediatR;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
@@ -106,38 +107,6 @@ public partial class Annotator
|
||||
_logger.LogError(e, e.Message);
|
||||
}
|
||||
};
|
||||
_inferenceClient.AIAvailabilityReceived += (_, command) =>
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
_logger.LogInformation(command.Message);
|
||||
var aiEnabled = command.Message == "enabled";
|
||||
AIDetectBtn.IsEnabled = aiEnabled;
|
||||
var aiDisabledText = "Будь ласка, зачекайте, наразі розпізнавання AI недоступне";
|
||||
var messagesDict = new Dictionary<string, string>
|
||||
{
|
||||
{ "disabled", aiDisabledText },
|
||||
{ "downloading", "Будь ласка зачекайте, йде завантаження AI для Вашої відеокарти" },
|
||||
{ "converting", "Будь ласка зачекайте, йде налаштування AI під Ваше залізо. (5-12 хвилин в залежності від моделі відеокарти, до 50 хв на старих GTX1650)" },
|
||||
{ "uploading", "Будь ласка зачекайте, йде зберігання" },
|
||||
{ "enabled", "AI готовий для розпізнавання" }
|
||||
};
|
||||
|
||||
if (command.Message?.StartsWith("Error") ?? false)
|
||||
{
|
||||
_logger.LogError(command.Message);
|
||||
StatusHelp.Text = command.Message;
|
||||
}
|
||||
|
||||
else
|
||||
StatusHelp.Text = messagesDict!.GetValueOrDefault(command.Message, aiDisabledText);
|
||||
|
||||
if (aiEnabled)
|
||||
StatusHelp.Foreground = aiEnabled ? Brushes.White : Brushes.Red;
|
||||
});
|
||||
};
|
||||
_inferenceClient.Send(RemoteCommand.Create(CommandType.AIAvailabilityCheck));
|
||||
|
||||
Editor.GetTimeFunc = () => TimeSpan.FromMilliseconds(_mediaPlayer.Time);
|
||||
MapMatcherComponent.Init(_appConfig, gpsMatcherService);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using Azaion.Common.DTO.Config;
|
||||
using Azaion.Common.Events;
|
||||
using Azaion.Common.Extensions;
|
||||
using Azaion.Common.Services;
|
||||
using Azaion.Common.Services.Inference;
|
||||
using GMap.NET;
|
||||
using GMap.NET.WindowsPresentation;
|
||||
using LibVLCSharp.Shared;
|
||||
@@ -43,7 +44,8 @@ public class AnnotatorEventHandler(
|
||||
INotificationHandler<AnnotationsDeletedEvent>,
|
||||
INotificationHandler<AnnotationAddedEvent>,
|
||||
INotificationHandler<SetStatusTextEvent>,
|
||||
INotificationHandler<GPSMatcherResultProcessedEvent>
|
||||
INotificationHandler<GPSMatcherResultProcessedEvent>,
|
||||
INotificationHandler<AIAvailabilityStatusEvent>
|
||||
{
|
||||
private const int STEP = 20;
|
||||
private const int LARGE_STEP = 5000;
|
||||
@@ -472,4 +474,14 @@ public class AnnotatorEventHandler(
|
||||
map.SatelliteMap.Position = pointLatLon;
|
||||
map.SatelliteMap.ZoomAndCenterMarkers(null);
|
||||
}
|
||||
|
||||
public async Task Handle(AIAvailabilityStatusEvent e, CancellationToken cancellationToken)
|
||||
{
|
||||
mainWindow.Dispatcher.Invoke(() =>
|
||||
{
|
||||
logger.LogInformation(e.ToString());
|
||||
mainWindow.AIDetectBtn.IsEnabled = e.Status == AIAvailabilityEnum.Enabled;
|
||||
mainWindow.StatusHelp.Text = e.ToString();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using MediatR;
|
||||
using MessagePack;
|
||||
|
||||
namespace Azaion.Common.DTO;
|
||||
|
||||
public enum AIAvailabilityEnum
|
||||
{
|
||||
None = 0,
|
||||
Downloading = 10,
|
||||
Converting = 20,
|
||||
Uploading = 30,
|
||||
Enabled = 200,
|
||||
Error = 500
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
public class AIAvailabilityStatusEvent : INotification
|
||||
{
|
||||
[Key("s")] public AIAvailabilityEnum Status { get; set; }
|
||||
[Key("m")] public string? ErrorMessage { get; set; }
|
||||
|
||||
public override string ToString() => $"{StatusMessageDict.GetValueOrDefault(Status, "Помилка")} {ErrorMessage}";
|
||||
|
||||
private static readonly Dictionary<AIAvailabilityEnum, string> StatusMessageDict = new()
|
||||
{
|
||||
{ AIAvailabilityEnum.Downloading, "Йде завантаження AI для Вашої відеокарти" },
|
||||
{ AIAvailabilityEnum.Converting, "Йде налаштування AI під Ваше залізо. (5-12 хвилин в залежності від моделі відеокарти, до 50 хв на старих GTX1650)" },
|
||||
{ AIAvailabilityEnum.Uploading, "Йде зберігання AI" },
|
||||
{ AIAvailabilityEnum.Enabled, "AI готовий для розпізнавання" },
|
||||
{ AIAvailabilityEnum.Error, "Помилка під час налаштування AI" }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ using RabbitMQ.Stream.Client.Reliable;
|
||||
namespace Azaion.Common.Services;
|
||||
|
||||
// SHOULD BE ONLY ONE INSTANCE OF AnnotationService. Do not add ANY NotificationHandler to it!
|
||||
// Queue consumer should be created only once.
|
||||
public class AnnotationService : IAnnotationService
|
||||
{
|
||||
private readonly IDbFactory _dbFactory;
|
||||
|
||||
+15
-16
@@ -1,18 +1,17 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Azaion.Common.DTO;
|
||||
using MediatR;
|
||||
using MessagePack;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
|
||||
namespace Azaion.Common.Services;
|
||||
namespace Azaion.Common.Services.Inference;
|
||||
|
||||
public interface IInferenceClient : IDisposable
|
||||
{
|
||||
event EventHandler<RemoteCommand>? InferenceDataReceived;
|
||||
event EventHandler<RemoteCommand>? AIAvailabilityReceived;
|
||||
void Send(RemoteCommand create);
|
||||
void Stop();
|
||||
}
|
||||
@@ -20,21 +19,22 @@ public interface IInferenceClient : IDisposable
|
||||
public class InferenceClient : IInferenceClient
|
||||
{
|
||||
private readonly ILogger<InferenceClient> _logger;
|
||||
public event EventHandler<RemoteCommand>? BytesReceived;
|
||||
public event EventHandler<RemoteCommand>? InferenceDataReceived;
|
||||
public event EventHandler<RemoteCommand>? AIAvailabilityReceived;
|
||||
|
||||
private readonly DealerSocket _dealer = new();
|
||||
private readonly NetMQPoller _poller = new();
|
||||
private readonly Guid _clientId = Guid.NewGuid();
|
||||
private readonly InferenceClientConfig _inferenceClientConfig;
|
||||
private readonly LoaderClientConfig _loaderClientConfig;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public InferenceClient(ILogger<InferenceClient> logger, IOptions<InferenceClientConfig> inferenceConfig, IOptions<LoaderClientConfig> loaderConfig)
|
||||
public InferenceClient(ILogger<InferenceClient> logger, IOptions<InferenceClientConfig> inferenceConfig,
|
||||
IMediator mediator,
|
||||
IOptions<LoaderClientConfig> loaderConfig)
|
||||
{
|
||||
_logger = logger;
|
||||
_inferenceClientConfig = inferenceConfig.Value;
|
||||
_loaderClientConfig = loaderConfig.Value;
|
||||
_mediator = mediator;
|
||||
Start();
|
||||
}
|
||||
|
||||
@@ -59,32 +59,31 @@ public class InferenceClient : IInferenceClient
|
||||
_dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N"));
|
||||
_dealer.Connect($"tcp://{_inferenceClientConfig.ZeroMqHost}:{_inferenceClientConfig.ZeroMqPort}");
|
||||
|
||||
_dealer.ReceiveReady += (_, e) => ProcessClientCommand(e.Socket);
|
||||
_dealer.ReceiveReady += async (_, e) => await ProcessClientCommand(e.Socket);
|
||||
_poller.Add(_dealer);
|
||||
_ = Task.Run(() => _poller.RunAsync());
|
||||
}
|
||||
|
||||
private void ProcessClientCommand(NetMQSocket socket, CancellationToken ct = default)
|
||||
private async Task ProcessClientCommand(NetMQSocket socket, CancellationToken ct = default)
|
||||
{
|
||||
while (socket.TryReceiveFrameBytes(TimeSpan.Zero, out var bytes))
|
||||
{
|
||||
if (bytes?.Length == 0)
|
||||
if (bytes.Length == 0)
|
||||
continue;
|
||||
|
||||
var remoteCommand = MessagePackSerializer.Deserialize<RemoteCommand>(bytes, cancellationToken: ct);
|
||||
switch (remoteCommand.CommandType)
|
||||
{
|
||||
case CommandType.DataBytes:
|
||||
BytesReceived?.Invoke(this, remoteCommand);
|
||||
break;
|
||||
case CommandType.InferenceData:
|
||||
InferenceDataReceived?.Invoke(this, remoteCommand);
|
||||
await _mediator.Publish(new InferenceDataEvent(remoteCommand), ct);
|
||||
break;
|
||||
case CommandType.AIAvailabilityResult:
|
||||
AIAvailabilityReceived?.Invoke(this, remoteCommand);
|
||||
var aiAvailabilityStatus = MessagePackSerializer.Deserialize<AIAvailabilityStatusEvent>(remoteCommand.Data, cancellationToken: ct);
|
||||
await _mediator.Publish(aiAvailabilityStatus, ct);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using Azaion.Common.DTO;
|
||||
using Azaion.Common.DTO.Config;
|
||||
using Azaion.Common.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Azaion.Common.Services.Inference;
|
||||
|
||||
public interface IInferenceService
|
||||
{
|
||||
Task RunInference(List<string> mediaPaths, CancellationToken ct = default);
|
||||
CancellationTokenSource InferenceCancelTokenSource { get; set; }
|
||||
void StopInference();
|
||||
}
|
||||
|
||||
// SHOULD BE ONLY ONE INSTANCE OF InferenceService. Do not add ANY NotificationHandler to it!
|
||||
// _inferenceCancelTokenSource should be created only once.
|
||||
public class InferenceService : IInferenceService
|
||||
{
|
||||
private readonly IInferenceClient _client;
|
||||
private readonly IAzaionApi _azaionApi;
|
||||
private readonly IOptions<AIRecognitionConfig> _aiConfigOptions;
|
||||
public CancellationTokenSource InferenceCancelTokenSource { get; set; } = new();
|
||||
public CancellationTokenSource CheckAIAvailabilityTokenSource { get; set; } = new();
|
||||
|
||||
public InferenceService(IInferenceClient client, IAzaionApi azaionApi, IOptions<AIRecognitionConfig> aiConfigOptions)
|
||||
{
|
||||
_client = client;
|
||||
_azaionApi = azaionApi;
|
||||
_aiConfigOptions = aiConfigOptions;
|
||||
}
|
||||
|
||||
public async Task CheckAIAvailabilityStatus()
|
||||
{
|
||||
CheckAIAvailabilityTokenSource = new CancellationTokenSource();
|
||||
while (!CheckAIAvailabilityTokenSource.IsCancellationRequested)
|
||||
{
|
||||
_client.Send(RemoteCommand.Create(CommandType.AIAvailabilityCheck));
|
||||
await Task.Delay(10000, CheckAIAvailabilityTokenSource.Token);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RunInference(List<string> mediaPaths, CancellationToken ct = default)
|
||||
{
|
||||
InferenceCancelTokenSource = new CancellationTokenSource();
|
||||
_client.Send(RemoteCommand.Create(CommandType.Login, _azaionApi.Credentials));
|
||||
|
||||
var aiConfig = _aiConfigOptions.Value;
|
||||
aiConfig.Paths = mediaPaths;
|
||||
_client.Send(RemoteCommand.Create(CommandType.Inference, aiConfig));
|
||||
|
||||
using var combinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(ct, InferenceCancelTokenSource.Token);
|
||||
await combinedTokenSource.Token.AsTask();
|
||||
}
|
||||
|
||||
public void StopInference() => _client.Stop();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Azaion.Common.Database;
|
||||
using Azaion.Common.DTO;
|
||||
using Azaion.Common.Events;
|
||||
using MediatR;
|
||||
using MessagePack;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Azaion.Common.Services.Inference;
|
||||
|
||||
public class InferenceServiceEventHandler(IInferenceService inferenceService,
|
||||
IAnnotationService annotationService,
|
||||
IMediator mediator,
|
||||
ILogger<InferenceServiceEventHandler> logger) :
|
||||
INotificationHandler<InferenceDataEvent>,
|
||||
INotificationHandler<AIAvailabilityStatusEvent>
|
||||
{
|
||||
public async Task Handle(InferenceDataEvent e, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.Command.Message == "DONE")
|
||||
{
|
||||
await inferenceService.InferenceCancelTokenSource.CancelAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
var annImage = MessagePackSerializer.Deserialize<AnnotationImage>(e.Command.Data, cancellationToken: ct);
|
||||
var annotation = await annotationService.SaveAnnotation(annImage, ct);
|
||||
await mediator.Publish(new AnnotationAddedEvent(annotation), ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Handle(AIAvailabilityStatusEvent e, CancellationToken ct)
|
||||
{
|
||||
|
||||
e.Status = AIAvailabilityEnum.Enabled;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Azaion.Common.DTO;
|
||||
using MediatR;
|
||||
|
||||
namespace Azaion.Common.Services.Inference;
|
||||
|
||||
public class InferenceDataEvent(RemoteCommand command) : INotification
|
||||
{
|
||||
public RemoteCommand Command { get; set; } = command;
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
using Azaion.Common.Database;
|
||||
using Azaion.Common.DTO;
|
||||
using Azaion.Common.DTO.Config;
|
||||
using Azaion.Common.Events;
|
||||
using Azaion.Common.Extensions;
|
||||
using MediatR;
|
||||
using MessagePack;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Azaion.Common.Services;
|
||||
|
||||
public interface IInferenceService
|
||||
{
|
||||
Task RunInference(List<string> mediaPaths, CancellationToken ct = default);
|
||||
void StopInference();
|
||||
}
|
||||
|
||||
public class InferenceService : IInferenceService
|
||||
{
|
||||
private readonly IInferenceClient _client;
|
||||
private readonly IAzaionApi _azaionApi;
|
||||
private readonly IOptions<AIRecognitionConfig> _aiConfigOptions;
|
||||
private readonly IAnnotationService _annotationService;
|
||||
private readonly IMediator _mediator;
|
||||
private CancellationTokenSource _inferenceCancelTokenSource = new();
|
||||
|
||||
public InferenceService(
|
||||
ILogger<InferenceService> logger,
|
||||
IInferenceClient client,
|
||||
IAzaionApi azaionApi,
|
||||
IOptions<AIRecognitionConfig> aiConfigOptions,
|
||||
IAnnotationService annotationService,
|
||||
IMediator mediator)
|
||||
{
|
||||
_client = client;
|
||||
_azaionApi = azaionApi;
|
||||
_aiConfigOptions = aiConfigOptions;
|
||||
_annotationService = annotationService;
|
||||
_mediator = mediator;
|
||||
|
||||
client.InferenceDataReceived += async (sender, command) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (command.Message == "DONE")
|
||||
{
|
||||
_inferenceCancelTokenSource?.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
var annImage = MessagePackSerializer.Deserialize<AnnotationImage>(command.Data);
|
||||
await ProcessDetection(annImage);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, e.Message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private async Task ProcessDetection(AnnotationImage annotationImage, CancellationToken ct = default)
|
||||
{
|
||||
var annotation = await _annotationService.SaveAnnotation(annotationImage, ct);
|
||||
await _mediator.Publish(new AnnotationAddedEvent(annotation), ct);
|
||||
}
|
||||
|
||||
public async Task RunInference(List<string> mediaPaths, CancellationToken ct = default)
|
||||
{
|
||||
_inferenceCancelTokenSource = new CancellationTokenSource();
|
||||
_client.Send(RemoteCommand.Create(CommandType.Login, _azaionApi.Credentials));
|
||||
|
||||
var aiConfig = _aiConfigOptions.Value;
|
||||
aiConfig.Paths = mediaPaths;
|
||||
_client.Send(RemoteCommand.Create(CommandType.Inference, aiConfig));
|
||||
|
||||
using var combinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(ct, _inferenceCancelTokenSource.Token);
|
||||
await combinedTokenSource.Token.AsTask();
|
||||
}
|
||||
|
||||
public void StopInference() => _client.Stop();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
cdef enum AIAvailabilityEnum:
|
||||
NONE = 0
|
||||
DOWNLOADING = 10
|
||||
CONVERTING = 20
|
||||
UPLOADING = 30
|
||||
ENABLED = 200
|
||||
ERROR = 500
|
||||
|
||||
cdef class AIAvailabilityStatus:
|
||||
cdef AIAvailabilityEnum status
|
||||
cdef str error_message
|
||||
|
||||
cdef bytes serialize(self)
|
||||
cdef set_status(self, AIAvailabilityEnum status, str error_message=*)
|
||||
@@ -0,0 +1,36 @@
|
||||
cimport constants_inf
|
||||
import msgpack
|
||||
|
||||
AIStatus2Text = {
|
||||
AIAvailabilityEnum.NONE: "None",
|
||||
AIAvailabilityEnum.DOWNLOADING: "Downloading",
|
||||
AIAvailabilityEnum.CONVERTING: "Converting",
|
||||
AIAvailabilityEnum.UPLOADING: "Uploading",
|
||||
AIAvailabilityEnum.ENABLED: "Enabled",
|
||||
AIAvailabilityEnum.ERROR: "Error",
|
||||
}
|
||||
|
||||
cdef class AIAvailabilityStatus:
|
||||
def __init__(self):
|
||||
self.status = AIAvailabilityEnum.NONE
|
||||
self.error_message = None
|
||||
|
||||
def __str__(self):
|
||||
status_text = AIStatus2Text.get(self.status, "Unknown")
|
||||
error_text = self.error_message if self.error_message else ""
|
||||
return f"{status_text} {error_text}"
|
||||
|
||||
cdef bytes serialize(self):
|
||||
return msgpack.packb({
|
||||
"s": self.status,
|
||||
"m": self.error_message
|
||||
})
|
||||
|
||||
cdef set_status(self, AIAvailabilityEnum status, str error_message=None):
|
||||
self.status = status
|
||||
self.error_message = error_message
|
||||
if error_message is not None:
|
||||
constants_inf.logerror(<str>error_message)
|
||||
else:
|
||||
constants_inf.log(<str>str(self))
|
||||
|
||||
@@ -35,6 +35,7 @@ venv\Scripts\pyinstaller --name=azaion-inference ^
|
||||
--collect-all jwt ^
|
||||
--collect-all loguru ^
|
||||
--hidden-import constants_inf ^
|
||||
--hidden-import ai_availability_status ^
|
||||
--hidden-import file_data ^
|
||||
--hidden-import remote_command_inf ^
|
||||
--hidden-import remote_command_handler_inf ^
|
||||
@@ -49,8 +50,9 @@ start.py
|
||||
|
||||
robocopy "dist\azaion-inference\_internal" "..\dist-azaion\_internal" "ai_config.cp312-win_amd64.pyd" "annotation.cp312-win_amd64.pyd"
|
||||
robocopy "dist\azaion-inference\_internal" "..\dist-azaion\_internal" "constants_inf.cp312-win_amd64.pyd" "file_data.cp312-win_amd64.pyd"
|
||||
robocopy "dist\azaion-inference\_internal" "..\dist-azaion\_internal" "ai_availability_status.pyd"
|
||||
robocopy "dist\azaion-inference\_internal" "..\dist-azaion\_internal" "remote_command_inf.cp312-win_amd64.pyd" "remote_command_handler_inf.cp312-win_amd64.pyd"
|
||||
robocopy "dist\azaion-inference\_internal" "..\dist-azaion\_internal" "inference.cp312-win_amd64.pyd" "inference_engine.cp312-win_amd64.pyd"
|
||||
robocopy "dist\azaion-inference\_internal" "..\dist-azaion\_internal" "inference.cp312-win_amd64.py=d" "inference_engine.cp312-win_amd64.pyd"
|
||||
robocopy "dist\azaion-inference\_internal" "..\dist-azaion\_internal" "loader_client.cp312-win_amd64.pyd" "tensorrt_engine.cp312-win_amd64.pyd"
|
||||
robocopy "dist\azaion-inference\_internal" "..\dist-azaion\_internal" "onnx_engine.cp312-win_amd64.pyd" "main_inference.cp312-win_amd64.pyd"
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from ai_availability_status cimport AIAvailabilityStatus
|
||||
from remote_command_inf cimport RemoteCommand
|
||||
from annotation cimport Annotation, Detection
|
||||
from ai_config cimport AIRecognitionConfig
|
||||
@@ -12,6 +13,7 @@ cdef class Inference:
|
||||
cdef dict[str, list(Detection)] _tile_detections
|
||||
cdef AIRecognitionConfig ai_config
|
||||
cdef bint stop_signal
|
||||
cdef AIAvailabilityStatus ai_availability_status
|
||||
|
||||
cdef str model_input
|
||||
cdef int model_width
|
||||
@@ -19,7 +21,7 @@ cdef class Inference:
|
||||
cdef int tile_width
|
||||
cdef int tile_height
|
||||
|
||||
cdef build_tensor_engine(self, object updater_callback)
|
||||
cdef bytes get_onnx_engine_bytes(self)
|
||||
cdef init_ai(self)
|
||||
cdef bint is_building_engine
|
||||
cdef bint is_video(self, str filepath)
|
||||
|
||||
@@ -5,6 +5,8 @@ from pathlib import Path
|
||||
import cv2
|
||||
import numpy as np
|
||||
cimport constants_inf
|
||||
|
||||
from ai_availability_status cimport AIAvailabilityEnum, AIAvailabilityStatus
|
||||
from remote_command_inf cimport RemoteCommand
|
||||
from annotation cimport Detection, Annotation
|
||||
from ai_config cimport AIRecognitionConfig
|
||||
@@ -60,67 +62,59 @@ cdef class Inference:
|
||||
self.tile_height = 0
|
||||
self.engine = None
|
||||
self.is_building_engine = False
|
||||
self.ai_availability_status = AIAvailabilityStatus()
|
||||
self.init_ai()
|
||||
|
||||
cdef build_tensor_engine(self, object updater_callback):
|
||||
if tensor_gpu_index == -1:
|
||||
return
|
||||
|
||||
try:
|
||||
engine_filename = TensorRTEngine.get_engine_filename(0)
|
||||
models_dir = constants_inf.MODELS_FOLDER
|
||||
|
||||
self.is_building_engine = True
|
||||
updater_callback('downloading')
|
||||
|
||||
res = self.loader_client.load_big_small_resource(engine_filename, models_dir)
|
||||
if res.err is None:
|
||||
constants_inf.log('tensor rt engine is here, no need to build')
|
||||
self.is_building_engine = False
|
||||
updater_callback('enabled')
|
||||
return
|
||||
|
||||
constants_inf.logerror(res.err)
|
||||
# time.sleep(8) # prevent simultaneously loading dll and models
|
||||
updater_callback('converting')
|
||||
constants_inf.log('try to load onnx')
|
||||
res = self.loader_client.load_big_small_resource(constants_inf.AI_ONNX_MODEL_FILE, models_dir)
|
||||
if res.err is not None:
|
||||
updater_callback(f'Error. {res.err}')
|
||||
model_bytes = TensorRTEngine.convert_from_onnx(res.data)
|
||||
updater_callback('uploading')
|
||||
res = self.loader_client.upload_big_small_resource(model_bytes, <str> engine_filename, models_dir)
|
||||
if res.err is not None:
|
||||
updater_callback(f'Error. {res.err}')
|
||||
constants_inf.log(f'uploaded {engine_filename} to CDN and API')
|
||||
self.is_building_engine = False
|
||||
updater_callback('enabled')
|
||||
except Exception as e:
|
||||
updater_callback(f'Error. {str(e)}')
|
||||
cdef bytes get_onnx_engine_bytes(self):
|
||||
models_dir = constants_inf.MODELS_FOLDER
|
||||
self.ai_availability_status.set_status(AIAvailabilityEnum.DOWNLOADING)
|
||||
res = self.loader_client.load_big_small_resource(constants_inf.AI_ONNX_MODEL_FILE, models_dir)
|
||||
if res.err is not None:
|
||||
raise Exception(res.err)
|
||||
return res.data
|
||||
|
||||
cdef init_ai(self):
|
||||
if self.engine is not None:
|
||||
return
|
||||
|
||||
models_dir = constants_inf.MODELS_FOLDER
|
||||
if tensor_gpu_index > -1:
|
||||
constants_inf.log(<str> 'init AI...')
|
||||
try:
|
||||
while self.is_building_engine:
|
||||
time.sleep(1)
|
||||
engine_filename = TensorRTEngine.get_engine_filename(0)
|
||||
if self.engine is not None:
|
||||
return
|
||||
|
||||
self.is_building_engine = True
|
||||
models_dir = constants_inf.MODELS_FOLDER
|
||||
if tensor_gpu_index > -1:
|
||||
try:
|
||||
engine_filename = TensorRTEngine.get_engine_filename(0)
|
||||
self.ai_availability_status.set_status(AIAvailabilityEnum.DOWNLOADING)
|
||||
res = self.loader_client.load_big_small_resource(engine_filename, models_dir)
|
||||
if res.err is not None:
|
||||
raise Exception(res.err)
|
||||
self.engine = TensorRTEngine(res.data)
|
||||
self.ai_availability_status.set_status(AIAvailabilityEnum.ENABLED)
|
||||
except Exception as e:
|
||||
self.ai_availability_status.set_status(AIAvailabilityEnum.ERROR, <str>str(e))
|
||||
onnx_engine_bytes = self.get_onnx_engine_bytes()
|
||||
self.ai_availability_status.set_status(AIAvailabilityEnum.CONVERTING)
|
||||
model_bytes = TensorRTEngine.convert_from_onnx(res.data)
|
||||
self.ai_availability_status.set_status(AIAvailabilityEnum.UPLOADING)
|
||||
res = self.loader_client.upload_big_small_resource(model_bytes, <str> engine_filename, models_dir)
|
||||
if res.err is not None:
|
||||
self.ai_availability_status.set_status(AIAvailabilityEnum.ERROR, res.err)
|
||||
self.ai_availability_status.set_status(AIAvailabilityEnum.ENABLED)
|
||||
else:
|
||||
self.engine = OnnxEngine(<bytes>self.get_onnx_engine_bytes())
|
||||
self.is_building_engine = False
|
||||
|
||||
self.model_height, self.model_width = self.engine.get_input_shape()
|
||||
#todo: temporarily, send it from the client
|
||||
self.tile_width = 550
|
||||
self.tile_height = 550
|
||||
except Exception as e:
|
||||
self.ai_availability_status.set_status(AIAvailabilityEnum.ERROR, <str>str(e))
|
||||
self.is_building_engine = False
|
||||
|
||||
res = self.loader_client.load_big_small_resource(engine_filename, models_dir)
|
||||
if res.err is not None:
|
||||
raise Exception(res.err)
|
||||
self.engine = TensorRTEngine(res.data)
|
||||
else:
|
||||
res = self.loader_client.load_big_small_resource(constants_inf.AI_ONNX_MODEL_FILE, models_dir)
|
||||
if res.err is not None:
|
||||
raise Exception(res.err)
|
||||
self.engine = OnnxEngine(res.data)
|
||||
|
||||
self.model_height, self.model_width = self.engine.get_input_shape()
|
||||
#todo: temporarily, send it from the client
|
||||
self.tile_width = 550
|
||||
self.tile_height = 550
|
||||
|
||||
cdef preprocess(self, frames):
|
||||
blobs = [cv2.dnn.blobFromImage(frame,
|
||||
|
||||
@@ -44,8 +44,8 @@ cdef class CommandProcessor:
|
||||
if command.command_type == CommandType.INFERENCE:
|
||||
self.inference_queue.put(command)
|
||||
elif command.command_type == CommandType.AI_AVAILABILITY_CHECK:
|
||||
self.inference.build_tensor_engine(lambda status: self.remote_handler.send(command.client_id,
|
||||
RemoteCommand(CommandType.AI_AVAILABILITY_RESULT, None, status).serialize()))
|
||||
status = self.inference.ai_availability_status.serialize()
|
||||
self.remote_handler.send(command.client_id, RemoteCommand(CommandType.AI_AVAILABILITY_RESULT, status).serialize())
|
||||
elif command.command_type == CommandType.STOP_INFERENCE:
|
||||
self.inference.stop()
|
||||
elif command.command_type == CommandType.EXIT:
|
||||
|
||||
@@ -14,6 +14,7 @@ trace_line = False
|
||||
|
||||
extensions = [
|
||||
Extension('constants_inf', ['constants_inf.pyx'], **debug_args),
|
||||
Extension('ai_availability_status', ['ai_availability_status.pyx'], **debug_args),
|
||||
Extension('file_data', ['file_data.pyx'], **debug_args),
|
||||
Extension('remote_command_inf', ['remote_command_inf.pyx'], **debug_args),
|
||||
Extension('remote_command_handler_inf', ['remote_command_handler_inf.pyx'], **debug_args),
|
||||
|
||||
@@ -11,6 +11,7 @@ using Azaion.Common.DTO.Config;
|
||||
using Azaion.Common.Events;
|
||||
using Azaion.Common.Extensions;
|
||||
using Azaion.Common.Services;
|
||||
using Azaion.Common.Services.Inference;
|
||||
using Azaion.Dataset;
|
||||
using CommandLine;
|
||||
using LibVLCSharp.Shared;
|
||||
|
||||
@@ -6,8 +6,8 @@ using System.Windows.Media;
|
||||
using Azaion.Common.Database;
|
||||
using Azaion.Common.DTO;
|
||||
using Azaion.Common.DTO.Config;
|
||||
using Azaion.Common.Extensions;
|
||||
using Azaion.Common.Services;
|
||||
using Azaion.Common.Services.Inference;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SharpVectors.Converters;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"LoaderClientConfig": {
|
||||
"ZeroMqHost": "127.0.0.1",
|
||||
"ZeroMqPort": 5024,
|
||||
"ZeroMqPort": 5025,
|
||||
"ApiUrl": "https://api.azaion.com"
|
||||
},
|
||||
"InferenceClientConfig": {
|
||||
"ZeroMqHost": "127.0.0.1",
|
||||
"ZeroMqPort": 5126,
|
||||
"ZeroMqPort": 5127,
|
||||
"ApiUrl": "https://api.azaion.com"
|
||||
},
|
||||
"GpsDeniedClientConfig": {
|
||||
|
||||
Reference in New Issue
Block a user