mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 21:46:30 +00:00
189 lines
6.0 KiB
C#
189 lines
6.0 KiB
C#
using System.Drawing;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using Newtonsoft.Json;
|
|
using Size = System.Windows.Size;
|
|
|
|
namespace Azaion.Annotator.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<List<YoloLabel>> 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<YoloLabel> 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; }
|
|
} |