182 lines
8.0 KiB
C#
182 lines
8.0 KiB
C#
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;
|
||
|
||
/// <summary>
|
||
// *.png
|
||
/// </summary>
|
||
internal class D2_FaceParts
|
||
{
|
||
|
||
protected readonly string _FilenameExtension;
|
||
public string FilenameExtension => _FilenameExtension;
|
||
|
||
private readonly Serilog.ILogger? _Log;
|
||
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<D2_FaceParts>();
|
||
}
|
||
|
||
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 facesDirectory, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, Item item, List<Face> 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;
|
||
long ticks = DateTime.Now.Ticks;
|
||
bool updateDateWhenMatches = false;
|
||
string deterministicHashCodeKeyDisplay;
|
||
List<(Face, string, string)> collection = new();
|
||
string[] changesFrom = new string[] { nameof(Property.Models.A_Property), nameof(B_Metadata), nameof(C_Resize), nameof(D_Face) };
|
||
List<DateTime> 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 (item.Property?.Id is null || face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null)
|
||
{
|
||
collection.Add(new(face, string.Empty, string.Empty));
|
||
continue;
|
||
}
|
||
deterministicHashCodeKeyDisplay = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKeyDisplay(item.Property.Id.Value, face.Location.NormalizedPixelPercentage.Value);
|
||
fileInfo = new FileInfo(Path.Combine(facesDirectory, $"{deterministicHashCodeKeyDisplay}{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, $"{deterministicHashCodeKeyDisplay} - 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);
|
||
}
|
||
|
||
} |