Mike Phares be7a6abbf4 AppSettings and Configuration changes,
major changes to E_Distance and minor for D_Face
2022-08-19 21:37:36 -07:00

622 lines
30 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 Configuration _Configuration;
private readonly ModelParameter _ModelParameter;
private readonly PredictorModel _PredictorModel;
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, FileHolder resizedFileHolder, List<string> imageFiles)
{
int width;
int height;
Graphics graphics;
Location location;
Bitmap preRotated;
Rectangle rectangle;
using Bitmap source = new(resizedFileHolder.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,
source.Width,
source.Height);
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(FileHolder resizedFileHolder, Item item, A_Property property, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, string facesDirectory)
{
List<D_Face> results = new();
if (item.ImageFileHolder is null)
throw new NullReferenceException(nameof(item.ImageFileHolder));
List<Location> locations;
FaceRecognitionDotNet.Image? unknownImage = null;
if (resizedFileHolder.Exists)
{
try
{ unknownImage = FaceRecognition.LoadImageFile(resizedFileHolder.FullName); }
catch (Exception) { }
}
if (unknownImage is null)
results.Add(new D_Face(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, item.RelativePath, i: null, location: null));
else
{
FaceRecognition faceRecognition = FaceRecognition.Create(_ModelParameter);
locations = faceRecognition.FaceLocations(_Model, unknownImage, _Configuration.NumberOfTimesToUpsample, sortByNormalizedPixelPercentage: true);
if (!locations.Any())
results.Add(new D_Face(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, item.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;
Shared.Models.FaceEncoding faceEncoding;
FaceRecognitionDotNet.Image? knownImage;
FaceRecognitionDotNet.Image? rotatedImage;
List<(FacePart, FacePoint[])[]> facesLandmarks;
List<FaceRecognitionDotNet.FaceEncoding> faceEncodings;
using Bitmap source = unknownImage.ToBitmap();
padding = (int)((source.Width + source.Height) / 2 * .01);
for (int i = 0; i < locations.Count; i++)
{
for (int p = 0; p <= _Configuration.PaddingLoops; p++)
{
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),
source.Width,
source.Height);
face = new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, item.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))
{
leftEyeX = null;
leftEyeY = null;
rightEyeX = null;
rightEyeY = null;
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 || knownImage.IsDisposed)
throw new NullReferenceException(nameof(knownImage));
facesLandmarks = faceRecognition.GetFaceLandmarkCollection(knownImage, _Configuration.NumberOfTimesToUpsample, faceLocations: null, _PredictorModel, _Model);
}
if (facesLandmarks.Count == 0 && p < _Configuration.PaddingLoops)
continue;
else if (facesLandmarks.Count != 1)
continue;
foreach ((FacePart facePart, FacePoint[] facePoints) in facesLandmarks[0])
{
face.FaceLandmarks.Add(facePart.ToString(), facePoints);
if (facePart is not FacePart.LeftEye and not FacePart.RightEye)
continue;
if (facePart is FacePart.LeftEye)
{
leftEyeX = (int)(from l in facePoints select l.X).Average();
leftEyeY = (int)(from l in facePoints select l.Y).Average();
}
if (facePart is FacePart.RightEye)
{
rightEyeX = (int)(from l in facePoints select l.X).Average();
rightEyeY = (int)(from l in facePoints select l.Y).Average();
}
}
if (rightEyeX is null || leftEyeX is null || rightEyeY is null || leftEyeY is null)
continue;
α = Shared.Models.Stateless.Methods.IFace.Getα(rightEyeX.Value, leftEyeX.Value, rightEyeY.Value, leftEyeY.Value);
using (rotated = RotateBitmap(preRotated, (float)α.Value))
{
// rotated.Save(Path.Combine(_Configuration.RootDirectory, $"{p} - rotated.jpg"));
using (rotatedImage = FaceRecognition.LoadImage(rotated))
{
if (rotatedImage is null || rotatedImage.IsDisposed)
throw new NullReferenceException(nameof(rotatedImage));
faceEncodings = faceRecognition.FaceEncodings(rotatedImage, _Configuration.NumberOfTimesToUpsample, knownFaceLocation: null, _Configuration.NumberOfJitters, _PredictorModel, _Model);
}
if (faceEncodings.Count == 0 && p < _Configuration.PaddingLoops)
continue;
else if (faceEncodings.Count != 1)
continue;
rawEncoding = faceEncodings[0].GetRawEncoding();
faceEncoding = new(rawEncoding, faceEncodings[0].Size);
face.Update(α, faceEncoding, populated: true);
}
faceFile = Path.Combine(facesDirectory, $"{i} - {item.ImageFileHolder.NameWithoutExtension}.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,
source.Width,
source.Height);
face = new D_Face(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, item.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(string dResultsFullGroupDirectory, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, Item item, A_Property property, FileHolder resizedFileHolder, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation)
{
List<D_Face>? results;
if (item.Property?.Id is null)
throw new NullReferenceException(nameof(item.Property.Id));
if (item.ImageFileHolder is null)
throw new NullReferenceException(nameof(item.ImageFileHolder));
if (string.IsNullOrEmpty(dResultsFullGroupDirectory))
throw new NullReferenceException(nameof(dResultsFullGroupDirectory));
string json;
string[] changesFrom = new string[] { nameof(A_Property), nameof(B_Metadata), nameof(C_Resize) };
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
string facesDirectory = Path.Combine(AngleBracketCollection[0].Replace("<>", "()"), item.ImageFileHolder.NameWithoutExtension);
string usingRelativePath = Path.Combine(AngleBracketCollection[0].Replace("<>", "[]"), $"{item.ImageFileHolder.NameWithoutExtension}.json");
string dCollectionFile = Path.Combine(dResultsFullGroupDirectory, "[]", Property.Models.Stateless.IResult.AllInOne, $"{item.Property.Id.Value}{item.ImageFileHolder.ExtensionLowered}.json");
FileInfo fileInfo = new(dCollectionFile);
if (!fileInfo.Exists)
{
if (File.Exists(usingRelativePath))
{
File.Move(usingRelativePath, fileInfo.FullName);
fileInfo.Refresh();
}
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.Move(parentCheck, fileInfo.FullName);
fileInfo.Refresh();
}
}
}
if (!Directory.Exists(facesDirectory))
_ = Directory.CreateDirectory(facesDirectory);
if (_Configuration.ForceFaceLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
{
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
fileInfo.Refresh();
}
if (_Configuration.ForceFaceLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime)
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
}
if (_Configuration.PropertiesChangedForFaces)
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 NullReferenceException(nameof(results));
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), fileInfo.LastWriteTime));
}
catch (Exception)
{
results = null;
parseExceptions.Add(nameof(D_Face));
}
}
if (results is null)
{
results = GetFaces(resizedFileHolder, item, property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, facesDirectory);
json = JsonSerializer.Serialize(results, _WriteIndentedJsonSerializerOptions);
bool updateDateWhenMatches = dateTimes.Any() && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime;
DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max();
if (Property.Models.Stateless.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime))
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), DateTime.Now));
}
if (_Configuration.ForceFaceLastWriteTimeToCreationTime)
{
json = JsonSerializer.Serialize(results, _WriteIndentedJsonSerializerOptions);
bool updateDateWhenMatches = dateTimes.Any() && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime;
DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max();
if (Property.Models.Stateless.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime))
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), fileInfo.CreationTime));
}
}
return results;
}
internal void SaveFaces(string dResultsFullGroupDirectory, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, Item item, List<D_Face> faceCollection)
{
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;
List<string> imageFiles = new();
string[] changesFrom = new string[] { nameof(A_Property), nameof(B_Metadata), nameof(C_Resize) };
string facesDirectory = Path.Combine(AngleBracketCollection[0].Replace("<>", "()"), item.ImageFileHolder.NameWithoutExtension);
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} - {item.ImageFileHolder.NameWithoutExtension}.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)
check = true;
else if (!fileInfo.Exists)
check = true;
else if (dateTimes.Any() && dateTimes.Max() > fileInfo.LastWriteTime)
check = true;
}
if (check)
SaveFaces(faceCollection, item.ResizedFileHolder, imageFiles);
}
internal static void SaveShortcuts(string[] juliePhares, string dResultsFullGroupDirectory, long ticks, Dictionary<string, List<Person>> peopleCollection, PropertyLogic propertyLogic, List<Item> items)
{
Person person;
string fileName;
string fullName;
DateTime? minimumDateTime;
WindowsShortcut windowsShortcut;
const string pattern = @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]";
string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, $"({ticks})");
List<(Item, (string, Shared.Models.Properties.IFace?, (string, string, string, string))[])> collections = Item.GetCollection(propertyLogic, items, dFacesContentDirectory);
foreach ((Item item, (string personKey, Shared.Models.Properties.IFace? _, (string, string, string, string))[] collection) in collections)
{
if (collection.Length != 1)
continue;
foreach ((string personKey, Shared.Models.Properties.IFace? _, (string directory, string copyDirectory, string copyFileName, string shortcutFileName)) in collection)
{
if (string.IsNullOrEmpty(personKey))
continue;
if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null)
continue;
minimumDateTime = Property.Models.Stateless.A_Property.GetMinimumDateTime(item.Property);
if (minimumDateTime is null)
continue;
if (!Directory.Exists(directory))
{
_ = Directory.CreateDirectory(directory);
if (!string.IsNullOrEmpty(personKey) && peopleCollection.ContainsKey(personKey))
{
person = peopleCollection[personKey][0];
fullName = string.Concat(Regex.Replace(Shared.Models.Stateless.Methods.IPersonName.GetFullName(person.Name), pattern, string.Empty), ".txt");
File.WriteAllText(Path.Combine(directory, fullName), string.Empty);
}
}
if (juliePhares.Contains(personKey) && !string.IsNullOrEmpty(copyDirectory))
{
if (!Directory.Exists(copyDirectory))
_ = Directory.CreateDirectory(copyDirectory);
fileName = Path.Combine(copyDirectory, $"{item.Property.Id.Value}{item.ResizedFileHolder.ExtensionLowered}");
if (!File.Exists(fileName))
File.Copy(item.ResizedFileHolder.FullName, fileName);
}
fileName = Path.Combine(directory, $"{item.Property.Id.Value}.lnk");
if (File.Exists(fileName))
continue;
windowsShortcut = new() { Path = item.ImageFileHolder.FullName };
windowsShortcut.Save(fileName);
windowsShortcut.Dispose();
if (!File.Exists(fileName))
continue;
File.SetLastWriteTime(fileName, 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;
}
}