Files
annotations/Azaion.Common/DTO/Label.cs
T
Oleksandr Bezdieniezhnykh 4780e8c61c fix detection label
fix schema migrator for enums
2025-08-13 10:12:25 +03:00

244 lines
7.9 KiB
C#

using System.Drawing;
using System.Globalization;
using System.IO;
using MessagePack;
using Newtonsoft.Json;
using Size = System.Windows.Size;
namespace Azaion.Common.DTO;
[MessagePackObject]
public abstract class Label
{
[JsonProperty(PropertyName = "cl")][Key("c")] public int ClassNumber { get; set; }
protected Label() { }
protected Label(int classNumber)
{
ClassNumber = classNumber;
}
}
public class CanvasLabel : Label
{
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 => Top + Height;
set => Height = value - Top;
}
public double Right
{
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)
{
Left = left;
Top = top;
Width = right - left;
Height = bottom - top;
Confidence = 1;
ClassNumber = -1;
}
public CanvasLabel(int classNumber, double left, double top, double width, double height, double confidence = 1) : base(classNumber)
{
Left = left;
Top = top;
Width = width;
Height = height;
Confidence = confidence;
}
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 = mediaSize.HasValue
? mediaSize.Value.Width / mediaSize.Value.Height
: canvasAr;
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
Left = left * cw;
Top = 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
Left = left * realWidth + blackStripWidth;
Top = top * ch;
Width = label.Width * realWidth;
Height = label.Height * ch;
}
Confidence = confidence;
}
public CanvasLabel ReframeToSmall(CanvasLabel smallTile) =>
new(ClassNumber, Left - smallTile.Left, Top - smallTile.Top, Width, Height, Confidence);
public CanvasLabel ReframeFromSmall(CanvasLabel smallTile) =>
new(ClassNumber, Left + smallTile.Left, Top + smallTile.Top, Width, Height, Confidence);
}
[MessagePackObject]
public class YoloLabel : Label
{
[JsonProperty(PropertyName = "x")][Key("x")] public double CenterX { get; set; }
[JsonProperty(PropertyName = "y")][Key("y")] public double CenterY { get; set; }
[JsonProperty(PropertyName = "w")][Key("w")] public double Width { get; set; }
[JsonProperty(PropertyName = "h")][Key("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? mediaSize = null)
{
var cw = canvasSize.Width;
var ch = canvasSize.Height;
var canvasAr = cw / ch;
var videoAr = mediaSize.HasValue
? mediaSize.Value.Width / mediaSize.Value.Height
: canvasAr;
ClassNumber = canvasLabel.ClassNumber;
double left, top;
if (videoAr > canvasAr) //100% width
{
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.Top - blackStripHeight) / realHeight;
Height = canvasLabel.Height / realHeight;
}
else //100% height
{
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.Left - 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!");
if (strings.Length > 5)
strings = strings[..5];
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<List<YoloLabel>> ReadFromFile(string filename, CancellationToken cancellationToken = default)
{
var str = await File.ReadAllTextAsync(filename, cancellationToken);
return Deserialize(str);
}
public static async Task WriteToFile(IEnumerable<YoloLabel> labels, string filename, CancellationToken cancellationToken = default)
{
var labelsStr = Serialize(labels);
await File.WriteAllTextAsync(filename, labelsStr, cancellationToken);
}
public static string Serialize(IEnumerable<YoloLabel> labels) =>
string.Join(Environment.NewLine, labels.Select(x => x.ToString()));
public static List<YoloLabel> Deserialize(string str) =>
str.Split('\n')
.Select(Parse)
.Where(ann => ann != null)
.ToList()!;
public override string ToString() => $"{ClassNumber} {CenterX:F5} {CenterY:F5} {Width:F5} {Height:F5}".Replace(',', '.');
}
[MessagePackObject]
public class Detection : YoloLabel
{
[JsonProperty(PropertyName = "an")][Key("an")] public string AnnotationName { get; set; } = null!;
[JsonProperty(PropertyName = "p")][Key("p")] public double Confidence { get; set; }
[JsonProperty(PropertyName = "dn")][Key("dn")] public string Description { get; set; }
[JsonProperty(PropertyName = "af")][Key("af")] public AffiliationEnum Affiliation { get; set; }
//For db & serialization
public Detection(){}
public Detection(string annotationName, YoloLabel label, string description = "", double confidence = 1)
{
AnnotationName = annotationName;
Description = description;
ClassNumber = label.ClassNumber;
CenterX = label.CenterX;
CenterY = label.CenterY;
Height = label.Height;
Width = label.Width;
Confidence = confidence;
}
}