using ShellProgressBar; using System.Collections.ObjectModel; using System.Text.Json; using View_by_Distance.Distance.Models.Stateless; using View_by_Distance.FaceRecognitionDotNet; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Methods; namespace View_by_Distance.Distance.Models; public partial class E_Distance : IDistance { private readonly List _Moved; private readonly List _Debug; private readonly List _Renamed; private readonly Serilog.ILogger? _Log; private readonly int _FaceConfidencePercent; private readonly bool _DistanceRenameToMatch; private readonly float[] _RangeFaceConfidence; private readonly bool _DistanceMoveUnableToMatch; private readonly float _RectangleIntersectMinimum; private readonly List _AllMappedFaceFiles; private readonly List _AllMappedFaceFileNames; private readonly double _RangeDistanceToleranceAverage; private readonly List _DuplicateMappedFaceFiles; public E_Distance(bool distanceMoveUnableToMatch, bool distanceRenameToMatch, int faceConfidencePercent, float[] rangeDistanceTolerance, float[] rangeFaceConfidence, float[] rectangleIntersectMinimums) { _Debug = new(); _Moved = new(); _Renamed = new(); _AllMappedFaceFiles = new(); _AllMappedFaceFileNames = new(); _DuplicateMappedFaceFiles = new(); _RangeFaceConfidence = rangeFaceConfidence; _Log = Serilog.Log.ForContext(); _DistanceRenameToMatch = distanceRenameToMatch; _FaceConfidencePercent = faceConfidencePercent; _DistanceMoveUnableToMatch = distanceMoveUnableToMatch; _RectangleIntersectMinimum = rectangleIntersectMinimums.Max(); _RangeDistanceToleranceAverage = rangeDistanceTolerance.Average(); } private static void MoveUnableToMatch(string file) { string checkFile = $"{file}.unk"; if (File.Exists(file) && !File.Exists(checkFile)) File.Move(file, checkFile); } private FaceDistanceContainer[] GetFaceDistanceContainers(MappingFromItem mappingFromItem, List intersectFaces) { FaceDistanceContainer[] results; int wholePercentages; int confidencePercent; FaceDistance faceDistance; FaceDistanceContainer faceDistanceContainer; List collection = new(); foreach (Face face in intersectFaces) { if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) throw new NotSupportedException(); confidencePercent = Shared.Models.Stateless.Methods.ILocation.GetConfidencePercent(_FaceConfidencePercent, _RangeFaceConfidence, face.Location.Confidence); wholePercentages = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); if (face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding faceEncoding) faceDistance = new(confidencePercent, mappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, mappingFromItem.Id, mappingFromItem.IsWrongYear, wholePercentages); else { faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); faceDistance = new(confidencePercent, mappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, mappingFromItem.Id, mappingFromItem.IsWrongYear, wholePercentages); lock (intersectFaces) face.SetFaceDistance(faceDistance); } faceDistanceContainer = new(face, faceDistance); collection.Add(faceDistanceContainer); } results = collection.ToArray(); return results; } private static ReadOnlyCollection GetFaceDistanceEncodings(FaceDistanceContainer[] faceDistanceContainers) { List faceDistanceEncodings = new(); foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers) { if (faceDistanceContainer.FaceDistance.Encoding is null) continue; faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance); } return new(faceDistanceEncodings); } private List<(Face Face, double? Length)> GetValues(MappingFromItem mappingFromItem, List intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding) { List<(Face Face, double? Length)> results = new(); Face face; FaceDistance faceDistanceLength; FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding = FaceRecognition.LoadFaceEncoding(modelsFaceEncoding.RawEncoding); FaceDistance faceDistanceEncoding = new(faceRecognitionDotNetFaceEncoding); FaceDistanceContainer[] faceDistanceContainers = GetFaceDistanceContainers(mappingFromItem, intersectFaces); int faceDistanceContainersLength = faceDistanceContainers.Length; if (faceDistanceContainersLength != intersectFaces.Count) throw new NotSupportedException(); ReadOnlyCollection faceDistanceEncodings = GetFaceDistanceEncodings(faceDistanceContainers); if (faceDistanceEncodings.Count != intersectFaces.Count) throw new NotSupportedException(); List faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding); if (faceDistanceLengths.Count != faceDistanceContainersLength) throw new NotSupportedException(); for (int i = 0; i < intersectFaces.Count; i++) { face = intersectFaces[i]; faceDistanceLength = faceDistanceLengths[i]; if (faceDistanceLength.Length is null) throw new NotSupportedException(); results.Add(new(face, faceDistanceLength.Length.Value)); } return results; } private (Face, double?)[] GetClosestFaceByDistanceIgnoringTolerance(MappingFromItem mappingFromItem, List intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding) { (Face, double?)[] results; List<(Face Face, double? Length)> collection = GetValues(mappingFromItem, intersectFaces, modelsFaceEncoding); results = (from l in collection where l.Length < _RangeDistanceToleranceAverage orderby l.Length select l).Take(1).ToArray(); if (results.Length > 0) { (Face _, double? length) = results.First(); _Debug.Add(length); } return results; } private static List<(Face, double?)> GetMatchingFacesByFaceEncoding(Face[] filteredFaces, string? json) { List<(Face, double?)> results = new(); string check; foreach (Face face in filteredFaces) { if (json is null || face.FaceEncoding is null) continue; if (!json.Contains(face.FaceEncoding.RawEncoding[0].ToString())) continue; check = JsonSerializer.Serialize(face.FaceEncoding); if (check != json) continue; results.Add(new(face, 0)); } return results; } private static FileInfo? CheckFileThenGetFileInfo(string facesFileNameExtension, MappingFromItem mappingFromItem, string file, List<(Face, double?)> checkFaces) { FileInfo? result = null; string checkFile; string? mappedFaceDirectory; string deterministicHashCodeKey; foreach ((Face face, _) in checkFaces) { if (checkFaces.Count != 1) break; if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) throw new NotSupportedException(); mappedFaceDirectory = Path.GetDirectoryName(file); if (mappedFaceDirectory is null) throw new NotSupportedException(); deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(mappingFromItem.Id, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); checkFile = Path.Combine(mappedFaceDirectory, $"{deterministicHashCodeKey}{mappingFromItem.ImageFileHolder.ExtensionLowered}{facesFileNameExtension}"); if (checkFile == file) continue; result = new FileInfo(checkFile); } return result; } private void AppendMatchingDuplicates(string file, string[] matches) { string checkFile; FileInfo fileInfo = new(file); List<(long Length, string FullName)> collection = new(); if (fileInfo.Exists) collection.Add(new(fileInfo.Length, fileInfo.FullName)); lock (_DuplicateMappedFaceFiles) _DuplicateMappedFaceFiles.Add(file); foreach (string match in matches) { fileInfo = new(match); if (!fileInfo.Exists) continue; collection.Add(new(fileInfo.Length, fileInfo.FullName)); break; } collection = collection.OrderBy(l => l.Length).ToList(); for (int i = 0; i < collection.Count - 1; i++) { checkFile = string.Concat(collection[i].FullName, ".dup"); if (File.Exists(checkFile)) continue; File.Move(collection[i].FullName, checkFile); } } public void LookForMatchFacesAndPossiblyRename(string facesFileNameExtension, MappingFromItem mappingFromItem, List faces, ReadOnlyCollection> locationContainers) { string? json; string fileName; string[] matches; FileInfo? fileInfo; List intersectFaces; List<(Face, double?)> checkFaces = new(); Shared.Models.FaceEncoding? modelsFaceEncoding; Face[] filteredFaces = (from l in faces where l.FaceEncoding is not null && l.Location is not null && l.OutputResolution is not null select l).ToArray(); if (filteredFaces.Length != faces.Count) checkFaces.Clear(); foreach (LocationContainer? locationContainer in locationContainers) { if (_Renamed.Contains(locationContainer.File)) continue; fileName = Path.GetFileName(locationContainer.File); if (locationContainer.FromDistanceContent && _DuplicateMappedFaceFiles.Contains(fileName)) continue; checkFaces.Clear(); if (locationContainer.Directories.Count == 0) { if (locationContainer.FromDistanceContent) throw new NullReferenceException(nameof(locationContainer.Directories)); continue; } json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.Directories); if (json is null) { if (_DistanceMoveUnableToMatch) MoveUnableToMatch(locationContainer.File); continue; } if (filteredFaces.Length > 0) checkFaces.AddRange(GetMatchingFacesByFaceEncoding(filteredFaces, json)); if (checkFaces.Count == 1) _Debug.Add(0); if (checkFaces.Count != 1 && !string.IsNullOrEmpty(json)) { checkFaces.Clear(); modelsFaceEncoding = JsonSerializer.Deserialize(json); if (modelsFaceEncoding is null) throw new NotSupportedException(); if (filteredFaces.Length > 0) { intersectFaces = Shared.Models.Stateless.Methods.ILocation.FilterByIntersect(filteredFaces, _RectangleIntersectMinimum, locationContainer.WholePercentages); if (intersectFaces.Count > 0) checkFaces.AddRange(GetClosestFaceByDistanceIgnoringTolerance(mappingFromItem, intersectFaces, modelsFaceEncoding)); } } if (checkFaces.Count == 0) { if (_DistanceMoveUnableToMatch) MoveUnableToMatch(locationContainer.File); continue; } if (checkFaces.Count != 1) { if (_DistanceMoveUnableToMatch) MoveUnableToMatch(locationContainer.File); continue; } fileInfo = CheckFileThenGetFileInfo(facesFileNameExtension, mappingFromItem, locationContainer.File, checkFaces); if (fileInfo is not null) { if (_DistanceRenameToMatch && fileInfo is not null) { if (fileInfo.Exists) File.Delete(locationContainer.File); else File.Move(locationContainer.File, fileInfo.FullName); File.WriteAllText($"{fileInfo.FullName}.old", $"{fileInfo.FullName}{Environment.NewLine}{locationContainer.File}"); _Renamed.Add(locationContainer.File); } continue; } if (_AllMappedFaceFileNames.Contains(fileName)) { lock (_AllMappedFaceFiles) matches = (from l in _AllMappedFaceFiles where l != locationContainer.File && Path.GetFileName(l) == fileName select l).ToArray(); if (locationContainer.FromDistanceContent && matches.Length > 0) AppendMatchingDuplicates(locationContainer.File, matches); } if (!locationContainer.FromDistanceContent) continue; lock (_AllMappedFaceFiles) _AllMappedFaceFiles.Add(locationContainer.File); lock (_AllMappedFaceFileNames) _AllMappedFaceFileNames.Add(fileName); } } public void Clear() { if (_Log is null) throw new NullReferenceException(nameof(_Log)); double?[] debug = (from l in _Debug where l is null or not 0 select l).ToArray(); if (debug.Length > 0) { string debugMessage = $"{_Debug.Count - debug.Length} - {debug.Min()} - {_Debug.Max()}"; _Log.Info(debugMessage); } if (_DuplicateMappedFaceFiles.Count > 0) _Log.Info($"Renamed {_DuplicateMappedFaceFiles.Count} to *.dup file(s)"); if (_Moved.Count > 0 || _Renamed.Count > 0) throw new NotImplementedException("Restart!"); _Debug.Clear(); _Moved.Clear(); _Renamed.Clear(); _AllMappedFaceFiles.Clear(); _AllMappedFaceFileNames.Clear(); _DuplicateMappedFaceFiles.Clear(); } public ReadOnlyCollection GetMissingFaceDistanceContainer(int maxDegreeOfParallelism, long ticks, string dFacesCollectionDirectory, ReadOnlyDictionary>> missingIdThenWholePercentagesToPersonContainers) { List results = new(); string[] files; List? faces; int wholePercentages; int confidencePercent; bool? isWrongYear = null; FaceDistance faceDistance; List<(int id, string json)> collection = new(); FaceDistanceContainer faceDistanceContainer; foreach (KeyValuePair>> keyValuePair in missingIdThenWholePercentagesToPersonContainers) { files = Directory.GetFiles(dFacesCollectionDirectory, $"{keyValuePair.Key}*.json", SearchOption.TopDirectoryOnly); if (files.Length != 1) continue; collection.Add(new(keyValuePair.Key, Shared.Models.Stateless.Methods.IFace.GetJson(files[0]))); } int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; string message = $") {collection.Count:000} Setting missing distance containers - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(collection.Count, message, options); _ = Parallel.For(0, collection.Count, parallelOptions, (i, state) => { progressBar.Tick(); int id = collection[i].id; string json = collection[i].json; faces = JsonSerializer.Deserialize>(json); if (faces is null) throw new NullReferenceException(nameof(faces)); foreach (Face face in faces) { if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) continue; confidencePercent = Shared.Models.Stateless.Methods.ILocation.GetConfidencePercent(_FaceConfidencePercent, _RangeFaceConfidence, face.Location.Confidence); wholePercentages = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); if (face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding faceEncoding) faceDistance = new(confidencePercent, face.DateTime, faceEncoding, id, isWrongYear, wholePercentages); else { faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); faceDistance = new(confidencePercent, face.DateTime, faceEncoding, id, isWrongYear, wholePercentages); face.SetFaceDistance(faceDistance); } faceDistanceContainer = new(face, faceDistance); lock (results) results.Add(faceDistanceContainer); } }); return new(results); } 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); } public static void SetFaceDistances(int maxDegreeOfParallelism, long ticks, ReadOnlyCollection distinctFilteredFaces) { List faces = new(); foreach (Face face in distinctFilteredFaces) { if (face.Mapping?.MappingFromFilter is null) throw new NotSupportedException(); if (face.Mapping.MappingFromFilter.IsFocusPerson is not null && !face.Mapping.MappingFromFilter.IsFocusPerson.Value) continue; if (face.FaceEncoding is not null && face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding _) continue; faces.Add(face); } int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; string message = $") {faces.Count:000} Load Face Encoding - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(faces.Count, message, options); _ = Parallel.For(0, faces.Count, parallelOptions, (i, state) => { Face face = faces[i]; FaceRecognitionDotNet.FaceEncoding faceEncoding; if (face.FaceEncoding is null || face.Mapping?.MappingFromLocation is null) throw new NotSupportedException(); progressBar.Tick(); faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); FaceDistance faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, face.Mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromLocation.WholePercentages); lock (face) face.SetFaceDistance(faceDistance); }); } private static List GetSortingContainers(Map.Models.Configuration mapConfiguration, IDistanceLimits distanceLimits, Face face, FaceDistance faceDistanceEncoding, List sortingCollection) { List results = new(); SortingContainer sortingContainer; int days = 0, distance = 0; Sorting[] collection = Shared.Models.Stateless.Methods.ISorting.Sort(sortingCollection); foreach (Sorting sorting in collection) { if (face.Mapping?.MappingFromLocation is null || faceDistanceEncoding.WholePercentages is null) throw new NotSupportedException(); if (!mapConfiguration.SaveSortingWithoutPerson && face.Mapping.MappingFromPerson is null) continue; if (sorting.DaysDelta > distanceLimits.RangeDaysDeltaTolerance) { days += 1; continue; } if (sorting.DistancePermyriad > distanceLimits.FaceDistancePermyriad) { distance += 1; continue; } sortingContainer = new(face.Mapping, sorting); results.Add(sortingContainer); if (results.Count >= distanceLimits.SortingMaximumPerFaceShouldBeHigh) break; } distanceLimits.AddCounts(days, distance); return results; } private static List GetSortingCollection(Map.Models.MapLogic mapLogic, ReadOnlyCollection faceDistanceEncodings, int i, FaceDistance faceDistanceEncoding) { List results; List faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding); results = mapLogic.GetSortingCollection(i, faceDistanceEncoding, faceDistanceLengths); return results; } public static ReadOnlyCollection GetFaceDistanceContainers(ReadOnlyCollection distinctFilteredFaces) { ReadOnlyCollection results; FaceDistance faceDistance; FaceDistanceContainer faceDistanceContainer; List collection = new(); foreach (Face face in distinctFilteredFaces) { if (face.Mapping?.MappingFromLocation is null) throw new NotSupportedException(); if (face.FaceDistance?.Encoding is not FaceRecognitionDotNet.FaceEncoding faceEncoding) continue; faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, face.Mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromLocation.WholePercentages); faceDistanceContainer = new(face, faceDistance); collection.Add(faceDistanceContainer); } results = new(collection.ToArray()); return results; } public static ReadOnlyCollection GetFaceDistanceEncodings(ReadOnlyCollection faceDistanceContainers, ReadOnlyCollection missingFaceDistanceContainers) { List faceDistanceEncodings = new(); foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers) { if (faceDistanceContainer.FaceDistance.Encoding is null) continue; faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance); } foreach (FaceDistanceContainer faceDistanceContainer in missingFaceDistanceContainers) { if (faceDistanceContainer.FaceDistance.Encoding is null) continue; faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance); } return new(faceDistanceEncodings); } public static FaceDistanceContainer[] FilteredFaceDistanceContainers(Map.Models.MapLogic mapLogic, ReadOnlyCollection faceDistanceContainers, long? skipOlderThan, DistanceLimits distanceLimits) { List results = new(); foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers) { if (faceDistanceContainer.FaceDistance is null || faceDistanceContainer.Face.Mapping?.MappingFromLocation is null) throw new NotSupportedException(); if (skipOlderThan is not null && faceDistanceContainer.FaceDistance.DateTimeOriginalThenMinimumDateTime.Ticks < skipOlderThan.Value) continue; if (faceDistanceContainer.Face.Mapping.MappingFromLocation.ConfidencePercent < distanceLimits.FaceConfidencePercent) continue; if (faceDistanceContainer.Face.Mapping.MappingFromLocation.AreaPermyriad < distanceLimits.FaceAreaPermyriad) continue; if (faceDistanceContainer.Face.Mapping.MappingFromFilter.IsUsed is not null && faceDistanceContainer.Face.Mapping.MappingFromFilter.IsUsed.Value) continue; if (faceDistanceContainer.Face.Mapping.MappingFromFilter.IsFocusPerson is not null && !faceDistanceContainer.Face.Mapping.MappingFromFilter.IsFocusPerson.Value) continue; if (faceDistanceContainer.Face.Mapping.MappingFromFilter.IsFocusModel is not null && !faceDistanceContainer.Face.Mapping.MappingFromFilter.IsFocusModel.Value) continue; if (faceDistanceContainer.Face.Mapping.MappingFromFilter.IsFocusRelativePath is not null && !faceDistanceContainer.Face.Mapping.MappingFromFilter.IsFocusRelativePath.Value) continue; if (faceDistanceContainer.Face.Mapping.MappingFromFilter.IsIgnoreRelativePath is not null && faceDistanceContainer.Face.Mapping.MappingFromFilter.IsIgnoreRelativePath.Value) continue; results.Add(faceDistanceContainer); } return results.ToArray(); } public static SortingContainer[] SetFaceMappingSortingCollectionThenGetSortingContainers(int maxDegreeOfParallelism, Map.Models.Configuration mapConfiguration, long ticks, Map.Models.MapLogic mapLogic, IDistanceLimits distanceLimits, ReadOnlyCollection faceDistanceEncodings, FaceDistanceContainer[] filteredFaceDistanceContainers) { SortingContainer[] results; List collection = new(); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; string message = $") {filteredFaceDistanceContainers.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(filteredFaceDistanceContainers.Length, message, options); _ = Parallel.For(0, filteredFaceDistanceContainers.Length, parallelOptions, (i, state) => { progressBar.Tick(); FaceDistance faceDistanceEncoding = filteredFaceDistanceContainers[i].FaceDistance; Face face = filteredFaceDistanceContainers[i].Face; List sortingCollection = GetSortingCollection(mapLogic, faceDistanceEncodings, i, faceDistanceEncoding); if (sortingCollection.Count == 0) return; List sortingContainers = GetSortingContainers(mapConfiguration, distanceLimits, face, faceDistanceEncoding, sortingCollection); if (sortingContainers.Count > 0) { lock (collection) collection.AddRange(sortingContainers); } }); if (collection.Count == 0) results = Array.Empty(); else { if (distanceLimits.RangeDaysDeltaTargetLessThenUpper) results = Shared.Models.Stateless.Methods.ISortingContainer.Sort(collection); else results = Shared.Models.Stateless.Methods.ISortingContainer.SortUsingDaysDelta(collection); } return results; } private static void ReviewLocationContainerDistanceTolerance(float locationContainerDistanceTolerance, DateTime dateTime, DateTime yesterday, List<(string File, FaceRecognitionDotNet.FaceEncoding FaceRecognitionDotNetFaceEncoding)> collection) { FileInfo fileInfo; List files = new(); FaceDistance? faceDistanceEncoding; List faceDistanceLengths; List firstPassFailures = new(); List faceDistanceEncodings = new(); faceDistanceEncoding = new(collection[0].FaceRecognitionDotNetFaceEncoding); foreach ((string file, FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding) in collection) { files.Add(file); faceDistanceEncodings.Add(new(faceRecognitionDotNetFaceEncoding)); } if (faceDistanceEncoding is null) throw new Exception(); faceDistanceLengths = FaceRecognition.FaceDistances(new(faceDistanceEncodings), faceDistanceEncoding); if (faceDistanceLengths.Count != files.Count) throw new Exception(); for (int i = 0; i < files.Count; i++) { if (faceDistanceLengths[i].Length < locationContainerDistanceTolerance) continue; firstPassFailures.Add(files[i]); } faceDistanceEncoding = new(collection[^1].FaceRecognitionDotNetFaceEncoding); foreach ((string file, FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding) in collection) { files.Add(file); faceDistanceEncodings.Add(new(faceRecognitionDotNetFaceEncoding)); } if (faceDistanceEncoding is null) throw new Exception(); faceDistanceLengths = FaceRecognition.FaceDistances(new(faceDistanceEncodings), faceDistanceEncoding); if (faceDistanceLengths.Count != files.Count) throw new Exception(); for (int i = 0; i < files.Count; i++) { fileInfo = new(files[i]); if (!fileInfo.Exists) continue; if (fileInfo.CreationTime > yesterday) File.SetCreationTime(fileInfo.FullName, yesterday); if (faceDistanceLengths[i].Length < locationContainerDistanceTolerance) continue; if (firstPassFailures.Contains(fileInfo.FullName)) continue; File.SetCreationTime(fileInfo.FullName, dateTime); } } void IDistance.ReviewLocationContainerDistanceTolerance(float locationContainerDistanceTolerance, ReadOnlyCollection> locationContainers) { string? json; int? lastDirectoryNumber = null; DateTime dateTime = DateTime.Now; DateTime yesterday = DateTime.Now.AddDays(-1); Shared.Models.FaceEncoding? modelsFaceEncoding; FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding; List<(string, FaceRecognitionDotNet.FaceEncoding)> collection = new(); foreach (LocationContainer? locationContainer in locationContainers) { if (locationContainer.DirectoryNumber is null) continue; if (lastDirectoryNumber is not null && locationContainer.DirectoryNumber.Value != lastDirectoryNumber.Value) { if (collection.Count > 2) ReviewLocationContainerDistanceTolerance(locationContainerDistanceTolerance, dateTime, yesterday, collection); collection.Clear(); } json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.Directories); if (json is null) continue; modelsFaceEncoding = JsonSerializer.Deserialize(json); if (modelsFaceEncoding is null) throw new NotSupportedException(); faceRecognitionDotNetFaceEncoding = FaceRecognition.LoadFaceEncoding(modelsFaceEncoding.RawEncoding); collection.Add((locationContainer.File, faceRecognitionDotNetFaceEncoding)); lastDirectoryNumber = locationContainer.DirectoryNumber.Value; } if (collection.Count > 2) ReviewLocationContainerDistanceTolerance(locationContainerDistanceTolerance, dateTime, yesterday, collection); } }