fix bug with annotation result gradient stops

add tensorrt engine
This commit is contained in:
Alex Bezdieniezhnykh
2025-04-02 00:29:21 +03:00
parent e0c88bd8fb
commit b21f8e320f
36 changed files with 544 additions and 206 deletions
+5 -16
View File
@@ -8,6 +8,7 @@
xmlns:controls1="clr-namespace:Azaion.Common.Controls;assembly=Azaion.Common" xmlns:controls1="clr-namespace:Azaion.Common.Controls;assembly=Azaion.Common"
xmlns:controls2="clr-namespace:Azaion.Annotator.Controls;assembly=Azaion.Common" xmlns:controls2="clr-namespace:Azaion.Annotator.Controls;assembly=Azaion.Common"
mc:Ignorable="d" mc:Ignorable="d"
xmlns:local="clr-namespace:Azaion.Annotator"
Title="Azaion Annotator" Height="800" Width="1100" Title="Azaion Annotator" Height="800" Width="1100"
WindowState="Maximized" WindowState="Maximized"
> >
@@ -49,6 +50,8 @@
</Trigger> </Trigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
<local:GradientStyleSelector x:Key="GradientStyleSelector"/>
</Window.Resources> </Window.Resources>
<Grid Name="GlobalGrid" <Grid Name="GlobalGrid"
@@ -232,7 +235,6 @@
Grid.Row="1" Grid.Row="1"
Grid.RowSpan="4" Grid.RowSpan="4"
Background="Black" Background="Black"
RowBackground="#252525"
Foreground="White" Foreground="White"
RowHeaderWidth="0" RowHeaderWidth="0"
Padding="2 0 0 0" Padding="2 0 0 0"
@@ -241,7 +243,8 @@
CellStyle="{DynamicResource DataGridCellStyle1}" CellStyle="{DynamicResource DataGridCellStyle1}"
IsReadOnly="True" IsReadOnly="True"
CanUserResizeRows="False" CanUserResizeRows="False"
CanUserResizeColumns="False"> CanUserResizeColumns="False"
RowStyleSelector="{StaticResource GradientStyleSelector}">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn <DataGridTextColumn
Width="60" Width="60"
@@ -264,20 +267,6 @@
<Setter Property="Background" Value="#252525"></Setter> <Setter Property="Background" Value="#252525"></Setter>
</Style> </Style>
</DataGridTextColumn.HeaderStyle> </DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0 0 " EndPoint="1 0">
<GradientStop Offset="0.3" Color="{Binding Path=ClassColor0}" />
<GradientStop Offset="0.5" Color="{Binding Path=ClassColor1}" />
<GradientStop Offset="0.8" Color="{Binding Path=ClassColor2}" />
<GradientStop Offset="0.99" Color="{Binding Path=ClassColor3}" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
+47 -2
View File
@@ -328,7 +328,11 @@ 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}!");
_formState.AnnotationResults.Remove(existingResult); _formState.AnnotationResults.Remove(existingResult);
}
var dict = _formState.AnnotationResults var dict = _formState.AnnotationResults
.Select((x, i) => new { x.Annotation.Time, Index = i }) .Select((x, i) => new { x.Annotation.Time, Index = i })
@@ -339,7 +343,8 @@ public partial class Annotator
.Select(x => x.Value + 1) .Select(x => x.Value + 1)
.FirstOrDefault(); .FirstOrDefault();
_formState.AnnotationResults.Insert(index, new AnnotationResult(_appConfig.AnnotationConfig.DetectionClassesDict, annotation)); var annRes = new AnnotationResult(_appConfig.AnnotationConfig.DetectionClassesDict, annotation);
_formState.AnnotationResults.Insert(index, annRes);
} }
private async Task ReloadFiles() private async Task ReloadFiles()
@@ -565,6 +570,7 @@ public partial class Annotator
await _mediator.Publish(new AnnotatorControlEvent(PlaybackControlEnum.Play), ct); await _mediator.Publish(new AnnotatorControlEvent(PlaybackControlEnum.Play), ct);
} }
} }
AddAnnotation(annotation); AddAnnotation(annotation);
if (FollowAI) if (FollowAI)
@@ -574,7 +580,7 @@ public partial class Annotator
$"{_appConfig.AnnotationConfig.DetectionClassesDict[det.ClassNumber].Name}: " + $"{_appConfig.AnnotationConfig.DetectionClassesDict[det.ClassNumber].Name}: " +
$"xy=({det.CenterX:F2},{det.CenterY:F2}), " + $"xy=({det.CenterX:F2},{det.CenterY:F2}), " +
$"size=({det.Width:F2}, {det.Height:F2}), " + $"size=({det.Width:F2}, {det.Height:F2}), " +
$"prob: {det.Probability*100:F0}%")); $"conf: {det.Confidence*100:F0}%"));
Dispatcher.Invoke(() => Dispatcher.Invoke(() =>
{ {
@@ -618,3 +624,42 @@ public partial class Annotator
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
public class GradientStyleSelector : StyleSelector
{
public override Style? SelectStyle(object item, DependencyObject container)
{
if (container is not DataGridRow row || row.DataContext is not AnnotationResult result)
return null;
var style = new Style(typeof(DataGridRow));
var brush = new LinearGradientBrush
{
StartPoint = new Point(0, 0),
EndPoint = new Point(1, 0)
};
var gradients = new List<GradientStop>();
if (result.Colors.Count != 0)
{
var color = (Color)ColorConverter.ConvertFromString("#40DDDDDD");
gradients = [new GradientStop(color, 0.99)];
}
else
{
var increment = 1.0 / result.Colors.Count;
var currentStop = increment;
foreach (var c in result.Colors)
{
var resultColor = c.Color.ToConfidenceColor(c.Confidence);
brush.GradientStops.Add(new GradientStop(resultColor, currentStop));
currentStop += increment;
}
}
foreach (var gradientStop in gradients)
brush.GradientStops.Add(gradientStop);
style.Setters.Add(new Setter(DataGridRow.BackgroundProperty, brush));
return style;
}
}
+19 -11
View File
@@ -38,16 +38,19 @@ public partial class MapMatcher : UserControl
SatelliteMap.Position = new PointLatLng(48.295985271707664, 37.14477539062501); SatelliteMap.Position = new PointLatLng(48.295985271707664, 37.14477539062501);
SatelliteMap.MultiTouchEnabled = true; SatelliteMap.MultiTouchEnabled = true;
GpsFiles.MouseDoubleClick += async (sender, args) => GpsFiles.MouseDoubleClick += async (sender, args) => { await OpenGpsLocation(GpsFiles.SelectedIndex); };
}
private async Task OpenGpsLocation(int gpsFilesIndex)
{
var media = GpsFiles.Items[gpsFilesIndex] as MediaFileInfo;
var ann = _annotations.GetValueOrDefault(Path.GetFileNameWithoutExtension(media.Name));
GpsImageEditor.Background = new ImageBrush
{ {
var media = (GpsFiles.SelectedItem as MediaFileInfo)!; ImageSource = await Path.Combine(_currentDir, ann.Name).OpenImage()
var ann = _annotations.GetValueOrDefault(Path.GetFileNameWithoutExtension(media.Name));
GpsImageEditor.Background = new ImageBrush
{
ImageSource = await Path.Combine(_currentDir, ann.Name).OpenImage()
};
SatelliteMap.Position = new PointLatLng(ann.Lat, ann.Lon);
}; };
if (ann.Lat != 0 && ann.Lon != 0)
SatelliteMap.Position = new PointLatLng(ann.Lat, ann.Lon);
} }
private void GpsFilesContextOpening(object sender, ContextMenuEventArgs e) private void GpsFilesContextOpening(object sender, ContextMenuEventArgs e)
@@ -104,8 +107,11 @@ public partial class MapMatcher : UserControl
_allMediaFiles = mediaFiles; _allMediaFiles = mediaFiles;
GpsFiles.ItemsSource = new ObservableCollection<MediaFileInfo>(_allMediaFiles); GpsFiles.ItemsSource = new ObservableCollection<MediaFileInfo>(_allMediaFiles);
var annotations = SetFromCsv(mediaFiles); var annotations = SetFromCsv(mediaFiles);
Cursor = Cursors.Wait;
await Task.Delay(TimeSpan.FromSeconds(10)); await Task.Delay(TimeSpan.FromSeconds(10));
SetMarkers(annotations); SetMarkers(annotations);
Cursor = Cursors.Arrow;
await OpenGpsLocation(0);
} }
private Dictionary<string, Annotation> SetFromCsv(List<MediaFileInfo> mediaFiles) private Dictionary<string, Annotation> SetFromCsv(List<MediaFileInfo> mediaFiles)
@@ -117,7 +123,9 @@ public partial class MapMatcher : UserControl
}).ToDictionary(x => Path.GetFileNameWithoutExtension(x.OriginalMediaName)); }).ToDictionary(x => Path.GetFileNameWithoutExtension(x.OriginalMediaName));
var csvResults = GpsCsvResult.ReadFromCsv(Constants.CSV_PATH); var csvResults = GpsCsvResult.ReadFromCsv(Constants.CSV_PATH);
var csvDict = csvResults.ToDictionary(x => x.Image); var csvDict = csvResults
.Where(x => x.MatchType == "stitched")
.ToDictionary(x => x.Image);
foreach (var ann in _annotations) foreach (var ann in _annotations)
{ {
var csvRes = csvDict.GetValueOrDefault(ann.Key); var csvRes = csvDict.GetValueOrDefault(ann.Key);
@@ -137,7 +145,7 @@ public partial class MapMatcher : UserControl
var firstAnnotation = annotations.FirstOrDefault(); var firstAnnotation = annotations.FirstOrDefault();
SatelliteMap.Position = new PointLatLng(firstAnnotation.Value.Lat, firstAnnotation.Value.Lon); SatelliteMap.Position = new PointLatLng(firstAnnotation.Value.Lat, firstAnnotation.Value.Lon);
foreach (var ann in annotations) foreach (var ann in annotations.Where(x => x.Value.Lat != 0 && x.Value.Lon != 0))
{ {
var marker = new GMapMarker(new PointLatLng(ann.Value.Lat, ann.Value.Lon)); var marker = new GMapMarker(new PointLatLng(ann.Value.Lat, ann.Value.Lon));
var circle = new CircleVisual(marker, System.Windows.Media.Brushes.Blue) var circle = new CircleVisual(marker, System.Windows.Media.Brushes.Blue)
@@ -147,6 +155,6 @@ public partial class MapMatcher : UserControl
marker.Shape = circle; marker.Shape = circle;
SatelliteMap.Markers.Add(marker); SatelliteMap.Markers.Add(marker);
} }
SatelliteMap.ZoomAndCenterMarkers(null);
} }
} }
+1 -1
View File
@@ -90,5 +90,5 @@ public class Constants
#endregion #endregion
public const string CSV_PATH = "D:\\matches.csv"; public const string CSV_PATH = "matches.csv";
} }
+4 -4
View File
@@ -54,7 +54,7 @@ public class CanvasEditor : Canvas
_verticalLine.Fill = value.ColorBrush; _verticalLine.Fill = value.ColorBrush;
_horizontalLine.Stroke = value.ColorBrush; _horizontalLine.Stroke = value.ColorBrush;
_horizontalLine.Fill = value.ColorBrush; _horizontalLine.Fill = value.ColorBrush;
_classNameHint.Text = value.Name; _classNameHint.Text = value.ShortName;
_newAnnotationRect.Stroke = value.ColorBrush; _newAnnotationRect.Stroke = value.ColorBrush;
_newAnnotationRect.Fill = value.ColorBrush; _newAnnotationRect.Fill = value.ColorBrush;
@@ -84,7 +84,7 @@ public class CanvasEditor : Canvas
}; };
_classNameHint = new TextBlock _classNameHint = new TextBlock
{ {
Text = CurrentAnnClass?.Name ?? "asd", Text = CurrentAnnClass?.ShortName ?? "",
Foreground = new SolidColorBrush(Colors.Black), Foreground = new SolidColorBrush(Colors.Black),
Cursor = Cursors.Arrow, Cursor = Cursors.Arrow,
FontSize = 16, FontSize = 16,
@@ -313,14 +313,14 @@ public class CanvasEditor : Canvas
foreach (var detection in detections) foreach (var detection in detections)
{ {
var annClass = DetectionClass.FromYoloId(detection.ClassNumber, detectionClasses); var annClass = DetectionClass.FromYoloId(detection.ClassNumber, detectionClasses);
var canvasLabel = new CanvasLabel(detection, RenderSize, videoSize, detection.Probability); var canvasLabel = new CanvasLabel(detection, RenderSize, videoSize, detection.Confidence);
CreateDetectionControl(annClass, time, canvasLabel); CreateDetectionControl(annClass, time, canvasLabel);
} }
} }
private void CreateDetectionControl(DetectionClass annClass, TimeSpan time, CanvasLabel canvasLabel) private void CreateDetectionControl(DetectionClass annClass, TimeSpan time, CanvasLabel canvasLabel)
{ {
var detectionControl = new DetectionControl(annClass, time, AnnotationResizeStart, canvasLabel.Probability) var detectionControl = new DetectionControl(annClass, time, AnnotationResizeStart, canvasLabel.Confidence)
{ {
Width = canvasLabel.Width, Width = canvasLabel.Width,
Height = canvasLabel.Height Height = canvasLabel.Height
+1 -1
View File
@@ -68,7 +68,7 @@
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
<DataGridTextColumn Width="*" Header="Назва" Binding="{Binding Path=Name}" CanUserSort="False"> <DataGridTextColumn Width="*" Header="Назва" Binding="{Binding Path=ShortName}" CanUserSort="False">
<DataGridTextColumn.HeaderStyle> <DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader"> <Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#252525"/> <Setter Property="Background" Value="#252525"/>
+7 -26
View File
@@ -1,52 +1,33 @@
using System.Windows.Media; using System.Windows.Media;
using Azaion.Common.Database; using Azaion.Common.Database;
using Azaion.Common.Extensions;
using Newtonsoft.Json;
namespace Azaion.Common.DTO; namespace Azaion.Common.DTO;
public class AnnotationResult public class AnnotationResult
{ {
public Annotation Annotation { get; set; } public Annotation Annotation { get; set; }
public List<(Color Color, double Confidence)> Colors { get; private set; }
public string ImagePath { get; set; } public string ImagePath { get; set; }
public string TimeStr { get; set; } public string TimeStr { get; set; }
public string ClassName { get; set; } public string ClassName { get; set; }
public Color ClassColor0 { get; set; }
public Color ClassColor1 { get; set; }
public Color ClassColor2 { get; set; }
public Color ClassColor3 { get; set; }
public AnnotationResult(Dictionary<int, DetectionClass> allDetectionClasses, Annotation annotation) public AnnotationResult(Dictionary<int, DetectionClass> allDetectionClasses, Annotation annotation)
{ {
Annotation = annotation; Annotation = annotation;
var detections = annotation.Detections.ToList();
Color GetAnnotationClass(List<int> detectionClasses, int colorNumber)
{
if (detections.Count == 0)
return (-1).ToColor();
return colorNumber >= detectionClasses.Count
? allDetectionClasses[detectionClasses.LastOrDefault()].Color
: allDetectionClasses[detectionClasses[colorNumber]].Color;
}
TimeStr = $"{annotation.Time:h\\:mm\\:ss}"; TimeStr = $"{annotation.Time:h\\:mm\\:ss}";
ImagePath = annotation.ImagePath; ImagePath = annotation.ImagePath;
var detectionClasses = detections.Select(x => x.ClassNumber).Distinct().ToList(); var detectionClasses = annotation.Detections.Select(x => x.ClassNumber).Distinct().ToList();
Colors = annotation.Detections
.Select(d => (allDetectionClasses[d.ClassNumber].Color, d.Confidence))
.ToList();
ClassName = detectionClasses.Count > 1 ClassName = detectionClasses.Count > 1
? string.Join(", ", detectionClasses.Select(x => allDetectionClasses[x].UIName)) ? string.Join(", ", detectionClasses.Select(x => allDetectionClasses[x].UIName))
: allDetectionClasses[detectionClasses.FirstOrDefault()].UIName; : allDetectionClasses[detectionClasses.FirstOrDefault()].UIName;
ClassColor0 = GetAnnotationClass(detectionClasses, 0);
ClassColor1 = GetAnnotationClass(detectionClasses, 1);
ClassColor2 = GetAnnotationClass(detectionClasses, 2);
ClassColor3 = GetAnnotationClass(detectionClasses, 3);
} }
} }
@@ -23,6 +23,7 @@ public class AnnotationConfig
{ {
Id = cls.Id, Id = cls.Id,
Name = cls.Name, Name = cls.Name,
Color = cls.Color,
ShortName = cls.ShortName, ShortName = cls.ShortName,
PhotoMode = mode PhotoMode = mode
})) }))
+2 -4
View File
@@ -1,5 +1,4 @@
using System.Windows.Media; using System.Windows.Media;
using Azaion.Common.Extensions;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Azaion.Common.DTO; namespace Azaion.Common.DTO;
@@ -11,6 +10,8 @@ public class DetectionClass
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
public string ShortName { get; set; } = null!; public string ShortName { get; set; } = null!;
public Color Color { get; set; }
[JsonIgnore] [JsonIgnore]
public string UIName public string UIName
{ {
@@ -31,9 +32,6 @@ public class DetectionClass
[JsonIgnore] [JsonIgnore]
public PhotoMode PhotoMode { get; set; } public PhotoMode PhotoMode { get; set; }
[JsonIgnore]
public Color Color => Id.ToColor();
[JsonIgnore] //For UI [JsonIgnore] //For UI
public int ClassNumber => Id + 1; public int ClassNumber => Id + 1;
+8 -8
View File
@@ -26,22 +26,22 @@ 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? Probability { get; } public double? Confidence { get; }
public CanvasLabel() public CanvasLabel()
{ {
} }
public CanvasLabel(int classNumber, double x, double y, double width, double height, double? probability = null) : base(classNumber) public CanvasLabel(int classNumber, double x, double y, double width, double height, double? confidence = null) : base(classNumber)
{ {
X = x; X = x;
Y = y; Y = y;
Width = width; Width = width;
Height = height; Height = height;
Probability = probability; Confidence = confidence;
} }
public CanvasLabel(YoloLabel label, Size canvasSize, Size? videoSize = null, double? probability = null) public CanvasLabel(YoloLabel label, Size canvasSize, Size? videoSize = null, double confidence = 1)
{ {
var cw = canvasSize.Width; var cw = canvasSize.Width;
var ch = canvasSize.Height; var ch = canvasSize.Height;
@@ -75,7 +75,7 @@ public class CanvasLabel : Label
Width = label.Width * realWidth; Width = label.Width * realWidth;
Height = label.Height * ch; Height = label.Height * ch;
} }
Probability = probability; Confidence = confidence;
} }
} }
@@ -189,12 +189,12 @@ public class YoloLabel : Label
public class Detection : YoloLabel public class Detection : YoloLabel
{ {
[JsonProperty(PropertyName = "an")][Key("an")] public string AnnotationName { get; set; } = null!; [JsonProperty(PropertyName = "an")][Key("an")] public string AnnotationName { get; set; } = null!;
[JsonProperty(PropertyName = "p")][Key("p")] public double? Probability { get; set; } [JsonProperty(PropertyName = "p")][Key("p")] public double Confidence { get; set; }
//For db & serialization //For db & serialization
public Detection(){} public Detection(){}
public Detection(string annotationName, YoloLabel label, double? probability = null) public Detection(string annotationName, YoloLabel label, double confidence = 1)
{ {
AnnotationName = annotationName; AnnotationName = annotationName;
ClassNumber = label.ClassNumber; ClassNumber = label.ClassNumber;
@@ -202,6 +202,6 @@ public class Detection : YoloLabel
CenterY = label.CenterY; CenterY = label.CenterY;
Height = label.Height; Height = label.Height;
Width = label.Width; Width = label.Width;
Probability = probability; Confidence = confidence;
} }
} }
+5 -18
View File
@@ -4,25 +4,12 @@ namespace Azaion.Common.Extensions;
public static class ColorExtensions public static class ColorExtensions
{ {
public static Color ToColor(this int id) private const int MIN_ALPHA = 20;
private const int MAX_ALPHA = 100;
public static Color ToConfidenceColor(this Color color, double confidence )
{ {
var index = id % ColorValues.Length; color.A = (byte)(MIN_ALPHA + (int)Math.Round(confidence * (MAX_ALPHA - MIN_ALPHA)));
var hex = index == -1
? "#40DDDDDD"
: $"#40{ColorValues[index]}";
var color =(Color)ColorConverter.ConvertFromString(hex);
return color; return color;
} }
private static readonly string[] ColorValues =
[
"FF0000", "00FF00", "0000FF", "FFFF00", "FF00FF", "00FFFF", "000000",
"800000", "008000", "000080", "808000", "800080", "008080", "808080",
"C00000", "00C000", "0000C0", "C0C000", "C000C0", "00C0C0", "C0C0C0",
"400000", "004000", "000040", "404000", "400040", "004040", "404040",
"200000", "002000", "000020", "202000", "200020", "002020", "202020",
"600000", "006000", "000060", "606000", "600060", "006060", "606060",
"A00000", "00A000", "0000A0", "A0A000", "A000A0", "00A0A0", "A0A0A0",
"E00000", "00E000", "0000E0", "E0E000", "E000E0", "00E0E0", "E0E0E0"
];
} }
@@ -13,4 +13,7 @@ public class InferenceClientConfig : ExternalClientConfig
public string ResourcesFolder { get; set; } = ""; public string ResourcesFolder { get; set; } = "";
} }
public class GpsDeniedClientConfig : ExternalClientConfig; public class GpsDeniedClientConfig : ExternalClientConfig
{
public int ZeroMqReceiverPort { get; set; }
}
@@ -40,7 +40,7 @@ public abstract class BaseZeroMqExternalClient : IExternalClient
using var process = new Process(); using var process = new Process();
process.StartInfo = new ProcessStartInfo process.StartInfo = new ProcessStartInfo
{ {
FileName = ClientPath FileName = ClientPath,
//Arguments = $"-e {credentials.Email} -p {credentials.Password} -f {apiConfig.ResourcesFolder}", //Arguments = $"-e {credentials.Email} -p {credentials.Password} -f {apiConfig.ResourcesFolder}",
//RedirectStandardOutput = true, //RedirectStandardOutput = true,
//RedirectStandardError = true, //RedirectStandardError = true,
@@ -14,9 +14,11 @@ public interface IHardwareService
public class HardwareService : IHardwareService public class HardwareService : IHardwareService
{ {
private const string WIN32_GET_HARDWARE_COMMAND = private const string WIN32_GET_HARDWARE_COMMAND =
"wmic OS get TotalVisibleMemorySize /Value && " + "powershell -Command \"" +
"wmic CPU get Name /Value && " + "Get-CimInstance -ClassName Win32_Processor | Select-Object -ExpandProperty Name | Write-Output; " +
"wmic path Win32_VideoController get Name /Value"; "Get-CimInstance -ClassName Win32_VideoController | Select-Object -ExpandProperty Name | Write-Output; " +
"Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -ExpandProperty TotalVisibleMemorySize | Write-Output" +
"\"";
private const string UNIX_GET_HARDWARE_COMMAND = private const string UNIX_GET_HARDWARE_COMMAND =
"/bin/bash -c \"free -g | grep Mem: | awk '{print $2}' && " + "/bin/bash -c \"free -g | grep Mem: | awk '{print $2}' && " +
+15 -11
View File
@@ -77,11 +77,11 @@ public partial class DatasetExplorer
await DeleteAnnotations(); await DeleteAnnotations();
break; break;
case Key.Enter: case Key.Enter:
await EditAnnotation(); await EditAnnotation(ThumbnailsView.SelectedIndex);
break; break;
} }
}; };
ThumbnailsView.MouseDoubleClick += async (_, _) => await EditAnnotation(); ThumbnailsView.MouseDoubleClick += async (_, _) => await EditAnnotation(ThumbnailsView.SelectedIndex);
ThumbnailsView.SelectionChanged += (_, _) => ThumbnailsView.SelectionChanged += (_, _) =>
{ {
@@ -152,7 +152,7 @@ public partial class DatasetExplorer
.Select(gr => new .Select(gr => new
{ {
gr.Key, gr.Key,
_annotationConfig.DetectionClassesDict[gr.Key].Name, _annotationConfig.DetectionClassesDict[gr.Key].ShortName,
_annotationConfig.DetectionClassesDict[gr.Key].Color, _annotationConfig.DetectionClassesDict[gr.Key].Color,
ClassCount = gr.Value.Count ClassCount = gr.Value.Count
}) })
@@ -175,7 +175,7 @@ public partial class DatasetExplorer
foreach (var x in data) foreach (var x in data)
{ {
var label = ClassDistribution.Plot.Add.Text(x.Name, 50, -1.5 * x.Key + 1.1); var label = ClassDistribution.Plot.Add.Text(x.ShortName, 50, -1.5 * x.Key + 1.1);
label.LabelFontColor = foregroundColor; label.LabelFontColor = foregroundColor;
label.LabelFontSize = 18; label.LabelFontSize = 18;
} }
@@ -204,16 +204,17 @@ public partial class DatasetExplorer
RefreshThumbnailsButtonItem.Visibility = Visibility.Visible; RefreshThumbnailsButtonItem.Visibility = Visibility.Visible;
} }
private async Task EditAnnotation() public async Task EditAnnotation(int index)
{ {
try try
{ {
ThumbnailLoading = true; ThumbnailLoading = true;
if (index == -1)
if (ThumbnailsView.SelectedItem == null)
return; return;
CurrentAnnotation = (ThumbnailsView.SelectedItem as AnnotationThumbnail)!; CurrentAnnotation = (ThumbnailsView.Items[index] as AnnotationThumbnail)!;
ThumbnailsView.SelectedIndex = index;
var ann = CurrentAnnotation.Annotation; var ann = CurrentAnnotation.Annotation;
ExplorerEditor.Background = new ImageBrush ExplorerEditor.Background = new ImageBrush
{ {
@@ -224,7 +225,6 @@ public partial class DatasetExplorer
var time = ann.Time; var time = ann.Time;
ExplorerEditor.RemoveAllAnns(); ExplorerEditor.RemoveAllAnns();
ExplorerEditor.CreateDetections(time, ann.Detections, _annotationConfig.DetectionClasses, ExplorerEditor.RenderSize); ExplorerEditor.CreateDetections(time, ann.Detections, _annotationConfig.DetectionClasses, ExplorerEditor.RenderSize);
ThumbnailLoading = false;
} }
catch (Exception e) catch (Exception e)
{ {
@@ -233,7 +233,11 @@ public partial class DatasetExplorer
} }
finally finally
{ {
ThumbnailLoading = false; _ = Task.Run(async () =>
{
await Task.Delay(100);
ThumbnailLoading = false;
});
} }
} }
@@ -260,7 +264,7 @@ public partial class DatasetExplorer
} }
} }
private async Task DeleteAnnotations() public async Task DeleteAnnotations()
{ {
var tempSelected = ThumbnailsView.SelectedIndex; var tempSelected = ThumbnailsView.SelectedIndex;
var result = MessageBox.Show("Чи дійсно видалити аннотації?","Підтвердження видалення", MessageBoxButton.YesNo, MessageBoxImage.Question); var result = MessageBox.Show("Чи дійсно видалити аннотації?","Підтвердження видалення", MessageBoxButton.YesNo, MessageBoxImage.Question);
+19 -3
View File
@@ -1,4 +1,5 @@
using System.IO; using System.IO;
using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using Azaion.Common.DTO; using Azaion.Common.DTO;
using Azaion.Common.DTO.Queue; using Azaion.Common.DTO.Queue;
@@ -22,7 +23,9 @@ public class DatasetExplorerEventHandler(
{ Key.Delete, PlaybackControlEnum.RemoveSelectedAnns }, { Key.Delete, PlaybackControlEnum.RemoveSelectedAnns },
{ Key.X, PlaybackControlEnum.RemoveAllAnns }, { Key.X, PlaybackControlEnum.RemoveAllAnns },
{ Key.Escape, PlaybackControlEnum.Close }, { Key.Escape, PlaybackControlEnum.Close },
{ Key.V, PlaybackControlEnum.ValidateAnnotations} { Key.Down, PlaybackControlEnum.Next },
{ Key.Up, PlaybackControlEnum.Previous },
{ Key.V, PlaybackControlEnum.ValidateAnnotations},
}; };
public async Task Handle(DatasetExplorerControlEvent notification, CancellationToken cancellationToken) public async Task Handle(DatasetExplorerControlEvent notification, CancellationToken cancellationToken)
@@ -63,15 +66,28 @@ public class DatasetExplorerEventHandler(
var detections = datasetExplorer.ExplorerEditor.CurrentDetections var detections = datasetExplorer.ExplorerEditor.CurrentDetections
.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;
await annotationService.SaveAnnotation(a.OriginalMediaName, a.Time, detections, SourceEnum.Manual, token: cancellationToken); await annotationService.SaveAnnotation(a.OriginalMediaName, a.Time, detections, SourceEnum.Manual, token: cancellationToken);
datasetExplorer.SwitchTab(toEditor: false); await datasetExplorer.EditAnnotation(index + 1);
break; break;
case PlaybackControlEnum.RemoveSelectedAnns: case PlaybackControlEnum.RemoveSelectedAnns:
datasetExplorer.ExplorerEditor.RemoveSelectedAnns(); if (datasetExplorer.ExplorerEditor.CurrentDetections.Any(x => x.IsSelected))
datasetExplorer.ExplorerEditor.RemoveSelectedAnns();
else
{
await datasetExplorer.DeleteAnnotations();
await datasetExplorer.EditAnnotation(datasetExplorer.ThumbnailsView.SelectedIndex);
}
break; break;
case PlaybackControlEnum.RemoveAllAnns: case PlaybackControlEnum.RemoveAllAnns:
datasetExplorer.ExplorerEditor.RemoveAllAnns(); datasetExplorer.ExplorerEditor.RemoveAllAnns();
break; break;
case PlaybackControlEnum.Next:
await datasetExplorer.EditAnnotation(datasetExplorer.ThumbnailsView.SelectedIndex + 1);
break;
case PlaybackControlEnum.Previous:
await datasetExplorer.EditAnnotation(datasetExplorer.ThumbnailsView.SelectedIndex - 1);
break;
case PlaybackControlEnum.Close: case PlaybackControlEnum.Close:
datasetExplorer.SwitchTab(toEditor: false); datasetExplorer.SwitchTab(toEditor: false);
break; break;
+1 -1
View File
@@ -72,5 +72,5 @@ In case of fbgemm.dll error (Windows specific):
<h3>Build exe</h3> <h3>Build exe</h3>
``` ```
.\build.exe .\build.cmd
``` ```
+1 -1
View File
@@ -14,4 +14,4 @@ cdef class ApiClient:
cdef load_bytes(self, str filename, str folder=*) cdef load_bytes(self, str filename, str folder=*)
cdef upload_file(self, str filename, str folder=*) cdef upload_file(self, str filename, str folder=*)
cdef load_ai_model(self) cdef load_ai_model(self, bint is_tensor=*)
+14 -4
View File
@@ -73,6 +73,7 @@ cdef class ApiClient:
cdef load_bytes(self, str filename, str folder=None): cdef load_bytes(self, str filename, str folder=None):
folder = folder or self.credentials.folder folder = folder or self.credentials.folder
hardware_service = HardwareService() hardware_service = HardwareService()
cdef HardwareInfo hardware = hardware_service.get_hardware_info() cdef HardwareInfo hardware = hardware_service.get_hardware_info()
@@ -110,11 +111,20 @@ cdef class ApiClient:
constants.log(<str>f'Downloaded file: {filename}, {len(data)} bytes') constants.log(<str>f'Downloaded file: {filename}, {len(data)} bytes')
return data return data
cdef load_ai_model(self): cdef load_ai_model(self, bint is_tensor=False):
with open(<str>constants.AI_MODEL_FILE_BIG, 'rb') as binary_file: if is_tensor:
encrypted_bytes_big = binary_file.read() big_file = <str> constants.AI_TENSOR_MODEL_FILE_BIG
encrypted_bytes_small = self.load_bytes(constants.AI_MODEL_FILE_SMALL) small_file = <str> constants.AI_TENSOR_MODEL_FILE_SMALL
else:
big_file = <str>constants.AI_ONNX_MODEL_FILE_BIG
small_file = <str> constants.AI_ONNX_MODEL_FILE_SMALL
with open(big_file, 'rb') as binary_file:
encrypted_bytes_big = binary_file.read()
print('read encrypted big file')
print(f'small file: {small_file}')
encrypted_bytes_small = self.load_bytes(small_file)
print('read encrypted small file')
encrypted_model_bytes = encrypted_bytes_small + encrypted_bytes_big encrypted_model_bytes = encrypted_bytes_small + encrypted_bytes_big
key = Security.get_model_encryption_key() key = Security.get_model_encryption_key()
+73
View File
@@ -0,0 +1,73 @@
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_all
datas = []
binaries = []
hiddenimports = ['constants', 'annotation', 'credentials', 'file_data', 'user', 'security', 'secure_model', 'api_client', 'hardware_service', 'remote_command', 'ai_config', 'inference_engine', 'inference', 'remote_command_handler']
tmp_ret = collect_all('pyyaml')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('jwt')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('requests')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('psutil')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('msgpack')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('zmq')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('cryptography')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('cv2')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('onnxruntime')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('tensorrt')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('pycuda')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('re')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
a = Analysis(
['start.py'],
pathex=[],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='azaion-inference',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='azaion-inference',
)
+5 -1
View File
@@ -1,4 +1,4 @@
pyinstaller --onefile ^ pyinstaller --name=azaion-inference ^
--collect-all pyyaml ^ --collect-all pyyaml ^
--collect-all jwt ^ --collect-all jwt ^
--collect-all requests ^ --collect-all requests ^
@@ -8,6 +8,9 @@ pyinstaller --onefile ^
--collect-all cryptography ^ --collect-all cryptography ^
--collect-all cv2 ^ --collect-all cv2 ^
--collect-all onnxruntime ^ --collect-all onnxruntime ^
--collect-all tensorrt ^
--collect-all pycuda ^
--collect-all re ^
--hidden-import constants ^ --hidden-import constants ^
--hidden-import annotation ^ --hidden-import annotation ^
--hidden-import credentials ^ --hidden-import credentials ^
@@ -19,6 +22,7 @@ pyinstaller --onefile ^
--hidden-import hardware_service ^ --hidden-import hardware_service ^
--hidden-import remote_command ^ --hidden-import remote_command ^
--hidden-import ai_config ^ --hidden-import ai_config ^
--hidden-import inference_engine ^
--hidden-import inference ^ --hidden-import inference ^
--hidden-import remote_command_handler ^ --hidden-import remote_command_handler ^
start.py start.py
+7 -2
View File
@@ -6,8 +6,13 @@ cdef str ANNOTATIONS_QUEUE # Name of the annotations queue in rabbit
cdef str API_URL # Base URL for the external API cdef str API_URL # Base URL for the external API
cdef str QUEUE_CONFIG_FILENAME # queue config filename to load from api cdef str QUEUE_CONFIG_FILENAME # queue config filename to load from api
cdef str AI_MODEL_FILE_BIG # AI Model file (BIG part)
cdef str AI_MODEL_FILE_SMALL # AI Model file (small part) cdef str AI_ONNX_MODEL_FILE_BIG
cdef str AI_ONNX_MODEL_FILE_SMALL
cdef str AI_TENSOR_MODEL_FILE_BIG
cdef str AI_TENSOR_MODEL_FILE_SMALL
cdef bytes DONE_SIGNAL cdef bytes DONE_SIGNAL
+6 -2
View File
@@ -8,8 +8,12 @@ cdef str ANNOTATIONS_QUEUE = "azaion-annotations"
cdef str API_URL = "https://api.azaion.com" # Base URL for the external API cdef str API_URL = "https://api.azaion.com" # Base URL for the external API
cdef str QUEUE_CONFIG_FILENAME = "secured-config.json" cdef str QUEUE_CONFIG_FILENAME = "secured-config.json"
cdef str AI_MODEL_FILE_BIG = "azaion.onnx.big"
cdef str AI_MODEL_FILE_SMALL = "azaion.onnx.small" cdef str AI_ONNX_MODEL_FILE_BIG = "azaion.onnx.big"
cdef str AI_ONNX_MODEL_FILE_SMALL = "azaion.onnx.small"
cdef str AI_TENSOR_MODEL_FILE_BIG = "azaion.engine.big"
cdef str AI_TENSOR_MODEL_FILE_SMALL = "azaion.engine.small"
cdef log(str log_message, bytes client_id=None): cdef log(str log_message, bytes client_id=None):
local_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) local_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
+3
View File
@@ -5,4 +5,7 @@ cdef class HardwareInfo:
cdef class HardwareService: cdef class HardwareService:
cdef bint is_windows cdef bint is_windows
cdef get_mac_address(self, interface=*) cdef get_mac_address(self, interface=*)
@staticmethod
cdef has_nvidia_gpu()
cdef HardwareInfo get_hardware_info(self) cdef HardwareInfo get_hardware_info(self)
+13
View File
@@ -1,3 +1,4 @@
import re
import subprocess import subprocess
import psutil import psutil
@@ -42,6 +43,18 @@ cdef class HardwareService:
return addr.address.replace('-', '') return addr.address.replace('-', '')
return None return None
@staticmethod
cdef has_nvidia_gpu():
try:
output = subprocess.check_output(['nvidia-smi']).decode()
match = re.search(r'CUDA Version:\s*([\d.]+)', output)
if match:
return float(match.group(1)) > 11
return False
except Exception as e:
print(e)
return False
cdef HardwareInfo get_hardware_info(self): cdef HardwareInfo get_hardware_info(self):
if self.is_windows: if self.is_windows:
os_command = ( os_command = (
+2 -1
View File
@@ -2,10 +2,11 @@ from remote_command cimport RemoteCommand
from annotation cimport Annotation, Detection from annotation cimport Annotation, Detection
from ai_config cimport AIRecognitionConfig from ai_config cimport AIRecognitionConfig
from api_client cimport ApiClient from api_client cimport ApiClient
from inference_engine cimport InferenceEngine
cdef class Inference: cdef class Inference:
cdef ApiClient api_client cdef ApiClient api_client
cdef object session cdef InferenceEngine engine
cdef object on_annotation cdef object on_annotation
cdef Annotation _previous_annotation cdef Annotation _previous_annotation
cdef AIRecognitionConfig ai_config cdef AIRecognitionConfig ai_config
+50 -42
View File
@@ -1,40 +1,40 @@
import json import json
import mimetypes import mimetypes
import subprocess
import cv2 import cv2
import numpy as np import numpy as np
import onnxruntime as onnx
cimport constants
from remote_command cimport RemoteCommand from remote_command cimport RemoteCommand
from annotation cimport Detection, Annotation from annotation cimport Detection, Annotation
from ai_config cimport AIRecognitionConfig from ai_config cimport AIRecognitionConfig
from inference_engine cimport OnnxEngine, TensorRTEngine
from hardware_service cimport HardwareService
cdef class Inference: cdef class Inference:
def __init__(self, api_client, on_annotation): def __init__(self, api_client, on_annotation):
self.api_client = api_client self.api_client = api_client
self.on_annotation = on_annotation self.on_annotation = on_annotation
self.stop_signal = False self.stop_signal = False
self.session = None
self.model_input = None self.model_input = None
self.model_width = 0 self.model_width = 0
self.model_height = 0 self.model_height = 0
self.engine = None
self.class_names = None self.class_names = None
def init_ai(self): def init_ai(self):
model_bytes = self.api_client.load_ai_model() if self.engine is not None:
self.session = onnx.InferenceSession( return
model_bytes, providers=["CUDAExecutionProvider", "CPUExecutionProvider"]
) is_nvidia = HardwareService.has_nvidia_gpu()
model_inputs = self.session.get_inputs() if is_nvidia:
self.model_input = model_inputs[0].name model_bytes = self.api_client.load_ai_model(is_tensor=True)
input_shape = model_inputs[0].shape self.engine = TensorRTEngine(model_bytes, batch_size=4)
self.model_width = input_shape[2] else:
self.model_height = input_shape[3] model_bytes = self.api_client.load_ai_model()
print(f'AI detection model input: {self.model_input} ({self.model_width}, {self.model_height})') self.engine = OnnxEngine(model_bytes, batch_size=4)
model_meta = self.session.get_modelmeta()
print("Metadata:", model_meta.custom_metadata_map) self.model_height, self.model_width = self.engine.get_input_shape()
self.class_names = eval(model_meta.custom_metadata_map["names"]) self.class_names = self.engine.get_class_names()
cdef preprocess(self, frames): cdef preprocess(self, frames):
blobs = [cv2.dnn.blobFromImage(frame, blobs = [cv2.dnn.blobFromImage(frame,
@@ -47,33 +47,37 @@ cdef class Inference:
return np.vstack(blobs) return np.vstack(blobs)
cdef postprocess(self, output, ai_config): cdef postprocess(self, output, ai_config):
print('enter postprocess')
cdef list[Detection] detections = [] cdef list[Detection] detections = []
cdef int ann_index cdef int ann_index
cdef float x1, y1, x2, y2, conf, cx, cy, w, h cdef float x1, y1, x2, y2, conf, cx, cy, w, h
cdef int class_id cdef int class_id
cdef list[list[Detection]] results = [] cdef list[list[Detection]] results = []
print('start try: code')
try:
for ann_index in range(len(output[0])):
detections.clear()
for det in output[0][ann_index]:
if det[4] == 0: # if confidence is 0 then valid points are over.
break
x1 = det[0] / self.model_width
y1 = det[1] / self.model_height
x2 = det[2] / self.model_width
y2 = det[3] / self.model_height
conf = round(det[4], 2)
class_id = int(det[5])
for ann_index in range(len(output[0])): x = (x1 + x2) / 2
detections.clear() y = (y1 + y2) / 2
for det in output[0][ann_index]: w = x2 - x1
if det[4] == 0: # if confidence is 0 then valid points are over. h = y2 - y1
break if conf >= ai_config.probability_threshold:
x1 = det[0] / self.model_width detections.append(Detection(x, y, w, h, class_id, conf))
y1 = det[1] / self.model_height filtered_detections = self.remove_overlapping_detections(detections)
x2 = det[2] / self.model_width results.append(filtered_detections)
y2 = det[3] / self.model_height return results
conf = round(det[4], 2) except Exception as e:
class_id = int(det[5]) raise RuntimeError(f"Failed to postprocess: {str(e)}")
x = (x1 + x2) / 2
y = (y1 + y2) / 2
w = x2 - x1
h = y2 - y1
if conf >= ai_config.probability_threshold:
detections.append(Detection(x, y, w, h, class_id, conf))
filtered_detections = self.remove_overlapping_detections(detections)
results.append(filtered_detections)
return results
cdef remove_overlapping_detections(self, list[Detection] detections): cdef remove_overlapping_detections(self, list[Detection] detections):
cdef Detection det1, det2 cdef Detection det1, det2
@@ -121,8 +125,7 @@ cdef class Inference:
raise Exception('ai recognition config is empty') raise Exception('ai recognition config is empty')
self.stop_signal = False self.stop_signal = False
if self.session is None: self.init_ai()
self.init_ai()
print(ai_config.paths) print(ai_config.paths)
for m in ai_config.paths: for m in ai_config.paths:
@@ -160,7 +163,9 @@ cdef class Inference:
if len(batch_frames) == ai_config.model_batch_size: if len(batch_frames) == ai_config.model_batch_size:
input_blob = self.preprocess(batch_frames) input_blob = self.preprocess(batch_frames)
outputs = self.session.run(None, {self.model_input: input_blob})
outputs = self.engine.run(input_blob)
list_detections = self.postprocess(outputs, ai_config) list_detections = self.postprocess(outputs, ai_config)
for i in range(len(list_detections)): for i in range(len(list_detections)):
detections = list_detections[i] detections = list_detections[i]
@@ -189,7 +194,9 @@ cdef class Inference:
timestamps.append(0) timestamps.append(0)
input_blob = self.preprocess(frames) input_blob = self.preprocess(frames)
outputs = self.session.run(None, {self.model_input: input_blob})
outputs = self.engine.run(input_blob)
list_detections = self.postprocess(outputs, ai_config) list_detections = self.postprocess(outputs, ai_config)
for i in range(len(list_detections)): for i in range(len(list_detections)):
detections = list_detections[i] detections = list_detections[i]
@@ -199,6 +206,7 @@ cdef class Inference:
print(annotation.to_str(self.class_names)) print(annotation.to_str(self.class_names))
self.on_annotation(cmd, annotation) self.on_annotation(cmd, annotation)
cdef stop(self): cdef stop(self):
self.stop_signal = True self.stop_signal = True
+29
View File
@@ -0,0 +1,29 @@
from typing import List, Tuple
import numpy as np
cdef class InferenceEngine:
cdef public int batch_size
cdef tuple get_input_shape(self)
cdef int get_batch_size(self)
cdef get_class_names(self)
cpdef run(self, input_data)
cdef class OnnxEngine(InferenceEngine):
cdef object session
cdef list model_inputs
cdef str input_name
cdef object input_shape
cdef object class_names
cdef class TensorRTEngine(InferenceEngine):
cdef object stream
cdef object context
cdef str input_name
cdef str output_name
cdef object d_input
cdef object d_output
cdef object input_shape
cdef object output_shape
cdef object h_output
cdef object class_names
+140
View File
@@ -0,0 +1,140 @@
import json
import struct
from typing import List, Tuple
import numpy as np
import onnxruntime as onnx
import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit # required for automatically initialize CUDA, do not remove.
cdef class InferenceEngine:
def __init__(self, model_bytes: bytes, batch_size: int = 1, **kwargs):
self.batch_size = batch_size
cdef tuple get_input_shape(self):
raise NotImplementedError("Subclass must implement get_input_shape")
cdef int get_batch_size(self):
return self.batch_size
cpdef run(self, input_data):
raise NotImplementedError("Subclass must implement run")
cdef get_class_names(self):
raise NotImplementedError("Subclass must implement get_class_names")
cdef class OnnxEngine(InferenceEngine):
def __init__(self, model_bytes: bytes, batch_size: int = 1, **kwargs):
super().__init__(model_bytes, batch_size)
self.batch_size = batch_size
self.session = onnx.InferenceSession(model_bytes, providers=["CUDAExecutionProvider", "CPUExecutionProvider"])
self.model_inputs = self.session.get_inputs()
self.input_name = self.model_inputs[0].name
self.input_shape = self.model_inputs[0].shape
if self.input_shape[0] != -1:
self.batch_size = self.input_shape[0]
print(f'AI detection model input: {self.model_inputs} {self.input_shape}')
model_meta = self.session.get_modelmeta()
print("Metadata:", model_meta.custom_metadata_map)
self.class_names = eval(model_meta.custom_metadata_map["names"])
cdef tuple get_input_shape(self):
shape = self.input_shape
return shape[2], shape[3]
cdef int get_batch_size(self):
return self.batch_size
cdef get_class_names(self):
return self.class_names
cpdef run(self, input_data):
return self.session.run(None, {self.input_name: input_data})
cdef class TensorRTEngine(InferenceEngine):
def __init__(self, model_bytes: bytes, batch_size: int = 4, **kwargs):
super().__init__(model_bytes, batch_size)
self.batch_size = batch_size
print('Enter init TensorRT')
try:
logger = trt.Logger(trt.Logger.WARNING)
metadata_len = struct.unpack("<I", model_bytes[:4])[0]
try:
metadata = json.loads(model_bytes[4:4 + metadata_len])
print(f"Model metadata: {json.dumps(metadata, indent=2)}")
string_dict = metadata['names']
self.class_names = {int(k): v for k, v in string_dict.items()}
except json.JSONDecodeError:
print(f"Failed to parse metadata")
return
engine_data = model_bytes[4 + metadata_len:]
runtime = trt.Runtime(logger)
engine = runtime.deserialize_cuda_engine(engine_data)
if engine is None:
raise RuntimeError(f"Failed to load TensorRT engine from bytes")
self.context = engine.create_execution_context()
# input
self.input_name = engine.get_tensor_name(0)
engine_input_shape = engine.get_tensor_shape(self.input_name)
if engine_input_shape[0] != -1:
self.batch_size = engine_input_shape[0]
self.input_shape = [
self.batch_size,
engine_input_shape[1], # Channels (usually fixed at 3 for RGB)
1280 if engine_input_shape[2] == -1 else engine_input_shape[2], # Height
1280 if engine_input_shape[3] == -1 else engine_input_shape[3] # Width
]
self.context.set_input_shape(self.input_name, self.input_shape)
input_size = trt.volume(self.input_shape) * np.dtype(np.float32).itemsize
self.d_input = cuda.mem_alloc(input_size)
# output
self.output_name = engine.get_tensor_name(1)
engine_output_shape = tuple(engine.get_tensor_shape(self.output_name))
self.output_shape = [
batch_size if self.input_shape[0] == -1 else self.input_shape[0],
300 if engine_output_shape[1] == -1 else engine_output_shape[1], # max detections number
6 if engine_output_shape[2] == -1 else engine_output_shape[2] # x1 y1 x2 y2 conf cls
]
self.h_output = cuda.pagelocked_empty(tuple(self.output_shape), dtype=np.float32)
self.d_output = cuda.mem_alloc(self.h_output.nbytes)
self.stream = cuda.Stream()
except Exception as e:
raise RuntimeError(f"Failed to initialize TensorRT engine: {str(e)}")
cdef tuple get_input_shape(self):
return self.input_shape[2], self.input_shape[3]
cdef int get_batch_size(self):
return self.batch_size
cdef get_class_names(self):
return self.class_names
cpdef run(self, input_data):
try:
cuda.memcpy_htod_async(self.d_input, input_data, self.stream)
self.context.set_tensor_address(self.input_name, int(self.d_input)) # input buffer
self.context.set_tensor_address(self.output_name, int(self.d_output)) # output buffer
self.context.execute_async_v3(stream_handle=self.stream.handle)
self.stream.synchronize()
# Fix: Remove the stream parameter from memcpy_dtoh
cuda.memcpy_dtoh(self.h_output, self.d_output)
output = self.h_output.reshape(self.output_shape)
return [output]
except Exception as e:
raise RuntimeError(f"Failed to run TensorRT inference: {str(e)}")
+3 -1
View File
@@ -9,4 +9,6 @@ msgpack
pyjwt pyjwt
zmq zmq
requests requests
pyyaml pyyaml
pycuda
tensorrt
+1
View File
@@ -14,6 +14,7 @@ extensions = [
Extension('user', ['user.pyx']), Extension('user', ['user.pyx']),
Extension('api_client', ['api_client.pyx']), Extension('api_client', ['api_client.pyx']),
Extension('ai_config', ['ai_config.pyx']), Extension('ai_config', ['ai_config.pyx']),
Extension('inference_engine', ['inference_engine.pyx'], include_dirs=[np.get_include()]),
Extension('inference', ['inference.pyx'], include_dirs=[np.get_include()]), Extension('inference', ['inference.pyx'], include_dirs=[np.get_include()]),
Extension('main', ['main.pyx']), Extension('main', ['main.pyx']),
] ]
+4 -4
View File
@@ -32,8 +32,8 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Azaion.Common\Azaion.Common.csproj" /> <ProjectReference Include="..\Azaion.Common\Azaion.Common.csproj" />
<ProjectReference Include="..\dummy\Azaion.Annotator\Azaion.Annotator.csproj" /> <ProjectReference Include="..\Azaion.Annotator\Azaion.Annotator.csproj" />
<ProjectReference Include="..\dummy\Azaion.Dataset\Azaion.Dataset.csproj" /> <ProjectReference Include="..\Azaion.Dataset\Azaion.Dataset.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -61,8 +61,8 @@
<Target Name="PostBuild" AfterTargets="Build"> <Target Name="PostBuild" AfterTargets="Build">
<MakeDir Directories="$(TargetDir)dummy" /> <MakeDir Directories="$(TargetDir)dummy" />
<Move SourceFiles="$(TargetDir)Azaion.Annotator.dll" DestinationFolder="$(TargetDir)dummy" /> <Copy SourceFiles="$(TargetDir)Azaion.Annotator.dll" DestinationFolder="$(TargetDir)dummy" />
<Move SourceFiles="$(TargetDir)Azaion.Dataset.dll" DestinationFolder="$(TargetDir)dummy" /> <Copy SourceFiles="$(TargetDir)Azaion.Dataset.dll" DestinationFolder="$(TargetDir)dummy" />
<Exec Command="upload.cmd $(ConfigurationName) stage" /> <Exec Command="upload.cmd $(ConfigurationName) stage" />
</Target> </Target>
+2 -2
View File
@@ -74,7 +74,7 @@
BorderBrush="DimGray" BorderBrush="DimGray"
BorderThickness="0,0,0,1" BorderThickness="0,0,0,1"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Text="" Text="admin@azaion.com"
/> />
<TextBlock Text="Пароль" <TextBlock Text="Пароль"
Grid.Row="2" Grid.Row="2"
@@ -89,7 +89,7 @@
Width="300" Width="300"
BorderThickness="0,0,0,1" BorderThickness="0,0,0,1"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Password=""/> Password="Az@1on1000Odm$n"/>
</Grid> </Grid>
<Button x:Name="LoginBtn" <Button x:Name="LoginBtn"
Content="Вхід" Content="Вхід"
+19 -16
View File
@@ -8,7 +8,8 @@
}, },
"GpsDeniedClientConfig": { "GpsDeniedClientConfig": {
"ZeroMqHost": "127.0.0.1", "ZeroMqHost": "127.0.0.1",
"ZeroMqPort": 5227, "ZeroMqPort": 5555,
"ZeroMqReceiverPort": 5556,
"RetryCount": 25, "RetryCount": 25,
"TimeoutSeconds": 5 "TimeoutSeconds": 5
}, },
@@ -21,20 +22,22 @@
}, },
"AnnotationConfig": { "AnnotationConfig": {
"DetectionClasses": [ "DetectionClasses": [
{ "Id": 0, "Name": "Броньована техніка", "ShortName": "Броня" }, { "Id": 0, "Name": "ArmorVehicle", "ShortName": "Броня", "Color": "#FF0000" },
{ "Id": 1, "Name": "Вантажівка", "ShortName": "Вантаж." }, { "Id": 1, "Name": "Truck", "ShortName": "Вантаж.", "Color": "#00FF00" },
{ "Id": 2, "Name": "Машина легкова", "ShortName": "Машина" }, { "Id": 2, "Name": "Vehicle", "ShortName": "Машина", "Color": "#0000FF" },
{ "Id": 3, "Name": "Артилерія", "ShortName": "Арта" }, { "Id": 3, "Name": "Atillery", "ShortName": "Арта", "Color": "#FFFF00" },
{ "Id": 4, "Name": "Тінь від техніки", "ShortName": "Тінь" }, { "Id": 4, "Name": "Shadow", "ShortName": "Тінь", "Color": "#FF00FF" },
{ "Id": 5, "Name": "Окопи", "ShortName": "Окопи" }, { "Id": 5, "Name": "Trenches", "ShortName": "Окопи", "Color": "#00FFFF" },
{ "Id": 6, "Name": "Військовий", "ShortName": "Військов" }, { "Id": 6, "Name": "MilitaryMan", "ShortName": "Військов", "Color": "#188021" },
{ "Id": 7, "Name": "Накати", "ShortName": "Накати" }, { "Id": 7, "Name": "TyreTracks", "ShortName": "Накати", "Color": "#800000" },
{ "Id": 8, "Name": "Танк з захистом", "ShortName": "Танк.захист" }, { "Id": 8, "Name": "AdditArmoredTank", "ShortName": "Танк.захист", "Color": "#008000" },
{ "Id": 9, "Name": "Дим", "ShortName": "Дим" }, { "Id": 9, "Name": "Smoke", "ShortName": "Дим", "Color": "#000080" },
{ "Id": 10, "Name": "Літак", "ShortName": "Літак" }, { "Id": 10, "Name": "Plane", "ShortName": "Літак", "Color": "#000080" },
{ "Id": 11, "Name": "Мотоцикл", "ShortName": "Мото" }, { "Id": 11, "Name": "Moto", "ShortName": "Мото", "Color": "#808000" },
{ "Id": 12, "Name": "Маскування сіткою", "ShortName": "Сітка" }, { "Id": 12, "Name": "CamouflageNnet", "ShortName": "Сітка", "Color": "#800080" },
{ "Id": 13, "Name": "Маскування гілками", "ShortName": "Гілки" } { "Id": 13, "Name": "CamouflageBranches", "ShortName": "Гілки", "Color": "#008080" },
{ "Id": 14, "Name": "Roof", "ShortName": "Дах", "Color": "#0050A0" },
{ "Id": 15, "Name": "Building", "ShortName": "Будівля", "Color": "#008080" }
], ],
"LastSelectedExplorerClass": null, "LastSelectedExplorerClass": null,
"VideoFormats": [ ".mp4", ".mov", ".avi" ], "VideoFormats": [ ".mp4", ".mov", ".avi" ],
@@ -52,7 +55,7 @@
"TrackingProbabilityIncrease": 15.0, "TrackingProbabilityIncrease": 15.0,
"TrackingIntersectionThreshold": 0.8, "TrackingIntersectionThreshold": 0.8,
"ModelBatchSize": 2 "ModelBatchSize": 4
}, },
"ThumbnailConfig": { "Size": "240,135", "Border": 10 }, "ThumbnailConfig": { "Size": "240,135", "Border": 10 },
"MapConfig": "MapConfig":
+17 -16
View File
@@ -3,8 +3,7 @@
"ZeroMqHost": "127.0.0.1", "ZeroMqHost": "127.0.0.1",
"ZeroMqPort": 5131, "ZeroMqPort": 5131,
"RetryCount": 25, "RetryCount": 25,
"TimeoutSeconds": 5, "TimeoutSeconds": 5
"ResourcesFolder": "stage"
}, },
"GpsDeniedClientConfig": { "GpsDeniedClientConfig": {
"ZeroMqHost": "127.0.0.1", "ZeroMqHost": "127.0.0.1",
@@ -21,20 +20,22 @@
}, },
"AnnotationConfig": { "AnnotationConfig": {
"DetectionClasses": [ "DetectionClasses": [
{ "Id": 0, "Name": "Броньована техніка", "ShortName": "Броня" }, { "Id": 0, "Name": "ArmorVehicle", "ShortName": "Броня", "Color": "#80FF0000" },
{ "Id": 1, "Name": "Вантажівка", "ShortName": "Вантаж." }, { "Id": 1, "Name": "Truck", "ShortName": "Вантаж.", "Color": "#8000FF00" },
{ "Id": 2, "Name": "Машина легкова", "ShortName": "Машина" }, { "Id": 2, "Name": "Vehicle", "ShortName": "Машина", "Color": "#800000FF" },
{ "Id": 3, "Name": "Артилерія", "ShortName": "Арта" }, { "Id": 3, "Name": "Atillery", "ShortName": "Арта", "Color": "#80FFFF00" },
{ "Id": 4, "Name": "Тінь від техніки", "ShortName": "Тінь" }, { "Id": 4, "Name": "Shadow", "ShortName": "Тінь", "Color": "#80FF00FF" },
{ "Id": 5, "Name": "Окопи", "ShortName": "Окопи" }, { "Id": 5, "Name": "Trenches", "ShortName": "Окопи", "Color": "#8000FFFF" },
{ "Id": 6, "Name": "Військовий", "ShortName": "Військов" }, { "Id": 6, "Name": "MilitaryMan", "ShortName": "Військов", "Color": "#80188021" },
{ "Id": 7, "Name": "Накати", "ShortName": "Накати" }, { "Id": 7, "Name": "TyreTracks", "ShortName": "Накати", "Color": "#80800000" },
{ "Id": 8, "Name": "Танк з захистом", "ShortName": "Танк.захист" }, { "Id": 8, "Name": "AdditArmoredTank", "ShortName": "Танк.захист", "Color": "#80008000" },
{ "Id": 9, "Name": "Дим", "ShortName": "Дим" }, { "Id": 9, "Name": "Smoke", "ShortName": "Дим", "Color": "#8080000080" },
{ "Id": 10, "Name": "Літак", "ShortName": "Літак" }, { "Id": 10, "Name": "Plane", "ShortName": "Літак", "Color": "#80000080" },
{ "Id": 11, "Name": "Мотоцикл", "ShortName": "Мото" }, { "Id": 11, "Name": "Moto", "ShortName": "Мото", "Color": "#80808000" },
{ "Id": 12, "Name": "Маскування сіткою", "ShortName": "Сітка" }, { "Id": 12, "Name": "CamouflageNnet", "ShortName": "Сітка", "Color": "#80800080" },
{ "Id": 13, "Name": "Маскування гілками", "ShortName": "Гілки" } { "Id": 13, "Name": "CamouflageBranches", "ShortName": "Гілки", "Color": "#80008080" },
{ "Id": 14, "Name": "Roof", "ShortName": "Дах", "Color": "#800050A0" },
{ "Id": 15, "Name": "Building", "ShortName": "Будівля", "Color": "#80008080" }
], ],
"LastSelectedExplorerClass": null, "LastSelectedExplorerClass": null,
"VideoFormats": [ ".mp4", ".mov", ".avi" ], "VideoFormats": [ ".mp4", ".mov", ".avi" ],
+10 -3
View File
@@ -21,7 +21,7 @@ move dist\Azaion.Dataset.dll dist\dummy\
echo Build Cython app echo Build Cython app
cd Azaion.Inference cd Azaion.Inference
.\venv\Scripts\pyinstaller --onefile ^ .\venv\Scripts\pyinstaller --name=azaion-inference ^
--collect-all jwt ^ --collect-all jwt ^
--collect-all requests ^ --collect-all requests ^
--collect-all psutil ^ --collect-all psutil ^
@@ -30,6 +30,9 @@ cd Azaion.Inference
--collect-all cryptography ^ --collect-all cryptography ^
--collect-all cv2 ^ --collect-all cv2 ^
--collect-all onnxruntime ^ --collect-all onnxruntime ^
--collect-all tensorrt ^
--collect-all pycuda ^
--collect-all re ^
--hidden-import constants ^ --hidden-import constants ^
--hidden-import annotation ^ --hidden-import annotation ^
--hidden-import credentials ^ --hidden-import credentials ^
@@ -41,6 +44,7 @@ cd Azaion.Inference
--hidden-import hardware_service ^ --hidden-import hardware_service ^
--hidden-import remote_command ^ --hidden-import remote_command ^
--hidden-import ai_config ^ --hidden-import ai_config ^
--hidden-import inference_engine ^
--hidden-import inference ^ --hidden-import inference ^
--hidden-import remote_command_handler ^ --hidden-import remote_command_handler ^
start.py start.py
@@ -51,7 +55,9 @@ cd..
echo Download onnx model echo Download onnx model
cd build cd build
call cdn_manager.exe download models azaion.onnx.big call cdn_manager.exe download models azaion.onnx.big
move azaion.onnx.big ..\dist\ call cdn_manager.exe download models azaion.engine.big
move azaion.* ..\dist\
cd.. cd..
echo Copy ico echo Copy ico
@@ -64,8 +70,9 @@ copy %cudnn-folder%\* dist\*
del dist\cudnn_adv64_9.dll del dist\cudnn_adv64_9.dll
echo building installer... echo building installer...
iscc build\installer.iss iscc build\installer.iss
cd build\ cd build\
echo uploading installer... echo uploading installer...
call .\cdn_manager.exe upload suite AzaionSuiteInstaller.exe ..\AzaionSuiteInstaller.exe call cdn_manager.exe upload suite AzaionSuiteInstaller.exe ..\AzaionSuiteInstaller.exe