mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 11:06:30 +00:00
splitting python complete
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWPF>true</UseWPF>
|
||||
<LangVersion>12</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+45
-43
@@ -9,13 +9,15 @@ using System.Windows;
|
||||
|
||||
namespace Azaion.Common;
|
||||
|
||||
public class Constants
|
||||
public static class Constants
|
||||
{
|
||||
public const string CONFIG_PATH = "config.json";
|
||||
public const string LOADER_CONFIG_PATH = "loaderconfig.json";
|
||||
public const string DEFAULT_API_URL = "https://api.azaion.com";
|
||||
public const string AZAION_SUITE_EXE = "Azaion.Suite.exe";
|
||||
|
||||
public const int AI_TILE_SIZE = 1280;
|
||||
|
||||
#region ExternalClientsConfig
|
||||
|
||||
private const string DEFAULT_ZMQ_LOADER_HOST = "127.0.0.1";
|
||||
@@ -27,11 +29,11 @@ public class Constants
|
||||
public static readonly string ExternalGpsDeniedPath = Path.Combine(EXTERNAL_GPS_DENIED_FOLDER, "image-matcher.exe");
|
||||
|
||||
public const string DEFAULT_ZMQ_INFERENCE_HOST = "127.0.0.1";
|
||||
public const int DEFAULT_ZMQ_INFERENCE_PORT = 5227;
|
||||
private const int DEFAULT_ZMQ_INFERENCE_PORT = 5227;
|
||||
|
||||
public const string DEFAULT_ZMQ_GPS_DENIED_HOST = "127.0.0.1";
|
||||
public const int DEFAULT_ZMQ_GPS_DENIED_PORT = 5255;
|
||||
public const int DEFAULT_ZMQ_GPS_DENIED_PUBLISH_PORT = 5256;
|
||||
private const string DEFAULT_ZMQ_GPS_DENIED_HOST = "127.0.0.1";
|
||||
private const int DEFAULT_ZMQ_GPS_DENIED_PORT = 5255;
|
||||
private const int DEFAULT_ZMQ_GPS_DENIED_PUBLISH_PORT = 5256;
|
||||
|
||||
#endregion ExternalClientsConfig
|
||||
|
||||
@@ -42,41 +44,33 @@ public class Constants
|
||||
|
||||
# endregion
|
||||
|
||||
public const string JPG_EXT = ".jpg";
|
||||
public const string JPG_EXT = ".jpg";
|
||||
public const string TXT_EXT = ".txt";
|
||||
#region DirectoriesConfig
|
||||
|
||||
public const string DEFAULT_VIDEO_DIR = "video";
|
||||
public const string DEFAULT_LABELS_DIR = "labels";
|
||||
public const string DEFAULT_IMAGES_DIR = "images";
|
||||
public const string DEFAULT_RESULTS_DIR = "results";
|
||||
public const string DEFAULT_THUMBNAILS_DIR = "thumbnails";
|
||||
public const string DEFAULT_GPS_SAT_DIRECTORY = "satellitesDir";
|
||||
public const string DEFAULT_GPS_ROUTE_DIRECTORY = "routeDir";
|
||||
private const string DEFAULT_VIDEO_DIR = "video";
|
||||
private const string DEFAULT_LABELS_DIR = "labels";
|
||||
private const string DEFAULT_IMAGES_DIR = "images";
|
||||
private const string DEFAULT_RESULTS_DIR = "results";
|
||||
private const string DEFAULT_THUMBNAILS_DIR = "thumbnails";
|
||||
private const string DEFAULT_GPS_SAT_DIRECTORY = "satellitesDir";
|
||||
private const string DEFAULT_GPS_ROUTE_DIRECTORY = "routeDir";
|
||||
|
||||
#endregion
|
||||
|
||||
#region AnnotatorConfig
|
||||
|
||||
public static readonly AnnotationConfig DefaultAnnotationConfig = new()
|
||||
{
|
||||
DetectionClasses = DefaultAnnotationClasses!,
|
||||
VideoFormats = DefaultVideoFormats!,
|
||||
ImageFormats = DefaultImageFormats!,
|
||||
AnnotationsDbFile = DEFAULT_ANNOTATIONS_DB_FILE
|
||||
};
|
||||
|
||||
private static readonly List<DetectionClass> DefaultAnnotationClasses =
|
||||
[
|
||||
new() { Id = 0, Name = "ArmorVehicle", ShortName = "Броня", Color = "#FF0000".ToColor() },
|
||||
new() { Id = 1, Name = "Truck", ShortName = "Вантаж.", Color = "#00FF00".ToColor() },
|
||||
new() { Id = 2, Name = "Vehicle", ShortName = "Машина", Color = "#0000FF".ToColor() },
|
||||
new() { Id = 3, Name = "Atillery", ShortName = "Арта", Color = "#FFFF00".ToColor() },
|
||||
new() { Id = 3, Name = "Artillery", ShortName = "Арта", Color = "#FFFF00".ToColor() },
|
||||
new() { Id = 4, Name = "Shadow", ShortName = "Тінь", Color = "#FF00FF".ToColor() },
|
||||
new() { Id = 5, Name = "Trenches", ShortName = "Окопи", Color = "#00FFFF".ToColor() },
|
||||
new() { Id = 6, Name = "MilitaryMan", ShortName = "Військов", Color = "#188021".ToColor() },
|
||||
new() { Id = 7, Name = "TyreTracks", ShortName = "Накати", Color = "#800000".ToColor() },
|
||||
new() { Id = 8, Name = "AdditArmoredTank", ShortName = "Танк.захист", Color = "#008000".ToColor() },
|
||||
new() { Id = 8, Name = "AdditionArmoredTank",ShortName = "Танк.захист", Color = "#008000".ToColor() },
|
||||
new() { Id = 9, Name = "Smoke", ShortName = "Дим", Color = "#000080".ToColor() },
|
||||
new() { Id = 10, Name = "Plane", ShortName = "Літак", Color = "#000080".ToColor() },
|
||||
new() { Id = 11, Name = "Moto", ShortName = "Мото", Color = "#808000".ToColor() },
|
||||
@@ -86,20 +80,28 @@ public class Constants
|
||||
new() { Id = 15, Name = "Building", ShortName = "Будівля", Color = "#ffb6c1".ToColor() },
|
||||
new() { Id = 16, Name = "Caponier", ShortName = "Капонір", Color = "#ffb6c1".ToColor() },
|
||||
];
|
||||
|
||||
private static readonly List<string> DefaultVideoFormats = ["mp4", "mov", "avi"];
|
||||
private static readonly List<string> DefaultImageFormats = ["jpg", "jpeg", "png", "bmp"];
|
||||
|
||||
public static readonly List<string> DefaultVideoFormats = ["mp4", "mov", "avi"];
|
||||
public static readonly List<string> DefaultImageFormats = ["jpg", "jpeg", "png", "bmp"];
|
||||
private static readonly AnnotationConfig DefaultAnnotationConfig = new()
|
||||
{
|
||||
DetectionClasses = DefaultAnnotationClasses,
|
||||
VideoFormats = DefaultVideoFormats,
|
||||
ImageFormats = DefaultImageFormats,
|
||||
AnnotationsDbFile = DEFAULT_ANNOTATIONS_DB_FILE
|
||||
};
|
||||
|
||||
private const int DEFAULT_LEFT_PANEL_WIDTH = 250;
|
||||
private const int DEFAULT_RIGHT_PANEL_WIDTH = 250;
|
||||
|
||||
public static int DEFAULT_LEFT_PANEL_WIDTH = 250;
|
||||
public static int DEFAULT_RIGHT_PANEL_WIDTH = 250;
|
||||
|
||||
public const string DEFAULT_ANNOTATIONS_DB_FILE = "annotations.db";
|
||||
private const string DEFAULT_ANNOTATIONS_DB_FILE = "annotations.db";
|
||||
|
||||
# endregion AnnotatorConfig
|
||||
|
||||
# region AIRecognitionConfig
|
||||
|
||||
public static readonly AIRecognitionConfig DefaultAIRecognitionConfig = new()
|
||||
private static readonly AIRecognitionConfig DefaultAIRecognitionConfig = new()
|
||||
{
|
||||
FrameRecognitionSeconds = DEFAULT_FRAME_RECOGNITION_SECONDS,
|
||||
TrackingDistanceConfidence = TRACKING_DISTANCE_CONFIDENCE,
|
||||
@@ -109,18 +111,18 @@ public class Constants
|
||||
FramePeriodRecognition = DEFAULT_FRAME_PERIOD_RECOGNITION
|
||||
};
|
||||
|
||||
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 const int DEFAULT_BIG_IMAGE_TILE_OVERLAP_PERCENT = 20;
|
||||
public const int DEFAULT_FRAME_PERIOD_RECOGNITION = 4;
|
||||
private const double DEFAULT_FRAME_RECOGNITION_SECONDS = 2;
|
||||
private const double TRACKING_DISTANCE_CONFIDENCE = 0.15;
|
||||
private const double TRACKING_PROBABILITY_INCREASE = 15;
|
||||
private const double TRACKING_INTERSECTION_THRESHOLD = 0.8;
|
||||
private const int DEFAULT_BIG_IMAGE_TILE_OVERLAP_PERCENT = 20;
|
||||
private const int DEFAULT_FRAME_PERIOD_RECOGNITION = 4;
|
||||
|
||||
# endregion AIRecognitionConfig
|
||||
|
||||
# region GpsDeniedConfig
|
||||
|
||||
public static readonly GpsDeniedConfig DefaultGpsDeniedConfig = new()
|
||||
private static readonly GpsDeniedConfig DefaultGpsDeniedConfig = new()
|
||||
{
|
||||
MinKeyPoints = 11
|
||||
};
|
||||
@@ -129,15 +131,15 @@ public class Constants
|
||||
|
||||
#region Thumbnails
|
||||
|
||||
public static readonly ThumbnailConfig DefaultThumbnailConfig = new()
|
||||
private static readonly Size DefaultThumbnailSize = new(240, 135);
|
||||
|
||||
private static readonly ThumbnailConfig DefaultThumbnailConfig = new()
|
||||
{
|
||||
Size = DefaultThumbnailSize,
|
||||
Border = DEFAULT_THUMBNAIL_BORDER
|
||||
};
|
||||
|
||||
public static readonly Size DefaultThumbnailSize = new(240, 135);
|
||||
|
||||
public const int DEFAULT_THUMBNAIL_BORDER = 10;
|
||||
private const int DEFAULT_THUMBNAIL_BORDER = 10;
|
||||
|
||||
public const string THUMBNAIL_PREFIX = "_thumb";
|
||||
public const string RESULT_PREFIX = "_result";
|
||||
@@ -163,10 +165,10 @@ public class Constants
|
||||
|
||||
#endregion
|
||||
|
||||
public const string CSV_PATH = "matches.csv";
|
||||
public const string SPLIT_SUFFIX = "!split!";
|
||||
|
||||
|
||||
public static readonly InitConfig DefaultInitConfig = new()
|
||||
private static readonly InitConfig DefaultInitConfig = new()
|
||||
{
|
||||
LoaderClientConfig = new LoaderClientConfig
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
using Azaion.Common.Database;
|
||||
using Azaion.Common.DTO;
|
||||
using Azaion.Common.Events;
|
||||
using MediatR;
|
||||
@@ -39,7 +40,6 @@ public class CanvasEditor : Canvas
|
||||
private readonly TimeSpan _viewThreshold = TimeSpan.FromMilliseconds(400);
|
||||
|
||||
public Image BackgroundImage { get; set; } = new() { Stretch = Stretch.Uniform };
|
||||
public IMediator Mediator { get; set; } = null!;
|
||||
|
||||
public static readonly DependencyProperty GetTimeFuncProp =
|
||||
DependencyProperty.Register(
|
||||
@@ -191,7 +191,6 @@ public class CanvasEditor : Canvas
|
||||
private void CanvasMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
var pos = e.GetPosition(this);
|
||||
Mediator.Publish(new SetStatusTextEvent($"Mouse Coordinates: {pos.X}, {pos.Y}"));
|
||||
_horizontalLine.Y1 = _horizontalLine.Y2 = pos.Y;
|
||||
_verticalLine.X1 = _verticalLine.X2 = pos.X;
|
||||
SetLeft(_classNameHint, pos.X + 10);
|
||||
@@ -223,7 +222,6 @@ public class CanvasEditor : Canvas
|
||||
matrix.Translate(delta.X, delta.Y);
|
||||
|
||||
_matrixTransform.Matrix = matrix;
|
||||
Mediator.Publish(new SetStatusTextEvent(_matrixTransform.Matrix.ToString()));
|
||||
}
|
||||
|
||||
private void CanvasMouseUp(object sender, MouseButtonEventArgs e)
|
||||
@@ -243,8 +241,8 @@ public class CanvasEditor : Canvas
|
||||
{
|
||||
Width = width,
|
||||
Height = height,
|
||||
X = Math.Min(endPos.X, _newAnnotationStartPos.X),
|
||||
Y = Math.Min(endPos.Y, _newAnnotationStartPos.Y),
|
||||
Left = Math.Min(endPos.X, _newAnnotationStartPos.X),
|
||||
Top = Math.Min(endPos.Y, _newAnnotationStartPos.Y),
|
||||
Confidence = 1
|
||||
});
|
||||
control.UpdateLayout();
|
||||
@@ -415,13 +413,26 @@ public class CanvasEditor : Canvas
|
||||
SetTop(_newAnnotationRect, currentPos.Y);
|
||||
}
|
||||
|
||||
public void CreateDetections(TimeSpan time, IEnumerable<Detection> detections, List<DetectionClass> detectionClasses, Size videoSize)
|
||||
public void CreateDetections(Annotation annotation, List<DetectionClass> detectionClasses, Size mediaSize)
|
||||
{
|
||||
foreach (var detection in detections)
|
||||
var splitTile = annotation.SplitTile;
|
||||
foreach (var detection in annotation.Detections)
|
||||
{
|
||||
var detectionClass = DetectionClass.FromYoloId(detection.ClassNumber, detectionClasses);
|
||||
var canvasLabel = new CanvasLabel(detection, RenderSize, videoSize, detection.Confidence);
|
||||
CreateDetectionControl(detectionClass, time, canvasLabel);
|
||||
CanvasLabel canvasLabel;
|
||||
if (splitTile == null)
|
||||
canvasLabel = new CanvasLabel(detection, RenderSize, mediaSize, detection.Confidence);
|
||||
else
|
||||
{
|
||||
canvasLabel = new CanvasLabel(detection, new Size(Constants.AI_TILE_SIZE, Constants.AI_TILE_SIZE), null, detection.Confidence)
|
||||
.ReframeFromSmall(splitTile);
|
||||
|
||||
//From CurrentMediaSize to Render Size
|
||||
var yoloLabel = new YoloLabel(canvasLabel, mediaSize);
|
||||
canvasLabel = new CanvasLabel(yoloLabel, RenderSize, mediaSize, canvasLabel.Confidence);
|
||||
}
|
||||
|
||||
CreateDetectionControl(detectionClass, annotation.Time, canvasLabel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,8 +440,8 @@ public class CanvasEditor : Canvas
|
||||
{
|
||||
var detectionControl = new DetectionControl(detectionClass, time, AnnotationResizeStart, canvasLabel);
|
||||
detectionControl.MouseDown += AnnotationPositionStart;
|
||||
SetLeft(detectionControl, canvasLabel.X );
|
||||
SetTop(detectionControl, canvasLabel.Y);
|
||||
SetLeft(detectionControl, canvasLabel.Left );
|
||||
SetTop(detectionControl, canvasLabel.Top);
|
||||
Children.Add(detectionControl);
|
||||
CurrentDetections.Add(detectionControl);
|
||||
_newAnnotationRect.Fill = new SolidColorBrush(detectionClass.Color);
|
||||
@@ -472,4 +483,12 @@ public class CanvasEditor : Canvas
|
||||
}
|
||||
|
||||
public void ResetBackground() => Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
|
||||
|
||||
public void ZoomTo(Point point)
|
||||
{
|
||||
SetZoom();
|
||||
var matrix = _matrixTransform.Matrix;
|
||||
matrix.ScaleAt(2, 2, point.X, point.Y);
|
||||
SetZoom(matrix);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public class DetectionControl : Border
|
||||
{
|
||||
var brush = new SolidColorBrush(value.Color.ToConfidenceColor());
|
||||
BorderBrush = brush;
|
||||
BorderThickness = new Thickness(3);
|
||||
BorderThickness = new Thickness(1);
|
||||
foreach (var rect in _resizedRectangles)
|
||||
rect.Stroke = brush;
|
||||
|
||||
@@ -141,7 +141,7 @@ public class DetectionControl : Border
|
||||
var rect = new Rectangle() // small rectangles at the corners and sides
|
||||
{
|
||||
ClipToBounds = false,
|
||||
Margin = new Thickness(-RESIZE_RECT_SIZE),
|
||||
Margin = new Thickness(-1.1 * RESIZE_RECT_SIZE),
|
||||
HorizontalAlignment = ha,
|
||||
VerticalAlignment = va,
|
||||
Width = RESIZE_RECT_SIZE,
|
||||
|
||||
@@ -3,31 +3,33 @@ using Azaion.Common.Database;
|
||||
|
||||
namespace Azaion.Common.DTO;
|
||||
|
||||
public class AnnotationResult
|
||||
{
|
||||
public Annotation Annotation { get; set; }
|
||||
public List<(Color Color, double Confidence)> Colors { get; private set; }
|
||||
// public class AnnotationResult
|
||||
//{
|
||||
//public Annotation Annotation { get; set; }
|
||||
|
||||
|
||||
public string ImagePath { get; set; }
|
||||
public string TimeStr { get; set; }
|
||||
public string ClassName { get; set; }
|
||||
//public string ImagePath { get; set; }
|
||||
//public string TimeStr { get; set; }
|
||||
|
||||
//public List<(Color Color, double Confidence)> Colors { get; private set; }
|
||||
// public string ClassName { get; set; }
|
||||
|
||||
public AnnotationResult(Dictionary<int, DetectionClass> allDetectionClasses, Annotation annotation)
|
||||
{
|
||||
// public AnnotationResult(Dictionary<int, DetectionClass> allDetectionClasses, Annotation annotation)
|
||||
// {
|
||||
|
||||
Annotation = annotation;
|
||||
//Annotation = annotation;
|
||||
|
||||
TimeStr = $"{annotation.Time:h\\:mm\\:ss}";
|
||||
ImagePath = annotation.ImagePath;
|
||||
//TimeStr = $"{annotation.Time:h\\:mm\\:ss}";
|
||||
//ImagePath = annotation.ImagePath;
|
||||
|
||||
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
|
||||
? string.Join(", ", detectionClasses.Select(x => allDetectionClasses[x].UIName))
|
||||
: allDetectionClasses[detectionClasses.FirstOrDefault()].UIName;
|
||||
}
|
||||
}
|
||||
// var detectionClasses = annotation.Detections.Select(x => x.ClassNumber).Distinct().ToList();
|
||||
// ClassName = detectionClasses.Count > 1
|
||||
// ? string.Join(", ", detectionClasses.Select(x => allDetectionClasses[x].UIName))
|
||||
// : allDetectionClasses[detectionClasses.FirstOrDefault()].UIName;
|
||||
//
|
||||
// Colors = annotation.Detections
|
||||
// .Select(d => (allDetectionClasses[d.ClassNumber].Color, d.Confidence))
|
||||
// .ToList();
|
||||
|
||||
// }
|
||||
// }
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using Azaion.Common.Database;
|
||||
|
||||
namespace Azaion.Common.DTO;
|
||||
|
||||
@@ -7,13 +8,12 @@ public class FormState
|
||||
{
|
||||
public MediaFileInfo? CurrentMedia { get; set; }
|
||||
public string MediaName => CurrentMedia?.FName ?? "";
|
||||
|
||||
public string CurrentMrl { get; set; } = null!;
|
||||
|
||||
public Size CurrentMediaSize { get; set; }
|
||||
public TimeSpan CurrentVideoLength { get; set; }
|
||||
|
||||
public TimeSpan? BackgroundTime { get; set; }
|
||||
public int CurrentVolume { get; set; } = 100;
|
||||
public ObservableCollection<AnnotationResult> AnnotationResults { get; set; } = [];
|
||||
public ObservableCollection<Annotation> AnnotationResults { get; set; } = [];
|
||||
public WindowEnum ActiveWindow { get; set; }
|
||||
}
|
||||
+31
-27
@@ -22,52 +22,56 @@ public abstract class Label
|
||||
|
||||
public class CanvasLabel : Label
|
||||
{
|
||||
public double X { get; set; } //left
|
||||
public double Y { get; set; } //top
|
||||
public double Left { get; set; }
|
||||
public double Top { get; set; }
|
||||
public double Width { get; set; }
|
||||
public double Height { get; set; }
|
||||
public double Confidence { get; set; }
|
||||
|
||||
public double Bottom
|
||||
{
|
||||
get => Y + Height;
|
||||
set => Height = value - Y;
|
||||
get => Top + Height;
|
||||
set => Height = value - Top;
|
||||
}
|
||||
|
||||
public double Right
|
||||
{
|
||||
get => X + Width;
|
||||
set => Width = value - X;
|
||||
get => Left + Width;
|
||||
set => Width = value - Left;
|
||||
}
|
||||
|
||||
public double CenterX => Left + Width / 2.0;
|
||||
public double CenterY => Top + Height / 2.0;
|
||||
public Size Size => new(Width, Height);
|
||||
|
||||
public CanvasLabel() { }
|
||||
|
||||
public CanvasLabel(double left, double right, double top, double bottom)
|
||||
{
|
||||
X = left;
|
||||
Y = top;
|
||||
Left = left;
|
||||
Top = top;
|
||||
Width = right - left;
|
||||
Height = bottom - top;
|
||||
Confidence = 1;
|
||||
ClassNumber = -1;
|
||||
}
|
||||
|
||||
public CanvasLabel(int classNumber, double x, double y, double width, double height, double confidence = 1) : base(classNumber)
|
||||
public CanvasLabel(int classNumber, double left, double top, double width, double height, double confidence = 1) : base(classNumber)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Left = left;
|
||||
Top = top;
|
||||
Width = width;
|
||||
Height = height;
|
||||
Confidence = confidence;
|
||||
}
|
||||
|
||||
public CanvasLabel(YoloLabel label, Size canvasSize, Size? videoSize = null, double confidence = 1)
|
||||
public CanvasLabel(YoloLabel label, Size canvasSize, Size? mediaSize = null, double confidence = 1)
|
||||
{
|
||||
var cw = canvasSize.Width;
|
||||
var ch = canvasSize.Height;
|
||||
var canvasAr = cw / ch;
|
||||
var videoAr = videoSize.HasValue
|
||||
? videoSize.Value.Width / videoSize.Value.Height
|
||||
var videoAr = mediaSize.HasValue
|
||||
? mediaSize.Value.Width / mediaSize.Value.Height
|
||||
: canvasAr;
|
||||
|
||||
ClassNumber = label.ClassNumber;
|
||||
@@ -80,8 +84,8 @@ public class CanvasLabel : Label
|
||||
var realHeight = cw / videoAr; //real video height in pixels on canvas
|
||||
var blackStripHeight = (ch - realHeight) / 2.0; //height of black strips at the top and bottom
|
||||
|
||||
X = left * cw;
|
||||
Y = top * realHeight + blackStripHeight;
|
||||
Left = left * cw;
|
||||
Top = top * realHeight + blackStripHeight;
|
||||
Width = label.Width * cw;
|
||||
Height = label.Height * realHeight;
|
||||
}
|
||||
@@ -90,8 +94,8 @@ public class CanvasLabel : Label
|
||||
var realWidth = ch * videoAr; //real video width in pixels on canvas
|
||||
var blackStripWidth = (cw - realWidth) / 2.0; //height of black strips at the top and bottom
|
||||
|
||||
X = left * realWidth + blackStripWidth;
|
||||
Y = top * ch;
|
||||
Left = left * realWidth + blackStripWidth;
|
||||
Top = top * ch;
|
||||
Width = label.Width * realWidth;
|
||||
Height = label.Height * ch;
|
||||
}
|
||||
@@ -99,10 +103,10 @@ public class CanvasLabel : Label
|
||||
}
|
||||
|
||||
public CanvasLabel ReframeToSmall(CanvasLabel smallTile) =>
|
||||
new(ClassNumber, X - smallTile.X, Y - smallTile.Y, Width, Height, Confidence);
|
||||
new(ClassNumber, Left - smallTile.Left, Top - smallTile.Top, Width, Height, Confidence);
|
||||
|
||||
public CanvasLabel ReframeFromSmall(CanvasLabel smallTile) =>
|
||||
new(ClassNumber, X + smallTile.X, Y + smallTile.Y, Width, Height, Confidence);
|
||||
new(ClassNumber, Left + smallTile.Left, Top + smallTile.Top, Width, Height, Confidence);
|
||||
|
||||
}
|
||||
|
||||
@@ -132,13 +136,13 @@ public class YoloLabel : Label
|
||||
public RectangleF ToRectangle() =>
|
||||
new((float)(CenterX - Width / 2.0), (float)(CenterY - Height / 2.0), (float)Width, (float)Height);
|
||||
|
||||
public YoloLabel(CanvasLabel canvasLabel, Size canvasSize, Size? videoSize = null)
|
||||
public YoloLabel(CanvasLabel canvasLabel, Size canvasSize, Size? mediaSize = null)
|
||||
{
|
||||
var cw = canvasSize.Width;
|
||||
var ch = canvasSize.Height;
|
||||
var canvasAr = cw / ch;
|
||||
var videoAr = videoSize.HasValue
|
||||
? videoSize.Value.Width / videoSize.Value.Height
|
||||
var videoAr = mediaSize.HasValue
|
||||
? mediaSize.Value.Width / mediaSize.Value.Height
|
||||
: canvasAr;
|
||||
|
||||
ClassNumber = canvasLabel.ClassNumber;
|
||||
@@ -146,20 +150,20 @@ public class YoloLabel : Label
|
||||
double left, top;
|
||||
if (videoAr > canvasAr) //100% width
|
||||
{
|
||||
left = canvasLabel.X / cw;
|
||||
left = canvasLabel.Left / cw;
|
||||
Width = canvasLabel.Width / cw;
|
||||
var realHeight = cw / videoAr; //real video height in pixels on canvas
|
||||
var blackStripHeight = (ch - realHeight) / 2.0; //height of black strips at the top and bottom
|
||||
top = (canvasLabel.Y - blackStripHeight) / realHeight;
|
||||
top = (canvasLabel.Top - blackStripHeight) / realHeight;
|
||||
Height = canvasLabel.Height / realHeight;
|
||||
}
|
||||
else //100% height
|
||||
{
|
||||
top = canvasLabel.Y / ch;
|
||||
top = canvasLabel.Top / ch;
|
||||
Height = canvasLabel.Height / ch;
|
||||
var realWidth = ch * videoAr; //real video width in pixels on canvas
|
||||
var blackStripWidth = (cw - realWidth) / 2.0; //height of black strips at the top and bottom
|
||||
left = (canvasLabel.X - blackStripWidth) / realWidth;
|
||||
left = (canvasLabel.Left - blackStripWidth) / realWidth;
|
||||
Width = canvasLabel.Width / realWidth;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using Azaion.Common.DTO;
|
||||
using Azaion.Common.DTO.Config;
|
||||
using Azaion.Common.DTO.Queue;
|
||||
@@ -12,12 +13,14 @@ public class Annotation
|
||||
private static string _labelsDir = null!;
|
||||
private static string _imagesDir = null!;
|
||||
private static string _thumbDir = null!;
|
||||
|
||||
public static void InitializeDirs(DirectoriesConfig config)
|
||||
private static Dictionary<int, DetectionClass> _detectionClassesDict;
|
||||
|
||||
public static void Init(DirectoriesConfig config, Dictionary<int, DetectionClass> detectionClassesDict)
|
||||
{
|
||||
_labelsDir = config.LabelsDirectory;
|
||||
_imagesDir = config.ImagesDirectory;
|
||||
_thumbDir = config.ThumbnailsDirectory;
|
||||
_detectionClassesDict = detectionClassesDict;
|
||||
}
|
||||
|
||||
[Key("n")] public string Name { get; set; } = null!;
|
||||
@@ -40,12 +43,64 @@ public class Annotation
|
||||
[Key("lon")]public double Lon { get; set; }
|
||||
|
||||
#region Calculated
|
||||
[IgnoreMember]public List<int> Classes => Detections.Select(x => x.ClassNumber).ToList();
|
||||
[IgnoreMember]public string ImagePath => Path.Combine(_imagesDir, $"{Name}{ImageExtension}");
|
||||
[IgnoreMember]public string LabelPath => Path.Combine(_labelsDir, $"{Name}.txt");
|
||||
[IgnoreMember]public string ThumbPath => Path.Combine(_thumbDir, $"{Name}{Constants.THUMBNAIL_PREFIX}.jpg");
|
||||
[IgnoreMember] public List<int> Classes => Detections.Select(x => x.ClassNumber).ToList();
|
||||
[IgnoreMember] public string ImagePath => Path.Combine(_imagesDir, $"{Name}{ImageExtension}");
|
||||
[IgnoreMember] public string LabelPath => Path.Combine(_labelsDir, $"{Name}.txt");
|
||||
[IgnoreMember] public string ThumbPath => Path.Combine(_thumbDir, $"{Name}{Constants.THUMBNAIL_PREFIX}.jpg");
|
||||
[IgnoreMember] public bool IsSplit => Name.Contains(Constants.SPLIT_SUFFIX);
|
||||
|
||||
private CanvasLabel? _splitTile;
|
||||
[IgnoreMember] public CanvasLabel? SplitTile
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsSplit)
|
||||
return null;
|
||||
if (_splitTile != null)
|
||||
return _splitTile;
|
||||
|
||||
var startCoordIndex = Name.IndexOf(Constants.SPLIT_SUFFIX, StringComparison.Ordinal) + Constants.SPLIT_SUFFIX.Length;
|
||||
var coordsStr = Name.Substring(startCoordIndex, 9).Split('_');
|
||||
_splitTile = new CanvasLabel
|
||||
{
|
||||
Left = double.Parse(coordsStr[0]),
|
||||
Top = double.Parse(coordsStr[1]),
|
||||
Width = Constants.AI_TILE_SIZE,
|
||||
Height = Constants.AI_TILE_SIZE
|
||||
};
|
||||
return _splitTile;
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreMember] public string TimeStr => $"{Time:h\\:mm\\:ss}";
|
||||
|
||||
private List<(Color Color, double Confidence)>? _colors;
|
||||
[IgnoreMember] public List<(Color Color, double Confidence)> Colors => _colors ??= Detections
|
||||
.Select(d => (_detectionClassesDict[d.ClassNumber].Color, d.Confidence))
|
||||
.ToList();
|
||||
|
||||
private string _className;
|
||||
[IgnoreMember] public string ClassName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_className))
|
||||
{
|
||||
var detectionClasses = Detections.Select(x => x.ClassNumber).Distinct().ToList();
|
||||
_className = detectionClasses.Count > 1
|
||||
? string.Join(", ", detectionClasses.Select(x => _detectionClassesDict[x].UIName))
|
||||
: _detectionClassesDict[detectionClasses.FirstOrDefault()].UIName;
|
||||
}
|
||||
return _className;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion Calculated
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Data.SQLite;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using Azaion.Common.DTO;
|
||||
using Azaion.Common.DTO.Config;
|
||||
@@ -48,7 +49,7 @@ public class DbFactory : IDbFactory
|
||||
.UseDataProvider(SQLiteTools.GetDataProvider())
|
||||
.UseConnection(_memoryConnection)
|
||||
.UseMappingSchema(AnnotationsDbSchemaHolder.MappingSchema)
|
||||
;//.UseTracing(TraceLevel.Info, t => logger.LogInformation(t.SqlText));
|
||||
.UseTracing(TraceLevel.Info, t => logger.LogInformation(t.SqlText));
|
||||
|
||||
|
||||
_fileConnection = new SQLiteConnection(FileConnStr);
|
||||
|
||||
@@ -94,6 +94,7 @@ public class AnnotationService : IAnnotationService
|
||||
await SaveAnnotationInner(
|
||||
msg.CreatedDate,
|
||||
msg.OriginalMediaName,
|
||||
msg.Name,
|
||||
msg.Time,
|
||||
JsonConvert.DeserializeObject<List<Detection>>(msg.Detections) ?? [],
|
||||
msg.Source,
|
||||
@@ -136,16 +137,16 @@ public class AnnotationService : IAnnotationService
|
||||
public async Task<Annotation> SaveAnnotation(AnnotationImage a, CancellationToken ct = default)
|
||||
{
|
||||
a.Time = TimeSpan.FromMilliseconds(a.Milliseconds);
|
||||
return await SaveAnnotationInner(DateTime.UtcNow, a.OriginalMediaName, a.Time, a.Detections.ToList(),
|
||||
return await SaveAnnotationInner(DateTime.UtcNow, a.OriginalMediaName, a.Name, a.Time, a.Detections.ToList(),
|
||||
SourceEnum.AI, new MemoryStream(a.Image), _api.CurrentUser.Role, _api.CurrentUser.Email, token: ct);
|
||||
}
|
||||
|
||||
//Manual
|
||||
public async Task<Annotation> SaveAnnotation(string originalMediaName, TimeSpan time, List<Detection> detections, Stream? stream = null, CancellationToken token = default) =>
|
||||
await SaveAnnotationInner(DateTime.UtcNow, originalMediaName, time, detections, SourceEnum.Manual, stream,
|
||||
public async Task<Annotation> SaveAnnotation(string originalMediaName, string annotationName, TimeSpan time, List<Detection> detections, Stream? stream = null, CancellationToken token = default) =>
|
||||
await SaveAnnotationInner(DateTime.UtcNow, originalMediaName, annotationName, time, detections, SourceEnum.Manual, stream,
|
||||
_api.CurrentUser.Role, _api.CurrentUser.Email, token: token);
|
||||
|
||||
private async Task<Annotation> SaveAnnotationInner(DateTime createdDate, string originalMediaName, TimeSpan time,
|
||||
private async Task<Annotation> SaveAnnotationInner(DateTime createdDate, string originalMediaName, string annotationName, TimeSpan time,
|
||||
List<Detection> detections, SourceEnum source, Stream? stream,
|
||||
RoleEnum userRole,
|
||||
string createdEmail,
|
||||
@@ -153,21 +154,20 @@ public class AnnotationService : IAnnotationService
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var status = AnnotationStatus.Created;
|
||||
var fName = originalMediaName.ToTimeName(time);
|
||||
var annotation = await _dbFactory.RunWrite(async db =>
|
||||
{
|
||||
var ann = await db.Annotations
|
||||
.LoadWith(x => x.Detections)
|
||||
.FirstOrDefaultAsync(x => x.Name == fName, token: token);
|
||||
.FirstOrDefaultAsync(x => x.Name == annotationName, token: token);
|
||||
|
||||
await db.Detections.DeleteAsync(x => x.AnnotationName == fName, token: token);
|
||||
await db.Detections.DeleteAsync(x => x.AnnotationName == annotationName, token: token);
|
||||
|
||||
if (ann != null) //Annotation is already exists
|
||||
{
|
||||
status = AnnotationStatus.Edited;
|
||||
|
||||
var annotationUpdatable = db.Annotations
|
||||
.Where(x => x.Name == fName)
|
||||
.Where(x => x.Name == annotationName)
|
||||
.Set(x => x.Source, source);
|
||||
|
||||
if (userRole.IsValidator() && source == SourceEnum.Manual)
|
||||
@@ -188,7 +188,7 @@ public class AnnotationService : IAnnotationService
|
||||
ann = new Annotation
|
||||
{
|
||||
CreatedDate = createdDate,
|
||||
Name = fName,
|
||||
Name = annotationName,
|
||||
OriginalMediaName = originalMediaName,
|
||||
Time = time,
|
||||
ImageExtension = Constants.JPG_EXT,
|
||||
@@ -264,6 +264,6 @@ public class AnnotationService : IAnnotationService
|
||||
public interface IAnnotationService
|
||||
{
|
||||
Task<Annotation> SaveAnnotation(AnnotationImage a, CancellationToken ct = default);
|
||||
Task<Annotation> SaveAnnotation(string originalMediaName, TimeSpan time, List<Detection> detections, Stream? stream = null, CancellationToken token = default);
|
||||
Task<Annotation> SaveAnnotation(string originalMediaName, string annotationName, TimeSpan time, List<Detection> detections, Stream? stream = null, CancellationToken token = default);
|
||||
Task ValidateAnnotations(List<string> annotationNames, bool fromQueue = false, CancellationToken token = default);
|
||||
}
|
||||
@@ -237,11 +237,11 @@ public class GalleryService(
|
||||
.ToList();
|
||||
if (annotation.Detections.Any())
|
||||
{
|
||||
var labelsMinX = labels.Min(x => x.X);
|
||||
var labelsMaxX = labels.Max(x => x.X + x.Width);
|
||||
var labelsMinX = labels.Min(x => x.Left);
|
||||
var labelsMaxX = labels.Max(x => x.Left + x.Width);
|
||||
|
||||
var labelsMinY = labels.Min(x => x.Y);
|
||||
var labelsMaxY = labels.Max(x => x.Y + x.Height);
|
||||
var labelsMinY = labels.Min(x => x.Top);
|
||||
var labelsMaxY = labels.Max(x => x.Top + x.Height);
|
||||
|
||||
var labelsHeight = labelsMaxY - labelsMinY + 2 * border;
|
||||
var labelsWidth = labelsMaxX - labelsMinX + 2 * border;
|
||||
@@ -270,7 +270,7 @@ public class GalleryService(
|
||||
var color = _annotationConfig.DetectionClassesDict[label.ClassNumber].Color;
|
||||
var brush = new SolidBrush(Color.FromArgb(color.A, color.R, color.G, color.B));
|
||||
|
||||
g.DrawRectangle(new Pen(brush, width: 3), (float)((label.X - frameX) / scale), (float)((label.Y - frameY) / scale), (float)(label.Width / scale), (float)(label.Height / scale));
|
||||
g.DrawRectangle(new Pen(brush, width: 3), (float)((label.Left - frameX) / scale), (float)((label.Top - frameY) / scale), (float)(label.Width / scale), (float)(label.Height / scale));
|
||||
}
|
||||
|
||||
bitmap.Save(annotation.ThumbPath, ImageFormat.Jpeg);
|
||||
@@ -291,10 +291,10 @@ public class GalleryService(
|
||||
var color = detClass.Color;
|
||||
var brush = new SolidBrush(Color.FromArgb(color.A, color.R, color.G, color.B));
|
||||
var det = new CanvasLabel(detection, new Size(originalImage.Width, originalImage.Height));
|
||||
g.DrawRectangle(new Pen(brush, width: 3), (float)det.X, (float)det.Y, (float)det.Width, (float)det.Height);
|
||||
g.DrawRectangle(new Pen(brush, width: 3), (float)det.Left, (float)det.Top, (float)det.Width, (float)det.Height);
|
||||
|
||||
var label = detection.Confidence >= 0.995 ? detClass.UIName : $"{detClass.UIName}: {detection.Confidence * 100:F0}%";
|
||||
g.DrawTextBox(label, new PointF((float)(det.X + det.Width / 2.0), (float)(det.Y - 24)), brush, Brushes.Black);
|
||||
g.DrawTextBox(label, new PointF((float)(det.Left + det.Width / 2.0), (float)(det.Top - 24)), brush, Brushes.Black);
|
||||
}
|
||||
|
||||
var imagePath = Path.Combine(_dirConfig.ResultsDirectory, $"{annotation.Name}{Constants.RESULT_PREFIX}.jpg");
|
||||
|
||||
@@ -49,7 +49,7 @@ public class InferenceClient : IInferenceClient
|
||||
Arguments = $"-p {_inferenceClientConfig.ZeroMqPort} -lp {_loaderClientConfig.ZeroMqPort} -a {_inferenceClientConfig.ApiUrl}",
|
||||
CreateNoWindow = true
|
||||
};
|
||||
process.Start();
|
||||
//process.Start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -18,10 +18,8 @@ public class TileResult
|
||||
|
||||
public static class TileProcessor
|
||||
{
|
||||
private const int MaxTileWidth = 1280;
|
||||
private const int MaxTileHeight = 1280;
|
||||
private const int Border = 10;
|
||||
|
||||
public const int BORDER = 10;
|
||||
|
||||
public static List<TileResult> Split(Size originalSize, List<CanvasLabel> detections, CancellationToken cancellationToken)
|
||||
{
|
||||
var results = new List<TileResult>();
|
||||
@@ -30,7 +28,7 @@ public static class TileProcessor
|
||||
while (processingDetectionList.Count > 0 && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var topMostDetection = processingDetectionList
|
||||
.OrderBy(d => d.Y)
|
||||
.OrderBy(d => d.Top)
|
||||
.First();
|
||||
|
||||
var result = GetDetectionsInTile(originalSize, topMostDetection, processingDetectionList);
|
||||
@@ -42,11 +40,8 @@ public static class TileProcessor
|
||||
|
||||
private static TileResult GetDetectionsInTile(Size originalSize, CanvasLabel startDet, List<CanvasLabel> allDetections)
|
||||
{
|
||||
var tile = new CanvasLabel(
|
||||
left: Math.Max(startDet.X - Border, 0),
|
||||
right: Math.Min(startDet.Right + Border, originalSize.Width),
|
||||
top: Math.Max(startDet.Y - Border, 0),
|
||||
bottom: Math.Min(startDet.Bottom + Border, originalSize.Height));
|
||||
var tile = new CanvasLabel(startDet.Left, startDet.Right, startDet.Top, startDet.Bottom);
|
||||
var maxSize = new List<double> { startDet.Width + BORDER, startDet.Height + BORDER, Constants.AI_TILE_SIZE }.Max();
|
||||
var selectedDetections = new List<CanvasLabel>{startDet};
|
||||
|
||||
foreach (var det in allDetections)
|
||||
@@ -55,26 +50,26 @@ public static class TileProcessor
|
||||
continue;
|
||||
|
||||
var commonTile = new CanvasLabel(
|
||||
left: Math.Max(Math.Min(tile.X, det.X) - Border, 0),
|
||||
right: Math.Min(Math.Max(tile.Right, det.Right) + Border, originalSize.Width),
|
||||
top: Math.Max(Math.Min(tile.Y, det.Y) - Border, 0),
|
||||
bottom: Math.Min(Math.Max(tile.Bottom, det.Bottom) + Border, originalSize.Height)
|
||||
left: Math.Min(tile.Left, det.Left),
|
||||
right: Math.Max(tile.Right, det.Right),
|
||||
top: Math.Min(tile.Top, det.Top),
|
||||
bottom: Math.Max(tile.Bottom, det.Bottom)
|
||||
);
|
||||
|
||||
if (commonTile.Width > MaxTileWidth || commonTile.Height > MaxTileHeight)
|
||||
|
||||
if (commonTile.Width + BORDER > maxSize || commonTile.Height + BORDER > maxSize)
|
||||
continue;
|
||||
|
||||
tile = commonTile;
|
||||
selectedDetections.Add(det);
|
||||
}
|
||||
|
||||
//normalization, width and height should be at least half of 1280px
|
||||
tile.Width = Math.Max(tile.Width, MaxTileWidth / 2.0);
|
||||
tile.Height = Math.Max(tile.Height, MaxTileHeight / 2.0);
|
||||
|
||||
//boundaries check after normalization
|
||||
tile.Right = Math.Min(tile.Right, originalSize.Width);
|
||||
tile.Bottom = Math.Min(tile.Bottom, originalSize.Height);
|
||||
// boundary-aware centering
|
||||
var centerX = selectedDetections.Average(x => x.CenterX);
|
||||
var centerY = selectedDetections.Average(d => d.CenterY);
|
||||
tile.Width = maxSize;
|
||||
tile.Height = maxSize;
|
||||
tile.Left = Math.Max(0, Math.Min(originalSize.Width - maxSize, centerX - tile.Width / 2.0));
|
||||
tile.Top = Math.Max(0, Math.Min(originalSize.Height - maxSize, centerY - tile.Height / 2.0));
|
||||
|
||||
return new TileResult(tile, selectedDetections);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user