using System.Drawing; using System.Globalization; using System.IO; using Newtonsoft.Json; using Size = System.Windows.Size; namespace Azaion.Common.DTO; public abstract class Label { [JsonProperty(PropertyName = "cl")] public int ClassNumber { get; set; } protected Label() { } protected Label(int classNumber) { ClassNumber = classNumber; } } public class CanvasLabel : Label { public double X { get; set; } public double Y { get; set; } public double Width { get; set; } public double Height { get; set; } public double? Probability { get; } public CanvasLabel() { } public CanvasLabel(int classNumber, double x, double y, double width, double height, double? probability = null) : base(classNumber) { X = x; Y = y; Width = width; Height = height; Probability = probability; } public CanvasLabel(YoloLabel label, Size canvasSize, Size videoSize, double? probability = null) { var cw = canvasSize.Width; var ch = canvasSize.Height; var canvasAr = cw / ch; var videoAr = videoSize.Width / videoSize.Height; ClassNumber = label.ClassNumber; var left = label.CenterX - label.Width / 2; var top = label.CenterY - label.Height / 2; if (videoAr > canvasAr) //100% width { 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; Width = label.Width * cw; Height = label.Height * realHeight; } else //100% height { 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; Width = label.Width * realWidth; Height = label.Height * ch; } Probability = probability; } } public class YoloLabel : Label { [JsonProperty(PropertyName = "x")] public double CenterX { get; set; } [JsonProperty(PropertyName = "y")] public double CenterY { get; set; } [JsonProperty(PropertyName = "w")] public double Width { get; set; } [JsonProperty(PropertyName = "h")] public double Height { get; set; } public YoloLabel() { } public YoloLabel(int classNumber, double centerX, double centerY, double width, double height) : base(classNumber) { CenterX = centerX; CenterY = centerY; Width = width; Height = height; } 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) { var cw = canvasSize.Width; var ch = canvasSize.Height; var canvasAr = cw / ch; var videoAr = videoSize.Width / videoSize.Height; ClassNumber = canvasLabel.ClassNumber; double left, top; if (videoAr > canvasAr) //100% width { left = canvasLabel.X / 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; Height = canvasLabel.Height / realHeight; } else //100% height { top = canvasLabel.Y / 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; Width = canvasLabel.Width / realWidth; } CenterX = left + Width / 2.0; CenterY = top + Height / 2.0; } public static YoloLabel? Parse(string s) { if (string.IsNullOrEmpty(s)) return null; var strings = s.Replace(',', '.').Split(' '); if (strings.Length != 5) throw new Exception("Wrong labels format!"); var res = new YoloLabel { ClassNumber = int.Parse(strings[0], CultureInfo.InvariantCulture), CenterX = double.Parse(strings[1], CultureInfo.InvariantCulture), CenterY = double.Parse(strings[2], CultureInfo.InvariantCulture), Width = double.Parse(strings[3], CultureInfo.InvariantCulture), Height = double.Parse(strings[4], CultureInfo.InvariantCulture) }; return res; } public static async Task> ReadFromFile(string filename, CancellationToken cancellationToken = default) { var str = await File.ReadAllTextAsync(filename, cancellationToken); return str.Split('\n') .Select(Parse) .Where(ann => ann != null) .ToList()!; } public static async Task WriteToFile(IEnumerable labels, string filename, CancellationToken cancellationToken = default) { var labelsStr = string.Join(Environment.NewLine, labels.Select(x => x.ToString())); await File.WriteAllTextAsync(filename, labelsStr, cancellationToken); } public override string ToString() => $"{ClassNumber} {CenterX:F5} {CenterY:F5} {Width:F5} {Height:F5}".Replace(',', '.'); } public class Detection : YoloLabel { public Detection(YoloLabel label, double? probability = null) { ClassNumber = label.ClassNumber; CenterX = label.CenterX; CenterY = label.CenterY; Height = label.Height; Width = label.Width; Probability = probability; } public double? Probability { get; set; } }