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 GetSortingContainers(Face face, FaceDistance faceDistanceEncoding, List sortingCollection, int? useFiltersCounter) { List 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 GetSortingCollection(MapLogic mapLogic, List faceDistanceEncodings, int faceDistanceContainersLength, int i, FaceDistance faceDistanceEncoding) { List results; List 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 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 GetFaceDistanceEncodings(FaceDistanceContainer[] faceDistanceContainers) { List 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 collection = new(); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; FaceDistanceContainer[] faceDistanceContainers = GetFaceDistanceContainers(distinctFilteredFaces); List 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 sortingCollection = GetSortingCollection(mapLogic, faceDistanceEncodings, faceDistanceContainers.Length, i, faceDistanceEncoding); if (!sortingCollection.Any()) return; List sortingContainers = GetSortingContainers(face, faceDistanceEncoding, sortingCollection, useFiltersCounter); if (sortingContainers.Any()) { lock (collection) collection.AddRange(sortingContainers); } }); if (!collection.Any()) results = Array.Empty(); else results = Shared.Models.Stateless.Methods.ISortingContainer.Sort(collection); return results; } public static Mapping[] GetSelectedMappingCollection(Face[] distinctFilteredFaces) { Mapping[] results; IEnumerable 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 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> GetIdToNormalizedPixelPercentageToFace(Mapping[] mappingCollection) { Dictionary> results = new(); Dictionary 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; } } } } }