627 lines
30 KiB
C#
627 lines
30 KiB
C#
using System.Drawing;
|
||
using System.Drawing.Drawing2D;
|
||
using System.Text.Json;
|
||
using System.Text.Json.Serialization;
|
||
using System.Text.RegularExpressions;
|
||
using View_by_Distance.FaceRecognitionDotNet;
|
||
using View_by_Distance.Metadata.Models;
|
||
using View_by_Distance.Property.Models;
|
||
using View_by_Distance.Resize.Models;
|
||
using View_by_Distance.Shared.Models;
|
||
using View_by_Distance.Shared.Models.Methods;
|
||
using View_by_Distance.Shared.Models.Stateless;
|
||
using WindowsShortcutFactory;
|
||
|
||
namespace View_by_Distance.Instance.Models;
|
||
|
||
/// <summary>
|
||
// List<D_Faces>
|
||
/// </summary>
|
||
public class D_Face : Shared.Models.Properties.IFace, IFace
|
||
{
|
||
|
||
internal List<string> AngleBracketCollection { get; }
|
||
|
||
private readonly Model _Model;
|
||
private readonly string _ArgZero;
|
||
private readonly Serilog.ILogger? _Log;
|
||
private readonly ModelParameter _ModelParameter;
|
||
private readonly PredictorModel _PredictorModel;
|
||
private readonly Configuration _Configuration;
|
||
private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions;
|
||
|
||
protected double? _Α;
|
||
protected DateTime _DateTime;
|
||
protected Shared.Models.FaceEncoding _FaceEncoding;
|
||
protected Dictionary<string, FacePoint[]> _FaceLandmarks;
|
||
protected Location _Location;
|
||
protected int? _LocationIndex;
|
||
protected OutputResolution _OutputResolution;
|
||
protected bool _Populated;
|
||
protected string _RelativePath;
|
||
public double? α => _Α;
|
||
public DateTime DateTime => _DateTime;
|
||
public Shared.Models.FaceEncoding FaceEncoding => _FaceEncoding;
|
||
public Dictionary<string, FacePoint[]> FaceLandmarks => _FaceLandmarks;
|
||
public OutputResolution OutputResolution => _OutputResolution;
|
||
public Location Location => _Location;
|
||
public int? LocationIndex => _LocationIndex;
|
||
public bool Populated => _Populated;
|
||
public string RelativePath => _RelativePath;
|
||
|
||
#nullable disable
|
||
|
||
[JsonConstructor]
|
||
public D_Face(double? α, DateTime dateTime, Shared.Models.FaceEncoding faceEncoding, Dictionary<string, FacePoint[]> faceLandmarks, Location location, int? locationIndex, OutputResolution outputResolution, bool populated, string relativePath)
|
||
{
|
||
_Α = α;
|
||
_DateTime = dateTime;
|
||
_FaceEncoding = faceEncoding;
|
||
_FaceLandmarks = faceLandmarks;
|
||
_Location = location;
|
||
_LocationIndex = locationIndex;
|
||
_OutputResolution = outputResolution;
|
||
_Populated = populated;
|
||
_RelativePath = relativePath;
|
||
}
|
||
|
||
internal D_Face(Configuration configuration, string argZero, Model model, ModelParameter modelParameter, PredictorModel predictorModel)
|
||
{
|
||
_Model = model;
|
||
_ArgZero = argZero;
|
||
_Configuration = configuration;
|
||
_ModelParameter = modelParameter;
|
||
_PredictorModel = predictorModel;
|
||
AngleBracketCollection = new List<string>();
|
||
_Log = Serilog.Log.ForContext<D_Face>();
|
||
_WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
|
||
}
|
||
|
||
private D_Face(Location location)
|
||
{
|
||
_Α = α;
|
||
_DateTime = DateTime.MinValue;
|
||
_FaceEncoding = null;
|
||
_FaceLandmarks = null;
|
||
_OutputResolution = null;
|
||
_Location = location;
|
||
_LocationIndex = null;
|
||
_Populated = false;
|
||
_RelativePath = string.Empty;
|
||
}
|
||
|
||
private D_Face()
|
||
{
|
||
_Α = α;
|
||
_DateTime = DateTime.MinValue;
|
||
_FaceEncoding = null;
|
||
_FaceLandmarks = null;
|
||
_OutputResolution = null;
|
||
_Location = null;
|
||
_LocationIndex = null;
|
||
_Populated = false;
|
||
_RelativePath = string.Empty;
|
||
}
|
||
|
||
private D_Face(A_Property property, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, string relativePath, int? i, Location location)
|
||
{
|
||
DateTime?[] dateTimes;
|
||
dateTimes = new DateTime?[] { property.CreationTime, property.LastWriteTime, property.DateTime, property.DateTimeDigitized, property.DateTimeOriginal, property.GPSDateStamp };
|
||
_DateTime = (from l in dateTimes where l.HasValue select l.Value).Min();
|
||
_FaceLandmarks = new Dictionary<string, FacePoint[]>();
|
||
_OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth);
|
||
_Location = location;
|
||
_LocationIndex = i;
|
||
_Populated = false;
|
||
_RelativePath = relativePath;
|
||
}
|
||
|
||
private D_Face(int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, Shared.Models.Properties.IFace face)
|
||
{
|
||
_Α = face.α;
|
||
_DateTime = face.DateTime;
|
||
_FaceEncoding = face.FaceEncoding;
|
||
_FaceLandmarks = face.FaceLandmarks;
|
||
_OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth);
|
||
_Location = face.Location;
|
||
_LocationIndex = face.LocationIndex;
|
||
_Populated = face.Populated;
|
||
_RelativePath = face.RelativePath;
|
||
}
|
||
|
||
#nullable restore
|
||
|
||
private static void GetPointBounds(PointF[] points, out float xMinimum, out float xMaximum, out float yMinimum, out float yMaximum)
|
||
{
|
||
xMinimum = points[0].X;
|
||
xMaximum = xMinimum;
|
||
yMinimum = points[0].Y;
|
||
yMaximum = yMinimum;
|
||
foreach (PointF point in points)
|
||
{
|
||
if (xMinimum > point.X)
|
||
xMinimum = point.X;
|
||
if (xMaximum < point.X)
|
||
xMaximum = point.X;
|
||
if (yMinimum > point.Y)
|
||
yMinimum = point.Y;
|
||
if (yMaximum < point.Y)
|
||
yMaximum = point.Y;
|
||
}
|
||
}
|
||
|
||
public override string ToString()
|
||
{
|
||
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
|
||
return result;
|
||
}
|
||
|
||
#pragma warning disable CA1416
|
||
|
||
internal static Bitmap RotateBitmap(Bitmap bitmap, float angle)
|
||
{
|
||
Bitmap result;
|
||
#if Linux
|
||
throw new Exception("Built on Linux!");
|
||
#elif OSX
|
||
throw new Exception("Built on macOS!");
|
||
#elif Windows
|
||
// Make a Matrix to represent rotation
|
||
// by this angle.
|
||
Matrix rotate_at_origin = new();
|
||
rotate_at_origin.Rotate(angle);
|
||
|
||
// Rotate the image's corners to see how big
|
||
// it will be after rotation.
|
||
PointF[] points =
|
||
{
|
||
new PointF(0, 0),
|
||
new PointF(bitmap.Width, 0),
|
||
new PointF(bitmap.Width, bitmap.Height),
|
||
new PointF(0, bitmap.Height),
|
||
};
|
||
rotate_at_origin.TransformPoints(points);
|
||
float xMinimum, xMaximum, yMinimum, yMaximum;
|
||
GetPointBounds(points, out xMinimum, out xMaximum, out yMinimum, out yMaximum);
|
||
|
||
// Make a bitmap to hold the rotated result.
|
||
int wid = (int)Math.Round(xMaximum - xMinimum);
|
||
int hgt = (int)Math.Round(yMaximum - yMinimum);
|
||
result = new Bitmap(wid, hgt);
|
||
|
||
// Create the real rotation transformation.
|
||
Matrix rotate_at_center = new();
|
||
rotate_at_center.RotateAt(angle,
|
||
new PointF(wid / 2f, hgt / 2f));
|
||
|
||
// Draw the image onto the new bitmap rotated.
|
||
using (Graphics gr = Graphics.FromImage(result))
|
||
{
|
||
// Use smooth image interpolation.
|
||
gr.InterpolationMode = InterpolationMode.High;
|
||
|
||
// Clear with the color in the image's upper left corner.
|
||
gr.Clear(bitmap.GetPixel(0, 0));
|
||
|
||
// For debugging. (It's easier to see the background.)
|
||
// gr.Clear(Color.LightBlue);
|
||
|
||
// Set up the transformation to rotate.
|
||
gr.Transform = rotate_at_center;
|
||
|
||
// Draw the image centered on the bitmap.
|
||
int x = (wid - bitmap.Width) / 2;
|
||
int y = (hgt - bitmap.Height) / 2;
|
||
gr.DrawImage(bitmap, x, y);
|
||
}
|
||
#endif
|
||
// Return the result bitmap.
|
||
return result;
|
||
}
|
||
|
||
private static void SaveFaces(List<D_Face> faceCollection, FileInfo resizedFileInfo, List<string> imageFiles)
|
||
{
|
||
int width;
|
||
int height;
|
||
Graphics graphics;
|
||
Location location;
|
||
Bitmap preRotated;
|
||
Rectangle rectangle;
|
||
using Bitmap source = new(resizedFileInfo.FullName);
|
||
for (int i = 0; i < faceCollection.Count; i++)
|
||
{
|
||
if (!faceCollection[i].Populated || faceCollection[i]?.Location is null)
|
||
continue;
|
||
location = new Location(faceCollection[i].Location.Confidence,
|
||
faceCollection[i].Location.Bottom,
|
||
faceCollection[i].Location.Left,
|
||
faceCollection[i].Location.Right,
|
||
faceCollection[i].Location.Top);
|
||
width = location.Right - location.Left;
|
||
height = location.Bottom - location.Top;
|
||
rectangle = new Rectangle(location.Left, location.Top, width, height);
|
||
using (preRotated = new(width, height))
|
||
{
|
||
using (graphics = Graphics.FromImage(preRotated))
|
||
graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
|
||
preRotated.Save(imageFiles[i], System.Drawing.Imaging.ImageFormat.Png);
|
||
}
|
||
}
|
||
}
|
||
|
||
private List<D_Face> GetFaces(FileInfo resizedFileInfo, string relativePath, string fileNameWithoutExtension, A_Property property, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, string facesDirectory)
|
||
{
|
||
List<D_Face> results = new();
|
||
if (_Configuration.PaddingLoops is null)
|
||
throw new Exception();
|
||
if (_Configuration.NumJitters is null)
|
||
throw new Exception();
|
||
Location[] locations;
|
||
const int numberOfTimesToUpSample = 1;
|
||
FaceRecognitionDotNet.Image? unknownImage = null;
|
||
if (resizedFileInfo.Exists)
|
||
{
|
||
try
|
||
{ unknownImage = FaceRecognition.LoadImageFile(resizedFileInfo.FullName); }
|
||
catch (Exception) { }
|
||
}
|
||
if (unknownImage is null)
|
||
results.Add(new D_Face(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, relativePath, i: null, location: null));
|
||
else
|
||
{
|
||
FaceRecognition faceRecognition = FaceRecognition.Create(_ModelParameter);
|
||
locations = faceRecognition.FaceLocations(unknownImage, numberOfTimesToUpSample, _Model).ToArray();
|
||
if (!locations.Any())
|
||
results.Add(new D_Face(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, relativePath, i: null, location: null));
|
||
else
|
||
{
|
||
double? α;
|
||
int width;
|
||
int height;
|
||
int padding;
|
||
int leftEyeX;
|
||
int leftEyeY;
|
||
int rightEyeX;
|
||
int rightEyeY;
|
||
Bitmap rotated;
|
||
string faceFile;
|
||
Location location;
|
||
Bitmap preRotated;
|
||
Graphics graphics;
|
||
D_Face? face = null;
|
||
Rectangle rectangle;
|
||
double[] rawEncoding;
|
||
FaceRecognitionDotNet.Image? knownImage;
|
||
FaceRecognitionDotNet.Image? rotatedImage;
|
||
Shared.Models.FaceEncoding faceEncoding;
|
||
FaceRecognitionDotNet.FaceEncoding[] faceEncodings;
|
||
IEnumerable<FacePoint> facePoints;
|
||
IDictionary<FacePart, IEnumerable<FacePoint>>[] faceLandmarks;
|
||
using Bitmap source = unknownImage.ToBitmap();
|
||
padding = (int)((source.Width + source.Height) / 2 * .01);
|
||
for (int i = 0; i < locations.Length; i++)
|
||
{
|
||
for (int p = 0; p <= _Configuration.PaddingLoops.Value; p++)
|
||
{
|
||
//Location(double confidence, int bottom, int left, int right, int top)
|
||
location = new(locations[i].Confidence,
|
||
locations[i].Bottom + (padding * p),
|
||
locations[i].Left - (padding * p),
|
||
locations[i].Right + (padding * p),
|
||
locations[i].Top - (padding * p));
|
||
face = new D_Face(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, relativePath, i, location);
|
||
width = location.Right - location.Left;
|
||
height = location.Bottom - location.Top;
|
||
rectangle = new Rectangle(location.Left, location.Top, width, height);
|
||
using (preRotated = new Bitmap(width, height))
|
||
{
|
||
using (graphics = Graphics.FromImage(preRotated))
|
||
graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
|
||
// source.Save(Path.Combine(_Configuration.RootDirectory, "source.jpg"));
|
||
// preRotated.Save(Path.Combine(_Configuration.RootDirectory, $"{p} - preRotated.jpg"));
|
||
using (knownImage = FaceRecognition.LoadImage(preRotated))
|
||
if (knownImage is null)
|
||
throw new Exception($"{nameof(knownImage)} is null");
|
||
faceLandmarks = faceRecognition.FaceLandmark(knownImage, faceLocations: null, _PredictorModel, _Model).ToArray();
|
||
if (faceLandmarks.Length == 0 && p < _Configuration.PaddingLoops.Value)
|
||
continue;
|
||
else if (faceLandmarks.Length != 1)
|
||
continue;
|
||
foreach (KeyValuePair<FacePart, IEnumerable<FacePoint>> keyValuePair in faceLandmarks[0])
|
||
face.FaceLandmarks.Add(keyValuePair.Key.ToString(), keyValuePair.Value.ToArray());
|
||
if (!faceLandmarks[0].ContainsKey(FacePart.LeftEye) || !faceLandmarks[0].ContainsKey(FacePart.RightEye))
|
||
continue;
|
||
facePoints = faceLandmarks[0][FacePart.LeftEye];
|
||
leftEyeX = (int)(from l in facePoints select l.X).Average();
|
||
leftEyeY = (int)(from l in facePoints select l.Y).Average();
|
||
facePoints = faceLandmarks[0][FacePart.RightEye];
|
||
rightEyeX = (int)(from l in facePoints select l.X).Average();
|
||
rightEyeY = (int)(from l in facePoints select l.Y).Average();
|
||
α = Shared.Models.Stateless.Methods.IFace.Getα(rightEyeX, leftEyeX, rightEyeY, leftEyeY);
|
||
using (rotated = RotateBitmap(preRotated, (float)α.Value))
|
||
{
|
||
// rotated.Save(Path.Combine(_Configuration.RootDirectory, $"{p} - rotated.jpg"));
|
||
using (rotatedImage = FaceRecognition.LoadImage(rotated))
|
||
if (rotatedImage is null)
|
||
throw new Exception($"{nameof(rotatedImage)} is null");
|
||
faceEncodings = faceRecognition.FaceEncodings(rotatedImage, knownFaceLocation: null, _Configuration.NumJitters.Value, _PredictorModel, _Model).ToArray();
|
||
if (faceEncodings.Length == 0 && p < _Configuration.PaddingLoops.Value)
|
||
continue;
|
||
else if (faceEncodings.Length != 1)
|
||
continue;
|
||
rawEncoding = faceEncodings[0].GetRawEncoding();
|
||
faceEncoding = new(rawEncoding, faceEncodings[0].Size);
|
||
face.Update(α, faceEncoding, populated: true);
|
||
}
|
||
faceFile = Path.Combine(facesDirectory, $"{i} - {fileNameWithoutExtension}.png");
|
||
preRotated.Save(faceFile, System.Drawing.Imaging.ImageFormat.Png);
|
||
results.Add(face);
|
||
}
|
||
if (face.Populated)
|
||
break;
|
||
}
|
||
if (face is null || !face.Populated)
|
||
{
|
||
location = new(locations[i].Confidence,
|
||
locations[i].Bottom,
|
||
locations[i].Left,
|
||
locations[i].Right,
|
||
locations[i].Top);
|
||
face = new D_Face(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, relativePath, i, location);
|
||
results.Add(face);
|
||
}
|
||
}
|
||
}
|
||
unknownImage.Dispose();
|
||
faceRecognition.Dispose();
|
||
}
|
||
if (!results.Any())
|
||
throw new Exception();
|
||
return results;
|
||
}
|
||
|
||
#pragma warning restore CA1416
|
||
|
||
private void Update(double? α, Shared.Models.FaceEncoding faceEncoding, bool populated)
|
||
{
|
||
_Α = α;
|
||
_FaceEncoding = faceEncoding;
|
||
_Populated = populated;
|
||
}
|
||
|
||
internal List<D_Face> GetFaces(Property.Models.Configuration configuration, string outputResolution, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, string relativePath, string fileNameWithoutExtension, A_Property property, FileInfo resizedFileInfo, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation)
|
||
{
|
||
List<D_Face>? results;
|
||
if (_Configuration.PropertiesChangedForFaces is null)
|
||
throw new Exception();
|
||
string json;
|
||
D_Face face;
|
||
bool checkForOutputResolutionChange = false;
|
||
string[] changesFrom = new string[] { nameof(A_Property), nameof(B_Metadata), nameof(C_Resize) };
|
||
string facesDirectory = Path.Combine(AngleBracketCollection[0].Replace("<>", "()"), fileNameWithoutExtension);
|
||
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
|
||
FileInfo fileInfo = new(Path.Combine(AngleBracketCollection[0].Replace("<>", "[]"), $"{fileNameWithoutExtension}.json"));
|
||
if (!fileInfo.Exists)
|
||
{
|
||
if (fileInfo.Directory?.Parent is null)
|
||
throw new Exception();
|
||
string parentCheck = Path.Combine(fileInfo.Directory.Parent.FullName, fileInfo.Name);
|
||
if (File.Exists(parentCheck))
|
||
File.Delete(parentCheck);
|
||
}
|
||
if (!Directory.Exists(facesDirectory))
|
||
_ = Directory.CreateDirectory(facesDirectory);
|
||
if (_Configuration.PropertiesChangedForFaces.Value)
|
||
results = null;
|
||
else if (!fileInfo.Exists)
|
||
results = null;
|
||
else if (dateTimes.Any() && dateTimes.Max() > fileInfo.LastWriteTime)
|
||
results = null;
|
||
else
|
||
{
|
||
json = Shared.Models.Stateless.Methods.IFace.GetJson(fileInfo.FullName);
|
||
try
|
||
{
|
||
results = JsonSerializer.Deserialize<List<D_Face>>(json);
|
||
if (results is null)
|
||
throw new Exception($"{nameof(results)} is null");
|
||
for (int i = 0; i < results.Count; i++)
|
||
{
|
||
face = results[i];
|
||
if (face.OutputResolution is not null)
|
||
continue;
|
||
if (!checkForOutputResolutionChange)
|
||
checkForOutputResolutionChange = true;
|
||
results[i] = new(outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, face);
|
||
}
|
||
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), fileInfo.LastWriteTime));
|
||
}
|
||
catch (Exception)
|
||
{
|
||
results = null;
|
||
parseExceptions.Add(nameof(D_Face));
|
||
}
|
||
}
|
||
if (results is not null && checkForOutputResolutionChange)
|
||
{
|
||
json = JsonSerializer.Serialize(results, _WriteIndentedJsonSerializerOptions);
|
||
if (Property.Models.Stateless.IPath.WriteAllText(fileInfo.FullName, json, compareBeforeWrite: true))
|
||
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
|
||
}
|
||
else if (results is null)
|
||
{
|
||
results = GetFaces(resizedFileInfo, relativePath, fileNameWithoutExtension, property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, facesDirectory);
|
||
json = JsonSerializer.Serialize(results, _WriteIndentedJsonSerializerOptions);
|
||
if (Property.Models.Stateless.IPath.WriteAllText(fileInfo.FullName, json, compareBeforeWrite: true))
|
||
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), DateTime.Now));
|
||
}
|
||
return results;
|
||
}
|
||
|
||
internal void SaveFaces(Property.Models.Configuration configuration, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, string relativePath, string fileNameWithoutExtension, FileInfo resizedFileInfo, List<D_Face> faceCollection)
|
||
{
|
||
if (_Configuration.OverrideForFaceImages is null)
|
||
throw new Exception();
|
||
FileInfo fileInfo;
|
||
bool check = false;
|
||
string parentCheck;
|
||
List<string> imageFiles = new();
|
||
string[] changesFrom = new string[] { nameof(A_Property), nameof(B_Metadata), nameof(C_Resize) };
|
||
string facesDirectory = Path.Combine(AngleBracketCollection[0].Replace("<>", "()"), fileNameWithoutExtension);
|
||
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
|
||
bool facesDirectoryExisted = Directory.Exists(facesDirectory);
|
||
if (!facesDirectoryExisted)
|
||
_ = Directory.CreateDirectory(facesDirectory);
|
||
for (int i = 0; i < faceCollection.Count; i++)
|
||
{
|
||
if (!faceCollection[i].Populated || faceCollection[i]?.Location is null)
|
||
{
|
||
imageFiles.Add(string.Empty);
|
||
continue;
|
||
}
|
||
fileInfo = new FileInfo(Path.Combine(facesDirectory, $"{i} - {fileNameWithoutExtension}.png"));
|
||
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);
|
||
}
|
||
imageFiles.Add(fileInfo.FullName);
|
||
if (_Configuration.OverrideForFaceImages.Value)
|
||
check = true;
|
||
else if (!fileInfo.Exists)
|
||
check = true;
|
||
else if (dateTimes.Any() && dateTimes.Max() > fileInfo.LastWriteTime)
|
||
check = true;
|
||
}
|
||
if (check)
|
||
SaveFaces(faceCollection, resizedFileInfo, imageFiles);
|
||
}
|
||
|
||
internal void SaveShortcuts(Property.Models.Configuration configuration, string[] juliePhares, Model model, PredictorModel predictorModel, PropertyLogic propertyLogic, Dictionary<string, List<Person>> peopleCollection, string outputResolution, PropertyHolder[] filteredPropertyHolderCollection, List<A_Property> propertyCollection, List<List<D_Face>> faceCollections)
|
||
{
|
||
string[] keys;
|
||
string fileName;
|
||
string fullName;
|
||
string personKey;
|
||
string directory;
|
||
bool? isWrongYear;
|
||
FileInfo fileInfo;
|
||
TimeSpan timeSpan;
|
||
DateTime? birthDate;
|
||
string copyDirectory;
|
||
string? relativePath;
|
||
string isWrongYearFlag;
|
||
string subDirectoryName;
|
||
DateTime minimumDateTime;
|
||
List<D_Face> faceCollection;
|
||
PropertyHolder propertyHolder;
|
||
WindowsShortcut windowsShortcut;
|
||
const string pattern = @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]";
|
||
string dFacesContentDirectory = Path.Combine(Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(configuration, model.ToString(), predictorModel.ToString(), nameof(D_Face), outputResolution, includeResizeGroup: true, includeModel: true, includePredictorModel: true), "(_)");
|
||
for (int i = 0; i < filteredPropertyHolderCollection.Length; i++)
|
||
{
|
||
personKey = string.Empty;
|
||
copyDirectory = string.Empty;
|
||
propertyHolder = filteredPropertyHolderCollection[i];
|
||
if (propertyHolder.ImageFileInfo is null)
|
||
continue;
|
||
fileInfo = propertyHolder.ImageFileInfo;
|
||
relativePath = Path.GetDirectoryName($"C:{propertyHolder.RelativePath}");
|
||
if (string.IsNullOrEmpty(relativePath) || relativePath.Length < 3)
|
||
continue;
|
||
if (propertyHolder.Property?.Id is null || propertyHolder.MinimumDateTime is null || propertyHolder.ResizedFileInfo is null)
|
||
continue;
|
||
if (!propertyLogic.NamedFaceInfoDeterministicHashCodeIndices.ContainsKey(propertyHolder.Property.Id.Value))
|
||
directory = Path.Combine(dFacesContentDirectory, $"Unnamed{relativePath[2..]}");
|
||
else
|
||
{
|
||
faceCollection = faceCollections[i];
|
||
keys = propertyLogic.NamedFaceInfoDeterministicHashCodeIndices[propertyHolder.Property.Id.Value];
|
||
minimumDateTime = Property.Models.Stateless.A_Property.GetMinimumDateTime(propertyHolder.Property);
|
||
(isWrongYear, _) = propertyHolder.Property.IsWrongYear(propertyHolder.ImageFileInfo.FullName, minimumDateTime);
|
||
isWrongYearFlag = isWrongYear is null ? "#" : isWrongYear.Value ? "~" : "=";
|
||
subDirectoryName = $"{isWrongYearFlag}{minimumDateTime:yyyy}";
|
||
if (!faceCollection.Any())
|
||
directory = Path.Combine(dFacesContentDirectory, $"None{relativePath[2..]}", subDirectoryName);
|
||
else if (keys.Length != 1)
|
||
directory = Path.Combine(dFacesContentDirectory, $"Not Supported{relativePath[2..]}", subDirectoryName);
|
||
else if (faceCollection.Count != 1)
|
||
directory = Path.Combine(dFacesContentDirectory, $"Many{relativePath[2..]}", subDirectoryName);
|
||
else
|
||
{
|
||
personKey = keys[0];
|
||
if (isWrongYear is not null && !isWrongYear.Value && personKey[..2] is "19" or "20")
|
||
{
|
||
birthDate = Shared.Models.Stateless.Methods.IPersonBirthday.Get(personKey);
|
||
if (birthDate.HasValue)
|
||
{
|
||
if (minimumDateTime < birthDate.Value)
|
||
subDirectoryName = "!---";
|
||
else
|
||
{
|
||
timeSpan = new(minimumDateTime.Ticks - birthDate.Value.Ticks);
|
||
subDirectoryName = $"^{Math.Floor(timeSpan.TotalDays / 365):000}";
|
||
}
|
||
}
|
||
}
|
||
directory = Path.Combine(dFacesContentDirectory, "Named Shortcuts", personKey, subDirectoryName);
|
||
if (juliePhares.Contains(personKey))
|
||
copyDirectory = Path.Combine(dFacesContentDirectory, "Named Images", personKey, subDirectoryName);
|
||
}
|
||
}
|
||
if (!Directory.Exists(directory))
|
||
{
|
||
_ = Directory.CreateDirectory(directory);
|
||
if (!string.IsNullOrEmpty(personKey) && peopleCollection.ContainsKey(personKey))
|
||
{
|
||
Person person = peopleCollection[personKey][0];
|
||
fullName = Regex.Replace($"{Shared.Models.Stateless.Methods.IPersonName.GetFullName(person.Name)}.txt", pattern, string.Empty);
|
||
File.WriteAllText(Path.Combine(directory, fullName), string.Empty);
|
||
}
|
||
}
|
||
if (!string.IsNullOrEmpty(copyDirectory))
|
||
{
|
||
if (!Directory.Exists(copyDirectory))
|
||
_ = Directory.CreateDirectory(copyDirectory);
|
||
fileName = Path.Combine(copyDirectory, $"{propertyHolder.Property.Id.Value}{propertyHolder.ResizedFileInfo.Extension}");
|
||
if (!File.Exists(fileName))
|
||
File.Copy(propertyHolder.ResizedFileInfo.FullName, fileName);
|
||
}
|
||
fileName = Path.Combine(directory, $"{propertyHolder.Property.Id.Value}.lnk");
|
||
if (File.Exists(fileName))
|
||
continue;
|
||
windowsShortcut = new() { Path = fileInfo.FullName };
|
||
windowsShortcut.Save(fileName);
|
||
windowsShortcut.Dispose();
|
||
if (!File.Exists(fileName))
|
||
continue;
|
||
File.SetLastWriteTime(fileName, propertyHolder.MinimumDateTime.Value);
|
||
}
|
||
}
|
||
|
||
double Shared.Models.Stateless.Methods.IFace.TestStatic_Getα(int x1, int x2, int y1, int y2) => throw new NotImplementedException();
|
||
|
||
string Shared.Models.Stateless.Methods.IFace.TestStatic_GetJson(string jsonFileFullName) => throw new NotImplementedException();
|
||
|
||
Face Shared.Models.Stateless.Methods.IFace.TestStatic_GetFace(string jsonFileFullName) => throw new NotImplementedException();
|
||
|
||
Face[] Shared.Models.Stateless.Methods.IFace.TestStatic_GetFaces(string jsonFileFullName) => throw new NotImplementedException();
|
||
|
||
private static bool HasLeftAndRight(Dictionary<string, FacePoint[]> faceLandmarks)
|
||
{
|
||
bool result = true;
|
||
if (!faceLandmarks.ContainsKey(FacePart.LeftEye.ToString()))
|
||
result = false;
|
||
else if (!faceLandmarks.ContainsKey(FacePart.RightEye.ToString()))
|
||
result = false;
|
||
else if (!faceLandmarks.ContainsKey(FacePart.LeftEyebrow.ToString()))
|
||
result = false;
|
||
else if (!faceLandmarks.ContainsKey(FacePart.RightEyebrow.ToString()))
|
||
result = false;
|
||
return result;
|
||
}
|
||
|
||
} |