2022-11-20 23:20:28 -07:00

409 lines
19 KiB
C#

using ShellProgressBar;
using System.Text.Json;
using View_by_Distance.FaceRecognitionDotNet;
using View_by_Distance.Map.Models;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
using WindowsShortcutFactory;
namespace View_by_Distance.Distance.Models;
public class MapLogicSupport : Shared.Models.Methods.IMapLogicSupport
{
private int _Area;
private int _Days;
private int _Distance;
private int _Confidence;
private readonly int _FaceConfidencePercent;
private readonly int _FaceDistancePermyriad;
private readonly int[] _RangeDaysDeltaTolerance;
private readonly int[] _RangeFaceAreaPermilleTolerance;
private readonly int _SortingMaximumPerFaceShouldBeHigh;
public MapLogicSupport(int faceConfidencePercent, int faceDistancePermyriad, int[] rangeDaysDeltaTolerance, int[] rangeFaceAreaPermilleTolerance, int sortingMaximumPerFaceShouldBeHigh)
{
_FaceConfidencePercent = faceConfidencePercent;
_FaceDistancePermyriad = faceDistancePermyriad;
_RangeDaysDeltaTolerance = rangeDaysDeltaTolerance;
_RangeFaceAreaPermilleTolerance = rangeFaceAreaPermilleTolerance;
_SortingMaximumPerFaceShouldBeHigh = sortingMaximumPerFaceShouldBeHigh;
}
public static void SaveFaceDistances(Property.Models.Configuration configuration, SortingContainer[] sortingContainers)
{
string eDistanceContentCollectionDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(E_Distance), "([])");
if (!Directory.Exists(eDistanceContentCollectionDirectory))
_ = Directory.CreateDirectory(eDistanceContentCollectionDirectory);
#pragma warning disable
string[] results = (from l in sortingContainers select l.ToString()).ToArray();
#pragma warning restore
string eDistanceContentFileName = Path.Combine(eDistanceContentCollectionDirectory, $"{configuration.ResultAllInOne}.tvs");
File.WriteAllLines(eDistanceContentFileName, results);
}
private List<SortingContainer> GetSortingContainers(Face face, FaceDistance faceDistanceEncoding, List<Sorting> sortingCollection, int? useFiltersCounter)
{
List<SortingContainer> results = new();
SortingContainer sortingContainer;
Sorting[] collection = Shared.Models.Stateless.Methods.ISorting.Sort(sortingCollection);
double a;
double b;
double c;
double d;
double faceAreaPermille;
double faceConfidencePercent;
double faceDistancePermyriad;
double rangeDaysDeltaTolerance;
if (useFiltersCounter is null)
{
a = 1f;
b = 1f;
c = 1f;
d = 1f;
}
else if (useFiltersCounter.Value < 5)
{
a = 1.25f;
b = 0.8f;
c = a;
d = b;
}
else if (useFiltersCounter.Value < 9)
{
a = 1.5f;
b = 0.667f;
c = a;
d = b;
}
else if (useFiltersCounter.Value < 13)
{
a = 1.75f;
b = 0.571f;
c = a;
d = b;
}
else
{
a = 2f;
b = 0.5f;
c = a;
d = b;
}
if (useFiltersCounter is null)
{
rangeDaysDeltaTolerance = _RangeDaysDeltaTolerance[1];
faceDistancePermyriad = _FaceDistancePermyriad;
faceConfidencePercent = _FaceConfidencePercent;
faceAreaPermille = _RangeFaceAreaPermilleTolerance[1];
}
else if (useFiltersCounter.Value is 1 or 5 or 9 or 13)
{
rangeDaysDeltaTolerance = _RangeDaysDeltaTolerance[1] * a;
faceDistancePermyriad = _FaceDistancePermyriad * c;
faceConfidencePercent = _FaceConfidencePercent * d;
faceAreaPermille = _RangeFaceAreaPermilleTolerance[1] * d;
}
else if (useFiltersCounter.Value is 2 or 6 or 10 or 14)
{
rangeDaysDeltaTolerance = _RangeDaysDeltaTolerance[1] * c;
faceDistancePermyriad = _FaceDistancePermyriad * a;
faceConfidencePercent = _FaceConfidencePercent * d;
faceAreaPermille = _RangeFaceAreaPermilleTolerance[1] * d;
}
else if (useFiltersCounter.Value is 3 or 7 or 11 or 15)
{
rangeDaysDeltaTolerance = _RangeDaysDeltaTolerance[1] * c;
faceDistancePermyriad = _FaceDistancePermyriad * c;
faceConfidencePercent = _FaceConfidencePercent * b;
faceAreaPermille = _RangeFaceAreaPermilleTolerance[1] * d;
}
else if (useFiltersCounter.Value is 4 or 8 or 12 or 16)
{
rangeDaysDeltaTolerance = _RangeDaysDeltaTolerance[1] * c;
faceDistancePermyriad = _FaceDistancePermyriad * c;
faceConfidencePercent = _FaceConfidencePercent * d;
faceAreaPermille = _RangeFaceAreaPermilleTolerance[1] * b;
}
else
{
rangeDaysDeltaTolerance = int.MaxValue;
faceDistancePermyriad = int.MaxValue;
faceAreaPermille = 0;
faceConfidencePercent = 0;
}
foreach (Sorting sorting in collection)
{
if (face.Mapping is null || faceDistanceEncoding.NormalizedPixelPercentage is null)
throw new NotSupportedException();
if (sorting.DaysDelta > rangeDaysDeltaTolerance)
{
_Days += 1;
continue;
}
if (sorting.DistancePermyriad > faceDistancePermyriad)
{
_Distance += 1;
continue;
}
if (face.Mapping.MappingFromLocation.ConfidencePercent < faceConfidencePercent)
{
_Confidence += 1;
continue;
}
if (face.Mapping.MappingFromLocation.AreaPermille < faceAreaPermille)
{
_Area += 1;
continue;
}
sortingContainer = new(face.Mapping, sorting);
results.Add(sortingContainer);
if (results.Count >= _SortingMaximumPerFaceShouldBeHigh)
break;
}
return results;
}
private static List<Sorting> GetSortingCollection(MapLogic mapLogic, List<FaceDistance> faceDistanceEncodings, int faceDistanceContainersLength, int i, FaceDistance faceDistanceEncoding)
{
List<Sorting> results;
List<FaceDistance> faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding);
if (faceDistanceLengths.Count != faceDistanceContainersLength)
throw new NotSupportedException();
results = mapLogic.GetSortingCollection(i, faceDistanceEncoding, faceDistanceLengths);
return results;
}
private static FaceDistanceContainer[] GetFaceDistanceContainers(Face[] distinctFilteredFaces)
{
FaceDistanceContainer[] results;
FaceDistance faceDistance;
FaceDistanceContainer faceDistanceContainer;
List<FaceDistanceContainer> collection = new();
foreach (Face face in distinctFilteredFaces)
{
if (face.Mapping is null)
throw new NotSupportedException();
if (face.FaceDistance?.Encoding is not FaceRecognitionDotNet.FaceEncoding faceEncoding)
continue;
faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, faceEncoding, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromItem.MinimumDateTime, face.Mapping.MappingFromLocation.NormalizedPixelPercentage);
faceDistanceContainer = new(face, faceDistance);
collection.Add(faceDistanceContainer);
}
results = collection.ToArray();
return results;
}
private static List<FaceDistance> GetFaceDistanceEncodings(FaceDistanceContainer[] faceDistanceContainers)
{
List<FaceDistance> faceDistanceEncodings = new();
foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers)
{
if (faceDistanceContainer.FaceDistance.Encoding is null)
continue;
faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance);
}
return faceDistanceEncodings;
}
public SortingContainer[] SetFaceMappingSortingCollectionThenGetSortingContainers(int maxDegreeOfParallelism, long ticks, MapLogic mapLogic, Face[] distinctFilteredFaces, int? useFiltersCounter)
{
SortingContainer[] results;
List<SortingContainer> collection = new();
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
FaceDistanceContainer[] faceDistanceContainers = GetFaceDistanceContainers(distinctFilteredFaces);
List<FaceDistance> faceDistanceEncodings = GetFaceDistanceEncodings(faceDistanceContainers);
string message = $") {faceDistanceContainers.Length:000} Get Sorting Containers Then Set Face Mapping Sorting Collection - {totalSeconds} total second(s)";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(faceDistanceContainers.Length, message, options);
_ = Parallel.For(0, faceDistanceContainers.Length, parallelOptions, (i, state) =>
{
progressBar.Tick();
Face face = faceDistanceContainers[i].Face;
if (face.Mapping is null)
throw new NotSupportedException();
FaceDistance faceDistanceEncoding = faceDistanceContainers[i].FaceDistance;
if (mapLogic.Used(faceDistanceEncoding))
return;
List<Sorting> sortingCollection = GetSortingCollection(mapLogic, faceDistanceEncodings, faceDistanceContainers.Length, i, faceDistanceEncoding);
if (!sortingCollection.Any())
return;
List<SortingContainer> sortingContainers = GetSortingContainers(face, faceDistanceEncoding, sortingCollection, useFiltersCounter);
if (sortingContainers.Any())
{
lock (collection)
collection.AddRange(sortingContainers);
}
});
if (!collection.Any())
results = Array.Empty<SortingContainer>();
else
results = Shared.Models.Stateless.Methods.ISortingContainer.Sort(collection);
return results;
}
public static Mapping[] GetSelectedMappingCollection(Face[] distinctFilteredFaces)
{
Mapping[] results;
IEnumerable<Mapping> collection = from l in distinctFilteredFaces orderby l.Mapping?.MappingFromItem.MinimumDateTime descending select l.Mapping;
results = (from l in collection where l is not null select l).ToArray();
return results;
}
public static void SetFaceDistances(int maxDegreeOfParallelism, long ticks, Face[] distinctFilteredFaces)
{
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
string message = $") {distinctFilteredFaces.Length:000} Load Face Encoding - {totalSeconds} total second(s)";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(distinctFilteredFaces.Length, message, options);
_ = Parallel.For(0, distinctFilteredFaces.Length, parallelOptions, (i, state) =>
{
Face face = distinctFilteredFaces[i];
if (face.FaceEncoding is null || face.Mapping is null)
throw new NotSupportedException();
if (face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding faceEncoding)
return;
progressBar.Tick();
faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding);
FaceDistance faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, faceEncoding, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromItem.MinimumDateTime, face.Mapping.MappingFromLocation.NormalizedPixelPercentage);
lock (face)
face.SetFaceDistance(faceDistance);
});
}
void Shared.Models.Methods.IMapLogicSupport.SavePossiblyNewPersonContainers(IPropertyConfiguration propertyConfiguration, string personBirthdayFormat, string facesFileNameExtension, string? a2PeopleSingletonDirectory, Dictionary<long, PersonContainer> personKeyToPersonContainer, List<(string[], PersonContainer)> possiblyNewPersonDisplayDirectoryNamesAndPersonContainer)
{
char @char;
string json;
string[] files;
string checkFile;
string[] segments;
const int zero = 0;
string personKeyFormatted;
string personDisplayDirectory;
PersonBirthday personBirthday;
string personDisplayDirectoryName;
string checkPersonDisplayDirectory;
string checkPersonKeyFormattedDirectory;
char[] chars = Shared.Models.Stateless.Methods.IAge.GetChars();
JsonSerializerOptions jsonSerializerOptions = new() { WriteIndented = true };
foreach ((string[] personDisplayDirectoryNames, PersonContainer personContainer) in possiblyNewPersonDisplayDirectoryNamesAndPersonContainer)
{
if (a2PeopleSingletonDirectory is null || personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any())
continue;
personBirthday = personContainer.Birthdays[zero];
personDisplayDirectoryName = personDisplayDirectoryNames[^1];
personDisplayDirectory = Path.Combine(personDisplayDirectoryNames);
personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthdayFormat, personBirthday);
segments = personDisplayDirectoryName.Split(chars);
if (segments.Length != 2)
@char = '_';
else
@char = personDisplayDirectoryName[segments[zero].Length];
checkPersonDisplayDirectory = Path.Combine(a2PeopleSingletonDirectory, @char.ToString(), personDisplayDirectoryName);
checkPersonKeyFormattedDirectory = Path.Combine(checkPersonDisplayDirectory, personKeyFormatted);
if (Directory.Exists(checkPersonKeyFormattedDirectory))
continue;
_ = Directory.CreateDirectory(checkPersonKeyFormattedDirectory);
checkFile = Path.Combine(checkPersonKeyFormattedDirectory, $"{personKeyFormatted}.json");
json = JsonSerializer.Serialize(personContainer.Person, jsonSerializerOptions);
_ = Shared.Models.Stateless.Methods.IPath.WriteAllText(checkFile, json, updateDateWhenMatches: false, compareBeforeWrite: true);
if (!Directory.Exists(personDisplayDirectory))
continue;
files = Directory.GetFiles(personDisplayDirectory, $"*{facesFileNameExtension}", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
checkFile = Path.Combine(checkPersonDisplayDirectory, Path.GetFileName(file));
if (File.Exists(checkFile))
continue;
File.Copy(files[0], checkFile);
break;
}
}
}
public static Dictionary<int, Dictionary<int, Mapping>> GetIdToNormalizedPixelPercentageToFace(Mapping[] mappingCollection)
{
Dictionary<int, Dictionary<int, Mapping>> results = new();
Dictionary<int, Mapping> keyValuePairs;
foreach (Mapping mapping in mappingCollection)
{
if (!results.ContainsKey(mapping.MappingFromItem.Id))
results.Add(mapping.MappingFromItem.Id, new());
keyValuePairs = results[mapping.MappingFromItem.Id];
if (keyValuePairs.ContainsKey(mapping.MappingFromLocation.NormalizedPixelPercentage))
throw new NotSupportedException();
keyValuePairs.Add(mapping.MappingFromLocation.NormalizedPixelPercentage, mapping);
}
return results;
}
string Shared.Models.Methods.IMapLogicSupport.GetCounts()
{
string result;
List<(int Value, string Name)> results = new()
{
new(_Area, nameof(_Area)),
new(_Confidence, nameof(_Confidence)),
new(_Days, nameof(_Days)),
new(_Distance, nameof(_Distance))
};
result = string.Join(' ', from l in results orderby l.Value descending select $"{l.Name}_{l.Value};");
return result;
}
private static bool TryToFind(string a2PeopleSingletonDirectory, string file, string path)
{
bool result = false;
string? pathName = Path.GetFileName(path);
string? group = Path.GetDirectoryName(path);
string? groupName = Path.GetFileName(group);
if (pathName is not null && group is not null && groupName is not null)
{
WindowsShortcut windowsShortcut;
string checkDirectory = Path.Combine(a2PeopleSingletonDirectory, groupName, pathName);
if (Directory.Exists(checkDirectory))
{
try
{
windowsShortcut = new() { Path = checkDirectory };
windowsShortcut.Save(file);
windowsShortcut.Dispose();
result = true;
}
catch (Exception)
{ }
}
}
return result;
}
public static void BeforeSaveResizedImagesByPersonKeyFormatted(string[] jLinks, string a2PeopleSingletonDirectory)
{
string[] files;
string checkDirectory;
WindowsShortcut windowsShortcut;
foreach (string directoryName in jLinks)
{
checkDirectory = Path.Combine(a2PeopleSingletonDirectory, directoryName);
if (!Directory.Exists(checkDirectory))
continue;
files = Directory.GetFiles(checkDirectory, "*.lnk", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
windowsShortcut = WindowsShortcut.Load(file);
if (windowsShortcut.Path is null)
continue;
if (!Directory.Exists(windowsShortcut.Path))
{
if (!TryToFind(a2PeopleSingletonDirectory, file, windowsShortcut.Path))
continue;
}
}
}
}
}