using System.Drawing; using System.Drawing.Imaging; using System.Text.Json; using View_by_Distance.Metadata.Models; using View_by_Distance.Resize.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless; namespace View_by_Distance.Instance.Models; /// // *.png /// internal class D2_FaceParts { private readonly Serilog.ILogger? _Log; private readonly string _FilenameExtension; private readonly Configuration _Configuration; private readonly ImageCodecInfo _ImageCodecInfo; private readonly EncoderParameters _EncoderParameters; internal D2_FaceParts(Configuration configuration, ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) { _Configuration = configuration; _ImageCodecInfo = imageCodecInfo; _EncoderParameters = encoderParameters; _FilenameExtension = filenameExtension; _Log = Serilog.Log.ForContext(); } public override string ToString() { string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); return result; } #pragma warning disable CA1416 private static Bitmap RotateBitmap(Image image, float angle) { Bitmap result; Bitmap bitmap = new(image); result = D_Face.RotateBitmap(bitmap, angle); if (bitmap is not null) bitmap.Dispose(); return result; } private void SaveFaceParts(int pointSize, IFileHolder resizedFileHolder, bool saveRotated, List<(Face, string, string)> collection) { int x; int y; double? α; int width; int height; Bitmap rotated; foreach ((Face face, string fileName, string rotatedFileName) in collection) { if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) continue; try { using (Image image = Image.FromFile(resizedFileHolder.FullName)) { using Graphics graphic = Graphics.FromImage(image); if (face.FaceParts is null || !face.FaceParts.Any()) { if (face.Location is null) continue; width = face.Location.Right - face.Location.Left; height = face.Location.Bottom - face.Location.Top; graphic.DrawEllipse(Pens.Red, face.Location.Left, face.Location.Top, width, height); } else { foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts) { foreach (FacePoint facePoint in facePoints) { if (face.Location is null) continue; graphic.DrawEllipse(Pens.GreenYellow, face.Location.Left + facePoint.X - pointSize, face.Location.Top + facePoint.Y - pointSize, pointSize * 2, pointSize * 2); } if (facePart == FacePart.Chin) continue; if (facePoints.Length < 3) continue; x = (int)(from l in facePoints select l.X).Average(); y = (int)(from l in facePoints select l.Y).Average(); if (face.Location is null) continue; graphic.DrawEllipse(Pens.Purple, face.Location.Left + x - pointSize, face.Location.Top + y - pointSize, pointSize * 2, pointSize * 2); } } image.Save(fileName, _ImageCodecInfo, _EncoderParameters); } if (saveRotated && face.FaceParts is not null) { α = Shared.Models.Stateless.Methods.IFace.Getα(face.FaceParts); if (α is null) continue; using Image image = Image.FromFile(resizedFileHolder.FullName); rotated = RotateBitmap(image, (float)α.Value); if (rotated is not null) { rotated.Save(rotatedFileName, _ImageCodecInfo, _EncoderParameters); rotated.Dispose(); } } } catch (Exception) { } } } #pragma warning restore CA1416 internal void SaveFaceLandmarkImages(string d2ResultsFullGroupDirectory, string sourceDirectory, List> subFileTuples, List parseExceptions, Item item, List faceCollection, bool saveRotated) { if (item.ImageFileHolder is null) throw new NullReferenceException(nameof(item.ImageFileHolder)); if (item.ResizedFileHolder is null) throw new NullReferenceException(nameof(item.ResizedFileHolder)); FileInfo fileInfo; bool check = false; string parentCheck; const int pointSize = 2; FileInfo rotatedFileInfo; DateTime? dateTime = null; double deterministicHashCodeKey; long ticks = DateTime.Now.Ticks; bool updateDateWhenMatches = false; List angleBracketCollection = new(); List<(Face, string, string)> collection = new(); angleBracketCollection.AddRange(Property.Models.Stateless.IResult.GetDirectoryInfoCollection( _Configuration.PropertyConfiguration, sourceDirectory, d2ResultsFullGroupDirectory, contentDescription: "n x 2 gif file(s) for each face found", singletonDescription: string.Empty, collectionDescription: string.Empty, converted: false)); string facesDirectory = Path.Combine(angleBracketCollection[0].Replace("<>", "()"), item.ImageFileHolder.NameWithoutExtension); string[] changesFrom = new string[] { nameof(Property.Models.A_Property), nameof(B_Metadata), nameof(C_Resize), nameof(D_Face) }; List dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList(); if (!Directory.Exists(facesDirectory)) _ = Directory.CreateDirectory(facesDirectory); foreach (Face face in faceCollection) { if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) { collection.Add(new(face, string.Empty, string.Empty)); continue; } deterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetDeterministicHashCodeKey(item, face); fileInfo = new FileInfo(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}")); if (!fileInfo.Exists) { if (fileInfo.Directory?.Parent is null) throw new Exception(); parentCheck = Path.Combine(fileInfo.Directory.Parent.FullName, fileInfo.Name); if (File.Exists(parentCheck)) File.Delete(parentCheck); } if (string.IsNullOrEmpty(fileInfo.DirectoryName)) continue; rotatedFileInfo = new FileInfo(Path.Combine(fileInfo.DirectoryName, $"{deterministicHashCodeKey} - R{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}")); collection.Add(new(face, fileInfo.FullName, rotatedFileInfo.FullName)); if (check) continue; else if (_Configuration.OverrideForFaceLandmarkImages) check = true; else if (!fileInfo.Exists) check = true; else if (saveRotated && !rotatedFileInfo.Exists) check = true; else if (dateTimes.Any() && dateTimes.Max() > fileInfo.LastWriteTime) check = true; if (check && !updateDateWhenMatches) { updateDateWhenMatches = dateTimes.Any() && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime; dateTime = !updateDateWhenMatches ? null : dateTimes.Max(); } } if (check) SaveFaceParts(pointSize, item.ResizedFileHolder, saveRotated, collection); } }