using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; using Azaion.Annotator.DTO; using Color = System.Drawing.Color; using Size = System.Windows.Size; namespace Azaion.Annotator; public class GalleryManager : IGalleryManager { private readonly Config _config; public int ThumbnailsCount { get; set; } public int ImagesCount { get; set; } public GalleryManager(Config config) { _config = config; } public async Task RefreshThumbnails(CancellationToken cancellationToken) { var dir = new DirectoryInfo(_config.ThumbnailsDirectory); if (!dir.Exists) Directory.CreateDirectory(_config.ThumbnailsDirectory); var prefixLen = Config.ThumbnailPrefix.Length; var thumbnailsDir = new DirectoryInfo(_config.ThumbnailsDirectory); var thumbnails = thumbnailsDir.GetFiles() .Select(x => Path.GetFileNameWithoutExtension(x.Name)[..^prefixLen]) .GroupBy(x => x) .Select(gr => gr.Key) .ToHashSet(); ThumbnailsCount = thumbnails.Count; var files = new DirectoryInfo(_config.ImagesDirectory).GetFiles(); ImagesCount = files.Length; foreach (var img in files) { var imgName = Path.GetFileNameWithoutExtension(img.Name); if (thumbnails.Contains(imgName)) continue; var bitmap = await GenerateThumbnail(img, cancellationToken); var thumbnailName = Path.Combine(thumbnailsDir.FullName, $"{imgName}{Config.ThumbnailPrefix}.jpg"); bitmap.Save(thumbnailName, ImageFormat.Jpeg); ThumbnailsCount++; } } private async Task GenerateThumbnail(FileInfo img, CancellationToken cancellationToken) { var width = (int)_config.ThumbnailConfig.Size.Width; var height = (int)_config.ThumbnailConfig.Size.Height; var imgName = Path.GetFileNameWithoutExtension(img.Name); var labelName = Path.Combine(_config.LabelsDirectory, $"{imgName}.txt"); var originalImage = Image.FromFile(img.FullName); var bitmap = new Bitmap(width, height); using var g = Graphics.FromImage(bitmap); g.CompositingQuality = CompositingQuality.HighSpeed; g.SmoothingMode = SmoothingMode.HighSpeed; g.InterpolationMode = InterpolationMode.Default; var size = new Size(originalImage.Width, originalImage.Height); var labels = (await YoloLabel.ReadFromFile(labelName, cancellationToken)) .Select(x => new CanvasLabel(x, size, size)) .ToList(); var thumbWhRatio = width / (float)height; var border = _config.ThumbnailConfig.Border; var labelsMinX = labels.Any() ? labels.Min(x => x.X); var labelsMaxX = labels.Max(x => x.X + x.Width); var labelsMinY = labels.Min(x => x.Y); var labelsMaxY = labels.Max(x => x.Y + x.Height); var labelsHeight = labelsMaxY - labelsMinY + 2 * border; var labelsWidth = labelsMaxX - labelsMinX + 2 * border; var frameHeight = 0.0; var frameWidth = 0.0; var frameX = 0.0; var frameY = 0.0; if (labelsWidth / labelsHeight > thumbWhRatio) { frameWidth = labelsWidth; frameHeight = Math.Min(labelsWidth / thumbWhRatio, size.Height); frameX = Math.Max(0, labelsMinX - border); frameY = Math.Max(0, 0.5 * (labelsMinY + labelsMaxY - frameHeight) - border); } else { frameHeight = labelsHeight; frameWidth = Math.Min(labelsHeight * thumbWhRatio, size.Width); frameY = Math.Max(0, labelsMinY - border); frameX = Math.Max(0, 0.5 * (labelsMinX + labelsMaxX - frameWidth) - border); } var scale = frameHeight / height; g.DrawImage(originalImage, new Rectangle(0, 0, width, height), new RectangleF((float)frameX, (float)frameY, (float)frameWidth, (float)frameHeight), GraphicsUnit.Pixel); foreach (var label in labels) { var color = _config.AnnotationClassesDict[label.ClassNumber].Color; var brush = new SolidBrush(Color.FromArgb(color.A, color.R, color.G, color.B)); var rectangle = new RectangleF((float)((label.X - frameX) / scale), (float)((label.Y - frameY) / scale), (float)(label.Width / scale), (float)(label.Height / scale)); g.FillRectangle(brush, rectangle); } return bitmap; } } public interface IGalleryManager { int ThumbnailsCount { get; set; } int ImagesCount { get; set; } Task RefreshThumbnails(CancellationToken cancellationToken); }