using System.Text; using System.Text.Json; 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.Stateless; namespace View_by_Distance.Instance.Models; /// // List /// internal class E_Distance { private readonly Serilog.ILogger? _Log; private readonly Configuration _Configuration; private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions; internal E_Distance(Configuration configuration) { _Configuration = configuration; _Log = Serilog.Log.ForContext(); _WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true }; } public override string ToString() { string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); return result; } private static void LoadFaceEncodingCollections(PropertyHolder[] filteredPropertyHolderCollection, List> faceCollections, List locationIndicesCollection, List faceEncodingCollection, List> faceEncodingCollections) { List faceCollection; FaceEncoding faceEncoding; for (int i = 0; i < filteredPropertyHolderCollection.Length; i++) { faceCollection = faceCollections[i]; if (!faceCollection.Any()) throw new Exception(); faceEncodingCollections.Add(new List()); for (int j = 0; j < faceCollection.Count; j++) { if (!faceCollection[j].Populated) continue; faceEncoding = FaceRecognition.LoadFaceEncoding(faceCollection[j].FaceEncoding.RawEncoding); faceEncodingCollection.Add(faceEncoding); faceEncodingCollections[i].Add(faceEncoding); locationIndicesCollection.Add(new int[] { i, j }); } } } private List> GetOrderedNoFaceCollection(List> faceCollections, int i, D_Face face) { List> results = new() { new(face, string.Empty) }; if (_Configuration.MaxItemsInDistanceCollection is null) throw new Exception(); for (int n = 0; n < faceCollections.Count; n++) { if (i == n) continue; for (int j = 0; j < faceCollections[n].Count; j++) results.Add(new(faceCollections[n][j], string.Empty)); } for (int r = results.Count - 1; r > _Configuration.MaxItemsInDistanceCollection.Value; r--) results.RemoveAt(r); return results; } private List GetValues(List> faceCollections, List locationIndicesCollection, List faceDistances) { List results = new(); if (_Configuration.LocationConfidenceFactor is null) throw new Exception(); if (_Configuration.DistanceFactor is null) throw new Exception(); D_Face face; int[] locationIndices; for (int d = 0; d < faceDistances.Count; d++) { locationIndices = locationIndicesCollection[d]; face = faceCollections[locationIndices[0]][locationIndices[1]]; if (face.Populated && face.LocationIndex is not null && locationIndices[1] != face.LocationIndex) throw new Exception(); results.Add(new double[] { d, faceDistances[d], (faceDistances[d] * _Configuration.DistanceFactor.Value) + face.Location.Confidence * _Configuration.LocationConfidenceFactor.Value / 10 }); } results = (from l in results orderby l[2] select l).ToList(); return results; } private List> GetOrderedFaceCollection(List> faceCollections, List locationIndicesCollection, List indicesAndValues) { List> results = new(); if (_Configuration.MaxItemsInDistanceCollection is null) throw new Exception(); int[] locationIndices; for (int t = 0; t < indicesAndValues.Count; t++) { locationIndices = locationIndicesCollection[(int)indicesAndValues[t][0]]; results.Add(new(faceCollections[locationIndices[0]][locationIndices[1]], string.Join('|', (from l in indicesAndValues[t] select l.ToString("0.000")).ToArray(), 1, indicesAndValues[t].Length - 1))); } for (int r = results.Count - 1; r > _Configuration.MaxItemsInDistanceCollection.Value; r--) results.RemoveAt(r); return results; } private static string GetText(string fileNameWithoutExtension, List> faceCollections, List locationIndicesCollection, List indicesAndValues) { string result; D_Face face; int[] locationIndices; StringBuilder tvs = new(); _ = tvs.Append("FileNameWithoutExtension").Append('\t').Append("LocationIndex").Append('\t').Append("FaceConfidence").Append('\t').Append("FaceDistance").Append('\t').Append("FactoredValue").AppendLine(); for (int t = 0; t < indicesAndValues.Count; t++) { locationIndices = locationIndicesCollection[(int)indicesAndValues[t][0]]; face = faceCollections[locationIndices[0]][locationIndices[1]]; if (face.Populated && face.LocationIndex is not null && locationIndices[1] != face.LocationIndex) throw new Exception(); _ = tvs.Append(fileNameWithoutExtension).Append('\t').Append(face.LocationIndex).Append('\t').Append(face.Location.Confidence).Append('\t').Append(indicesAndValues[t][1]).Append('\t').Append(indicesAndValues[t][2]).AppendLine(); } result = tvs.ToString(); return result; } private void LoadOrCreateThenSaveDistanceResultsForOutputResolutionsLoop(Property.Models.Configuration configuration, List> faceCollections, int subFilesCount, int i, List faceCollection, List locationIndicesCollection, List> subFileTuples, List faceEncodingCollection, List faceEncodingCollections, string fileNameWithoutExtension, string jsonDirectory, string tvsDirectory, bool updateDateWhenMatches, DateTime? updateToWhenMatches) { string text; string json; string jsonFile; List> orderedFaceCollection; if (!Directory.Exists(jsonDirectory)) _ = Directory.CreateDirectory(jsonDirectory); if (!Directory.Exists(tvsDirectory)) _ = Directory.CreateDirectory(tvsDirectory); if (!faceEncodingCollections.Any()) { int j = 0; orderedFaceCollection = GetOrderedNoFaceCollection(faceCollections, i, faceCollection[j]); json = JsonSerializer.Serialize(orderedFaceCollection, _WriteIndentedJsonSerializerOptions); jsonFile = Path.Combine(jsonDirectory, $"{j} - {fileNameWithoutExtension}.json"); if (Property.Models.Stateless.IPath.WriteAllText(jsonFile, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: updateToWhenMatches)) subFileTuples.Add(new Tuple(nameof(E_Distance), DateTime.Now)); } else { string tvsFile; List faceDistances; List indicesAndValues; for (int j = 0; j < faceEncodingCollections.Count; j++) { if (!faceCollection[j].Populated) continue; tvsFile = Path.Combine(tvsDirectory, $"{j} - {fileNameWithoutExtension}.tvs"); jsonFile = Path.Combine(jsonDirectory, $"{j} - {fileNameWithoutExtension}.json"); faceDistances = FaceRecognition.FaceDistances(faceEncodingCollection, faceEncodingCollections[j]); indicesAndValues = GetValues(faceCollections, locationIndicesCollection, faceDistances); orderedFaceCollection = GetOrderedFaceCollection(faceCollections, locationIndicesCollection, indicesAndValues); text = GetText(fileNameWithoutExtension, faceCollections, locationIndicesCollection, indicesAndValues); if (Property.Models.Stateless.IPath.WriteAllText(tvsFile, text, updateDateWhenMatches, compareBeforeWrite: true)) subFileTuples.Add(new Tuple(nameof(E_Distance), DateTime.Now)); json = JsonSerializer.Serialize(orderedFaceCollection, _WriteIndentedJsonSerializerOptions); if (Property.Models.Stateless.IPath.WriteAllText(jsonFile, json, updateDateWhenMatches, compareBeforeWrite: true)) subFileTuples.Add(new Tuple(nameof(E_Distance), DateTime.Now)); } } } private void LoadOrCreateThenSaveDistanceResultsForOutputResolutions(Property.Models.Configuration configuration, PropertyHolder[] filteredPropertyHolderCollection, List> faceCollections, List directories, bool updateDateWhenMatches, DateTime? updateToWhenMatches) { FileHolder? fileHolder; List locationIndicesCollection = new(); List> subFileTuples = new(); List faceEncodingCollection = new(); List> faceEncodingCollections = new(); LoadFaceEncodingCollections(filteredPropertyHolderCollection, faceCollections, locationIndicesCollection, faceEncodingCollection, faceEncodingCollections); if (faceEncodingCollections.Count != faceCollections.Count) throw new Exception(); if (locationIndicesCollection.Count != faceEncodingCollection.Count) throw new Exception(); for (int i = 0; i < filteredPropertyHolderCollection.Length; i++) { fileHolder = filteredPropertyHolderCollection[i].ImageFileHolder; if (fileHolder is null) continue; LoadOrCreateThenSaveDistanceResultsForOutputResolutionsLoop(configuration, faceCollections, filteredPropertyHolderCollection.Length, i, faceCollections[i], locationIndicesCollection, subFileTuples, faceEncodingCollection, faceEncodingCollections[i], fileHolder.NameWithoutExtension, directories[i][0], directories[i][1], updateDateWhenMatches, updateToWhenMatches); } } internal void LoadOrCreateThenSaveDistanceResultsForOutputResolutions(Property.Models.Configuration configuration, Model? model, PredictorModel? predictorModel, string sourceDirectory, string outputResolution, List> sourceDirectoryChanges, PropertyHolder[] filteredPropertyHolderCollection, List> faceCollections) { if (_Configuration.CheckJsonForDistanceResults is null) throw new Exception(); if (_Configuration.PropertiesChangedForDistance is null) throw new Exception(); string json; bool check = false; string parentCheck; FileHolder? fileHolder; DateTime? dateTime = null; FileInfo[] fileInfoCollection; bool updateDateWhenMatches = false; List directories = new(); System.IO.DirectoryInfo directoryInfo; System.IO.DirectoryInfo tvsDirectoryInfo; string[] changesFrom = new string[] { nameof(A_Property), nameof(B_Metadata), nameof(C_Resize), nameof(D_Face) }; List dateTimes = (from l in sourceDirectoryChanges where changesFrom.Contains(l.Item1) select l.Item2).ToList(); List directoryInfoCollection = Property.Models.Stateless.IResult.GetDirectoryInfoCollection(configuration, model, predictorModel, sourceDirectory, nameof(E_Distance), outputResolution, includeResizeGroup: true, includeModel: true, includePredictorModel: true, contentDescription: ".tvs File", singletonDescription: string.Empty, collectionDescription: "n json file(s) for each face found (one to many)"); for (int i = 0; i < filteredPropertyHolderCollection.Length; i++) { fileHolder = filteredPropertyHolderCollection[i].ImageFileHolder; if (fileHolder is null) continue; directoryInfo = new System.IO.DirectoryInfo(Path.Combine(directoryInfoCollection[0].Replace("<>", "[]"), fileHolder.NameWithoutExtension)); if (!directoryInfo.Exists) { if (directoryInfo.Parent?.Parent is null) throw new Exception(); parentCheck = Path.Combine(directoryInfo.Parent.Parent.FullName, directoryInfo.Name); if (Directory.Exists(parentCheck)) { foreach (string file in Directory.GetFiles(parentCheck)) File.Delete(file); Directory.Delete(parentCheck); } } tvsDirectoryInfo = new System.IO.DirectoryInfo(Path.Combine(directoryInfoCollection[0].Replace("<>", "()"), fileHolder.NameWithoutExtension)); directories.Add(new string[] { directoryInfo.FullName, tvsDirectoryInfo.FullName }); if (_Configuration.CheckJsonForDistanceResults.Value && directoryInfo.Exists) { fileInfoCollection = directoryInfo.GetFiles("*.json", SearchOption.AllDirectories); for (int j = 0; j < fileInfoCollection.Length; j++) { json = Shared.Models.Stateless.Methods.IIndex.GetJson(fileInfoCollection[j].FullName, fileInfoCollection[j]); if (!_Configuration.PropertiesChangedForDistance.Value && Shared.Models.Stateless.Methods.IFace.GetFace(fileInfoCollection[j].FullName) is null) check = true; } } if (check) continue; if (_Configuration.PropertiesChangedForDistance.Value) check = true; else if (!directoryInfo.Exists) check = true; else if (!tvsDirectoryInfo.Exists) check = true; else if (dateTimes.Any() && dateTimes.Max() > directoryInfo.LastWriteTime) check = true; if (check && !updateDateWhenMatches) { updateDateWhenMatches = dateTimes.Any() && directoryInfo.Exists && dateTimes.Max() > directoryInfo.LastWriteTime; dateTime = !updateDateWhenMatches ? null : dateTimes.Max(); } } if (check) LoadOrCreateThenSaveDistanceResultsForOutputResolutions(configuration, filteredPropertyHolderCollection, faceCollections, directories, updateDateWhenMatches, updateToWhenMatches: dateTime); _ = Property.Models.Stateless.IPath.DeleteEmptyDirectories(directoryInfoCollection[0].Replace("<>", "()")); } private List<(string, List>)> GetFiles(Property.Models.Configuration configuration, Model? model, PredictorModel? predictorModel, string outputResolution) { string json; List>? facesKeyValuePairCollection; List<(string, List>)> results = new(); string dFacesCollectionDirectory = Path.Combine(Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(configuration, model, predictorModel, nameof(D_Face), outputResolution, includeResizeGroup: true, includeModel: true, includePredictorModel: true), "[[]]"); string[] dFacesCollectionFiles = Directory.GetFiles(dFacesCollectionDirectory, "*.json", SearchOption.TopDirectoryOnly); foreach (string dFacesCollectionFile in dFacesCollectionFiles) { json = File.ReadAllText(dFacesCollectionFile); facesKeyValuePairCollection = JsonSerializer.Deserialize>>(json); if (facesKeyValuePairCollection is null) continue; results.Add(new(dFacesCollectionFile, facesKeyValuePairCollection)); } return results; } private static List<(string, List, List)> GetMatches(List<(string, List>)> files) { List<(string, List, List)> results = new(); FaceEncoding faceEncoding; List faces; List faceEncodings; foreach ((string, List>) file in files) { faces = new(); faceEncodings = new(); foreach (KeyValuePair keyValuePair in file.Item2) { foreach (Shared.Models.Face face in keyValuePair.Value) { if (!face.Populated) continue; faces.Add(face); faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); faceEncodings.Add(faceEncoding); } } results.Add(new(file.Item1, faces, faceEncodings)); } return results; } private static int GetIndex(double[] faceDistances) { int result; List faceDistancesWithIndex = new(); for (int y = 0; y < faceDistances.Length; y++) faceDistancesWithIndex.Add(new double[] { faceDistances[y], y }); faceDistancesWithIndex = (from l in faceDistancesWithIndex orderby l[0] select l).ToList(); result = (int)faceDistancesWithIndex[0][1]; return result; } private void Save(Property.Models.Configuration configuration, Model? model, PredictorModel? predictorModel, string outputResolution, string eDistanceCollectionDirectory, int k, string relativePath, Shared.Models.Face face, List> faceAndFaceDistanceCollection) { if (string.IsNullOrEmpty(eDistanceCollectionDirectory)) eDistanceCollectionDirectory = Path.Combine(Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(configuration, model, predictorModel, nameof(E_Distance), outputResolution, includeResizeGroup: true, includeModel: true, includePredictorModel: true), "[]"); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(face.RelativePath); string jsonDirectory = string.Concat(eDistanceCollectionDirectory, Path.Combine(relativePath, fileNameWithoutExtension)); if (!Directory.Exists(jsonDirectory)) _ = Directory.CreateDirectory(jsonDirectory); string json = JsonSerializer.Serialize(faceAndFaceDistanceCollection, _WriteIndentedJsonSerializerOptions); string jsonFile = Path.Combine(jsonDirectory, $"{k} - {fileNameWithoutExtension}.nosj"); _ = Property.Models.Stateless.IPath.WriteAllText(jsonFile, json, updateDateWhenMatches: true, compareBeforeWrite: true); } private static Tuple Get(FaceEncoding faceEncoding, (string, List, List) match) { Tuple result; double[] faceDistances = FaceRecognition.FaceDistances(match.Item3, faceEncoding).ToArray(); int index = GetIndex(faceDistances); result = new(match.Item2[index], faceDistances[index]); return result; } internal void LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions(Property.Models.Configuration configuration, Model? model, PredictorModel? predictorModel, string outputResolution) { if (_Log is null) throw new ArgumentNullException(nameof(_Log)); string? relativePath; Shared.Models.Face face; ParallelOptions parallelOptions = new(); FaceEncoding faceEncoding; string eDistanceCollectionDirectory = string.Empty; Tuple faceAndFaceDistance; List> faceAndFaceDistanceCollection; List<(string, List>)> files = GetFiles(configuration, model, predictorModel, outputResolution); List<(string, List, List)> matches = GetMatches(files); if (files.Count != matches.Count) throw new Exception(); int filesCount = files.Count; for (int i = 0; i < filesCount; i++) { if (_Configuration.CrossDirectoryMaxItemsInDistanceCollection is null) continue; _Log.Debug(string.Concat("LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions - ", nameof(outputResolution), ' ', outputResolution, " - ", i, " of ", filesCount)); for (int j = 0; j < files[i].Item2.Count; j++) { if (!matches[i].Item2.Any()) continue; for (int k = 0; k < files[i].Item2[j].Value.Length; k++) { if (!files[i].Item2[j].Value[k].Populated) continue; face = files[i].Item2[j].Value[k]; faceAndFaceDistanceCollection = new(matches.Count); relativePath = Path.GetDirectoryName(face.RelativePath); if (string.IsNullOrEmpty(relativePath)) continue; faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); _ = Parallel.For(0, matches.Count, parallelOptions, z => { if (z != i && matches[z].Item2.Any()) { faceAndFaceDistance = Get(faceEncoding, matches[z]); // if (faceAndFaceDistance.Item2 < _Configuration.) faceAndFaceDistanceCollection.Add(new(faceAndFaceDistance.Item1, faceAndFaceDistance.Item2.ToString("0.000"))); } }); if (faceAndFaceDistanceCollection.Any()) { faceAndFaceDistanceCollection = (from l in faceAndFaceDistanceCollection orderby l.Item2 select l).Take(_Configuration.CrossDirectoryMaxItemsInDistanceCollection.Value).ToList(); Save(configuration, model, predictorModel, outputResolution, eDistanceCollectionDirectory, k, relativePath, face, faceAndFaceDistanceCollection); } } } } } public static double GetStandardDeviation(IEnumerable values, double average) { double result = 0; if (!values.Any()) throw new Exception("Collection must have at least one value!"); double sum = values.Sum(l => (l - average) * (l - average)); result = Math.Sqrt(sum / values.Count()); return result; } internal static List<(string[], Shared.Models.PersonBirthday, FaceEncoding[])> GetGroupedFaceEncodings(string argZero, List propertyHolderCollections) { List<(string[], Shared.Models.PersonBirthday, FaceEncoding[])> results = new(); double lcl; double ucl; double sum; double average; int lowestIndex; double lowestSum; const int zero = 0; double standardDeviation; FaceEncoding faceEncoding; List faceDistances; List faceEncodings; Dictionary> keyValuePairs = PropertyHolder.GetKeyValuePairs(argZero, propertyHolderCollections); foreach (KeyValuePair> keyValuePair in keyValuePairs) { lowestIndex = 0; faceEncodings = new(); lowestSum = double.MaxValue; foreach ((string[] directories, Shared.Models.PersonBirthday personBirthday, Shared.Models.Properties.IFace face) in keyValuePair.Value) { if (!face.Populated) continue; faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); faceEncodings.Add(faceEncoding); } if (keyValuePair.Value.Count != faceEncodings.Count) continue; if (faceEncodings.Count < 2) continue; for (int i = 0; i < faceEncodings.Count; i++) { faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncodings[i]); sum = faceDistances.Sum(); if (sum > lowestSum) continue; lowestIndex = i; lowestSum = sum; } faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncodings[lowestIndex]); sum = faceDistances.Sum(); if (sum != lowestSum) continue; average = faceDistances.Average(); standardDeviation = GetStandardDeviation(faceDistances, average); lcl = average - (standardDeviation * 3); ucl = average + (standardDeviation * 3); for (int i = faceEncodings.Count; i > -1; i--) { if (faceDistances[i] < lcl || faceDistances[i] > ucl) faceEncodings.RemoveAt(i); } results.Add(new(keyValuePair.Value[zero].Directories, keyValuePair.Value[zero].PersonBirthday, faceEncodings.ToArray())); } return results; } internal static void SaveGroupedFaceEncodings(List<(string[], Shared.Models.PersonBirthday, FaceEncoding[])> collection, Dictionary> peopleCollection, string eDistanceCollectionDirectory) { string json; string checkFile; string personKey; string directory; List rawEncodings; Shared.Models.Person person; const string pattern = @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]"; foreach ((string[] directories, Shared.Models.PersonBirthday personBirthday, FaceEncoding[] faceEncodings) in collection) { directories[0] = eDistanceCollectionDirectory; rawEncodings = new(); checkFile = string.Empty; directory = Path.Combine(directories); personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); person = peopleCollection[personKey][0]; checkFile = string.Concat(directory, " - ", Regex.Replace(Shared.Models.Stateless.Methods.IPersonName.GetFullName(person.Name), pattern, string.Empty), ".json"); if (string.IsNullOrEmpty(checkFile)) continue; if (!Directory.Exists(directory)) _ = Directory.CreateDirectory(directory); for (int i = 0; i < faceEncodings.Length; i++) rawEncodings.Add(faceEncodings[i].GetRawEncoding()); json = JsonSerializer.Serialize(rawEncodings, new JsonSerializerOptions { WriteIndented = true }); _ = Property.Models.Stateless.IPath.WriteAllText(checkFile, json, updateDateWhenMatches: true, compareBeforeWrite: true); } } public static void GetKeyValuePairs(string argZero, List propertyHolderCollections, List<(string[], Shared.Models.PersonBirthday, FaceEncoding[])> collection) { double average; string personKey; bool? isWrongYear; TimeSpan? timeSpan; double lowestAverage; string[] directories; string isWrongYearFlag; FaceEncoding faceEncoding; List faceDistances; const string facePopulatedKey = "MatchImages"; Shared.Models.PersonBirthday? lowestPersonBirthday; foreach (PropertyHolder[] propertyHolderCollection in propertyHolderCollections) { lowestPersonBirthday = null; lowestAverage = double.MaxValue; if (!propertyHolderCollection.Any()) continue; if (!propertyHolderCollection[0].SourceDirectory.StartsWith(argZero)) continue; foreach (PropertyHolder propertyHolder in propertyHolderCollection) { if (propertyHolder.ImageFileHolder is null || propertyHolder.Property is null || propertyHolder.Named.Any()) continue; if (propertyHolder.MinimumDateTime is null) continue; (isWrongYear, _) = propertyHolder.IsWrongYear(); foreach (Shared.Models.Properties.IFace face in propertyHolder.Faces) { if (!face.Populated) continue; faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); foreach ((string[] _, Shared.Models.PersonBirthday personBirthday, FaceEncoding[] faceEncodings) in collection) { faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncoding); average = faceDistances.Average(); if (average < lowestAverage) continue; lowestPersonBirthday = personBirthday; } if (lowestPersonBirthday is null) continue; personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(lowestPersonBirthday); timeSpan = Shared.Models.Stateless.Methods.IPersonBirthday.GetTimeSpan(propertyHolder.MinimumDateTime.Value, isWrongYear, lowestPersonBirthday); if (timeSpan.HasValue && timeSpan.Value.Ticks < 0) directories = new string[] { string.Empty, facePopulatedKey, personKey, "!---" }; else if (timeSpan.HasValue) directories = new string[] { string.Empty, facePopulatedKey, personKey, $"^{Math.Floor(timeSpan.Value.TotalDays / 365):000}" }; else { isWrongYearFlag = PropertyHolder.GetWrongYearFlag(isWrongYear); directories = new string[] { string.Empty, facePopulatedKey, personKey, $"{isWrongYearFlag}{propertyHolder.MinimumDateTime.Value:yyyy}" }; } } } } } internal static void GetClosest(string argZero, List propertyHolderCollections, List<(string[], Shared.Models.PersonBirthday, FaceEncoding[])> collection, Dictionary> peopleCollection, string eDistanceCollectionDirectory) { GetKeyValuePairs(argZero, propertyHolderCollections, collection); if (peopleCollection is null) { } if (string.IsNullOrEmpty(eDistanceCollectionDirectory)) { } // foreach (KeyValuePair> keyValuePair in keyValuePairs) // { // foreach ((string[] directories, Shared.Models.PersonBirthday personBirthday, Shared.Models.Properties.IFace face) in keyValuePair.Value) // { // if (face.Populated) // continue; // } // } } }