using ShellProgressBar; using System.Collections.ObjectModel; using System.Data; using System.Text.Json; using View_by_Distance.FaceRecognitionDotNet; using View_by_Distance.Map.Models; using View_by_Distance.Property.Models.Stateless; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Methods; using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Distance.Models; public partial class E_Distance : IDistance { internal record Record(FilePath FilePath, FaceRecognitionDotNet.FaceEncoding FaceRecognitionDotNetFaceEncoding); private readonly List _Moved; private readonly List _Debug; private readonly List _Renamed; private readonly int _FaceConfidencePercent; private readonly bool _DistanceRenameToMatch; 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[] rectangleIntersectMinimums) { _Debug = []; _Moved = []; _Renamed = []; _AllMappedFaceFiles = []; _AllMappedFaceFileNames = []; _DuplicateMappedFaceFiles = []; _DistanceRenameToMatch = distanceRenameToMatch; _FaceConfidencePercent = faceConfidencePercent; _DistanceMoveUnableToMatch = distanceMoveUnableToMatch; _RectangleIntersectMinimum = rectangleIntersectMinimums.Max(); _RangeDistanceToleranceAverage = rangeDistanceTolerance.Average(); } private static void MoveUnableToMatch(FilePath filePath) { string checkFile = $"{filePath.FullName}.unk"; if (File.Exists(filePath.FullName) && !File.Exists(checkFile)) File.Move(filePath.FullName, checkFile); } private FaceDistanceContainer[] GetFaceDistanceContainers(MappingFromItem mappingFromItem, List intersectFaces) { FaceDistanceContainer[] results; DateTime dateTime; int wholePercentages; int confidencePercent; FaceDistance faceDistance; FaceDistanceContainer faceDistanceContainer; List collection = []; foreach (Face face in intersectFaces) { if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) throw new NotSupportedException(); if (face.Mapping?.MappingFromFilterPost is null) throw new NotSupportedException(); dateTime = mappingFromItem.GetDateTimeOriginalThenMinimumDateTime(); confidencePercent = Shared.Models.Stateless.Methods.ILocation.GetConfidencePercent(_FaceConfidencePercent, 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, dateTime, faceEncoding, face.Mapping?.MappingFromFilterPost, mappingFromItem.Id, mappingFromItem.IsWrongYear, wholePercentages); else { faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); faceDistance = new(confidencePercent, dateTime, faceEncoding, face.Mapping?.MappingFromFilterPost, 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 = []; 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(IDistanceLimits distanceLimits, MappingFromItem mappingFromItem, List intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding) { List<(Face Face, double? Length)> results = []; 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 || faceDistanceLength.Length > distanceLimits.RangeDistanceToleranceUpperLimit) continue; if (faceDistanceLength.Length is null) throw new NotSupportedException(); results.Add(new(face, faceDistanceLength.Length.Value)); } return results; } private (Face, double?)[] GetClosestFaceByDistanceIgnoringTolerance(IDistanceLimits distanceLimits, MappingFromItem mappingFromItem, List intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding) { (Face, double?)[] results; List<(Face Face, double? Length)> collection = GetValues(distanceLimits, 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(List faces, string? json) { List<(Face, double?)> results = []; string check; foreach (Face face in faces) { 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, FilePath filePath, 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 = IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); checkFile = Path.Combine(mappedFaceDirectory, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.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 = []; 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 = $"{collection[i].FullName}.dup"; if (File.Exists(checkFile)) continue; File.Move(collection[i].FullName, checkFile); } } public void LookForMatchFacesAndPossiblyRename(bool overrideForFaceImages, IDistanceLimits distanceLimits, IFaceD dFace, FilePath filePath, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces, ReadOnlyCollection locationContainers) { string? json; string[] matches; FileInfo? fileInfo; List intersectFaces; Shared.Models.FaceEncoding? modelsFaceEncoding; List<(Face Face, double? Distance)> checkFaces = []; foreach (LocationContainer locationContainer in locationContainers) { if (_Renamed.Contains(locationContainer.FilePath.FullName)) continue; if (locationContainer.FromDistanceContent && _DuplicateMappedFaceFiles.Contains(locationContainer.FilePath.Name)) continue; checkFaces.Clear(); if (locationContainer.ExifDirectory is null) { if (locationContainer.FromDistanceContent) throw new NullReferenceException(nameof(locationContainer.ExifDirectory)); continue; } json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.ExifDirectory); if (json is null) { if (_DistanceMoveUnableToMatch) MoveUnableToMatch(locationContainer.FilePath); continue; } if (faces.Count > 0) checkFaces.AddRange(GetMatchingFacesByFaceEncoding(faces, 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 (faces.Count > 0) { intersectFaces = Shared.Models.Stateless.Methods.ILocation.FilterByIntersect(faces, _RectangleIntersectMinimum, locationContainer.WholePercentages); if (intersectFaces.Count > 0) checkFaces.AddRange(GetClosestFaceByDistanceIgnoringTolerance(distanceLimits, mappingFromItem, intersectFaces, modelsFaceEncoding)); } } if (checkFaces.Count == 0) { if (_DistanceMoveUnableToMatch) MoveUnableToMatch(locationContainer.FilePath); continue; } if (checkFaces.Count != 1) { if (_DistanceMoveUnableToMatch) MoveUnableToMatch(locationContainer.FilePath); continue; } fileInfo = CheckFileThenGetFileInfo(dFace.FileNameExtension, filePath, mappingFromItem, locationContainer.FilePath.FullName, checkFaces); if (fileInfo is not null) { if (_DistanceRenameToMatch && fileInfo is not null) { if (fileInfo.Exists) File.Delete(locationContainer.FilePath.FullName); else File.Move(locationContainer.FilePath.FullName, fileInfo.FullName); File.WriteAllText($"{fileInfo.FullName}.old", $"{fileInfo.FullName}{Environment.NewLine}{locationContainer.FilePath.FullName}"); _Renamed.Add(locationContainer.FilePath.FullName); } continue; } if (overrideForFaceImages) { json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(locationContainer.ExifDirectory); if (json is null || !json.Contains(nameof(DateTime))) { if (checkFaces.Count == 1) dFace.ReSaveFace(exifDirectory, locationContainer.FilePath, checkFaces[0].Face, mappedFile: true); } } if (_AllMappedFaceFileNames.Contains(locationContainer.FilePath.Name)) { lock (_AllMappedFaceFiles) matches = (from l in _AllMappedFaceFiles where l != locationContainer.FilePath.FullName && Path.GetFileName(l) == locationContainer.FilePath.Name select l).ToArray(); if (locationContainer.FromDistanceContent && matches.Length > 0) AppendMatchingDuplicates(locationContainer.FilePath.FullName, matches); } if (!locationContainer.FromDistanceContent) continue; lock (_AllMappedFaceFiles) _AllMappedFaceFiles.Add(locationContainer.FilePath.FullName); lock (_AllMappedFaceFileNames) _AllMappedFaceFileNames.Add(locationContainer.FilePath.Name); } } public void Clear() { // 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()}"; // } // 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 static void SaveFaceDistances(Property.Models.Configuration configuration, ReadOnlyCollection sortingContainers) { string eDistanceContentCollectionDirectory = 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 ReadOnlyDictionary> GetMappedWithEncoding(ReadOnlyDictionary> mapped) { Dictionary> results = []; string? json; LocationContainer? locationContainer; Shared.Models.FaceEncoding? faceEncoding; FaceRecognitionDotNet.FaceEncoding? encoding; Dictionary keyValuePairs; foreach (KeyValuePair> keyValuePair in mapped) { keyValuePairs = []; foreach (KeyValuePair keyValue in keyValuePair.Value) { json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(keyValue.Value.ExifDirectory); faceEncoding = json is null ? null : JsonSerializer.Deserialize(json); if (faceEncoding is null) continue; encoding = FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding); locationContainer = LocationContainer.Get(keyValue.Value, encoding, keepExifDirectory: false); keyValuePairs.Add(keyValue.Key, locationContainer); } results.Add(keyValuePair.Key, new(keyValuePairs)); } return new(results); } public static List GetPreFilterLocationContainer(int maxDegreeOfParallelism, Configuration configuration, string focusDirectory, string focusModel, int? skipPersonWithMoreThen, long ticks, MapLogic mapLogic, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary> mapped, List available) { List results = []; string? json; string? model; bool? canReMap; bool? isFocusPerson; bool? inSkipCollection; Shared.Models.FaceEncoding? faceEncoding; FaceRecognitionDotNet.FaceEncoding? encoding; ReadOnlyDictionary? keyValuePairs; ReadOnlyDictionary>? wholePercentagesToPersonContainers; foreach (LocationContainer locationContainer in available) { if (mapped.TryGetValue(locationContainer.Id, out keyValuePairs)) { if (keyValuePairs.ContainsKey(locationContainer.WholePercentages)) continue; } if (locationContainer.ExifDirectory is null || locationContainer.FaceFile is null) continue; inSkipCollection = mapLogic.InSkipCollection(locationContainer.Id, locationContainer.WholePercentages); if (inSkipCollection is not null && inSkipCollection.Value) continue; wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(locationContainer.Id); canReMap = Map.Models.Stateless.Methods.IMapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, locationContainer.WholePercentages); if (canReMap is not null && !canReMap.Value) continue; isFocusPerson = mapLogic.IsFocusPerson(skipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, locationContainer.WholePercentages); if (isFocusPerson is not null && !isFocusPerson.Value) continue; if (!string.IsNullOrEmpty(focusModel)) { model = Metadata.Models.Stateless.Methods.IMetadata.GetModel(locationContainer.ExifDirectory); if (string.IsNullOrEmpty(model) || !model.Contains(focusModel)) continue; } if (!string.IsNullOrEmpty(focusDirectory)) { if (!locationContainer.FilePath.DirectoryName.Contains(focusDirectory)) continue; } json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.ExifDirectory); faceEncoding = json is null ? null : JsonSerializer.Deserialize(json); if (faceEncoding is null) continue; encoding = FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding); results.Add(LocationContainer.Get(locationContainer, encoding, keepExifDirectory: false)); } return results; } public static void PreFilterSetFaceDistances(int maxDegreeOfParallelism, Configuration configuration, long ticks, ReadOnlyCollection distinctValidImageFaces) { List faces = []; foreach (Face face in distinctValidImageFaces) { if (face.Mapping?.MappingFromFilterPre is null) throw new NotSupportedException(); if (face.Mapping.MappingFromFilterPre.InSkipCollection is not null && face.Mapping.MappingFromFilterPre.InSkipCollection.Value) continue; if (face.Mapping.MappingFromFilterPre.IsFocusModel is not null && !face.Mapping.MappingFromFilterPre.IsFocusModel.Value) continue; if (face.Mapping.MappingFromFilterPre.IsFocusRelativePath is not null && !face.Mapping.MappingFromFilterPre.IsFocusRelativePath.Value) continue; if (!configuration.ReMap && face.Mapping.MappingFromPerson is not null) continue; if (!configuration.ReMap && face.FaceEncoding is not null && face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding) // throw new NotSupportedException($"{face.FaceEncoding} should not be null!"); 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); DateTime dateTime = face.Mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime(); FaceDistance faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, dateTime, faceEncoding, face.Mapping.MappingFromFilterPost, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromLocation.WholePercentages); lock (face) face.SetFaceDistance(faceDistance); }); } private static List GetSortingContainers(Configuration mapConfiguration, IDistanceLimits distanceLimits, Face face, FaceDistance faceDistanceEncoding, List sortingCollection) { List results = []; int days = 0, distance = 0; SortingContainer sortingContainer; Sorting[] collection = 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(sorting, face.Mapping); results.Add(sortingContainer); if (results.Count >= distanceLimits.SortingMaximumPerFaceShouldBeHigh) break; } distanceLimits.AddCounts(days, distance); return results; } private static List GetSortingCollection(MapLogic mapLogic, ReadOnlyCollection faceDistanceEncodings, int i, Face face, FaceDistance faceDistanceEncoding) { List results; List faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding); results = mapLogic.GetSortingCollection(i, face, faceDistanceEncoding, faceDistanceLengths); return results; } public static ReadOnlyCollection GetFaceDistanceContainers(ReadOnlyCollection distinctValidImageFaces) { ReadOnlyCollection results; DateTime dateTime; FaceDistance faceDistance; FaceDistanceContainer faceDistanceContainer; List collection = []; foreach (Face face in distinctValidImageFaces) { if (face.Mapping?.MappingFromLocation is null) throw new NotSupportedException(); if (face.FaceDistance?.Encoding is not FaceRecognitionDotNet.FaceEncoding faceEncoding) continue; dateTime = face.Mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime(); faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, dateTime, faceEncoding, face.Mapping.MappingFromFilterPost, 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 List GetPostFilterLocationContainer(MapLogic mapLogic, List preFiltered, DistanceLimits distanceLimits) { List results = []; foreach (LocationContainer locationContainer in preFiltered) { if (locationContainer.FaceFile is null) continue; if (locationContainer.FaceFile.AreaPermyriad < distanceLimits.FaceAreaPermyriad) continue; if (locationContainer.FaceFile.ConfidencePercent < distanceLimits.FaceConfidencePercent) continue; results.Add(locationContainer); } return results; } public static FaceDistanceContainer[] FilteredPostLoadFaceDistanceContainers(MapLogic mapLogic, ReadOnlyCollection faceDistanceContainers, long? skipOlderThan, DistanceLimits distanceLimits) { List results = []; 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.MappingFromFilterPre.InSkipCollection is not null && faceDistanceContainer.Face.Mapping.MappingFromFilterPre.InSkipCollection.Value) // throw new NotSupportedException(nameof(PreFilterSetFaceDistances)); continue; if (faceDistanceContainer.Face.Mapping.MappingFromFilterPre.IsFocusModel is not null && !faceDistanceContainer.Face.Mapping.MappingFromFilterPre.IsFocusModel.Value) continue; if (faceDistanceContainer.Face.Mapping.MappingFromFilterPre.IsFocusRelativePath is not null && !faceDistanceContainer.Face.Mapping.MappingFromFilterPre.IsFocusRelativePath.Value) continue; if (faceDistanceContainer.Face.Mapping.MappingFromFilterPost.InSkipCollection is not null && faceDistanceContainer.Face.Mapping.MappingFromFilterPost.InSkipCollection.Value) continue; if (faceDistanceContainer.Face.Mapping.MappingFromFilterPost.IsFocusPerson is not null && !faceDistanceContainer.Face.Mapping.MappingFromFilterPost.IsFocusPerson.Value) continue; results.Add(faceDistanceContainer); } return results.ToArray(); } private static ReadOnlyCollection GetReadOnlyLocationContainer(ReadOnlyDictionary> mappedWithEncoding, List postFiltered) { List results = []; foreach (LocationContainer locationContainer in postFiltered) results.Add(locationContainer); foreach (KeyValuePair> keyValuePair in mappedWithEncoding) { foreach (KeyValuePair keyValue in keyValuePair.Value) results.Add(keyValue.Value); } return new(results); } public static ReadOnlyCollection GetMatrixLocationContainers(IDlibDotNet dlibDotNet, Configuration mapConfiguration, long ticks, MapLogic mapLogic, ReadOnlyDictionary> mappedWithEncoding, List preFiltered, DistanceLimits distanceLimits, List postFiltered) { List results = []; ReadOnlyCollection locationContainers; ReadOnlyCollection readOnlyLocationContainers = GetReadOnlyLocationContainer(mappedWithEncoding, postFiltered); foreach (LocationContainer locationContainer in postFiltered) { dlibDotNet.Tick(); locationContainers = FaceRecognition.GetLocationContainers(mapConfiguration.FaceDistancePermyriad, readOnlyLocationContainers, locationContainer); foreach (LocationContainer item in locationContainers) { if (item.LengthPermyriad is null) continue; if (item.LengthPermyriad > distanceLimits.FaceDistancePermyriad) break; if (!mapConfiguration.SaveSortingWithoutPerson && item.PersonKey is null) continue; if (item.Id == locationContainer.Id && item.WholePercentages == locationContainer.WholePercentages) continue; results.Add(item); } } LocationContainer[] array = results.OrderBy(l => l.LengthPermyriad).ToArray(); return new(array); } public static ReadOnlyCollection SetFaceMappingSortingCollectionThenGetSortedSortingContainers(int maxDegreeOfParallelism, Configuration mapConfiguration, long ticks, MapLogic mapLogic, IDistanceLimits distanceLimits, ReadOnlyCollection faceDistanceEncodings, FaceDistanceContainer[] filteredFaceDistanceContainers) { List results = []; 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(); Face face = filteredFaceDistanceContainers[i].Face; FaceDistance faceDistanceEncoding = filteredFaceDistanceContainers[i].FaceDistance; List sortingCollection = GetSortingCollection(mapLogic, faceDistanceEncodings, i, face, faceDistanceEncoding); if (sortingCollection.Count == 0) return; List sortingContainers = GetSortingContainers(mapConfiguration, distanceLimits, face, faceDistanceEncoding, sortingCollection); if (sortingContainers.Count > 0) { lock (results) results.AddRange(sortingContainers); } }); if (distanceLimits is not null && distanceLimits.RangeDaysDeltaTargetLessThenUpper) results = ISortingContainer.Sort(results); else results = ISortingContainer.SortUsingDaysDelta(results); return new(results); } private static ReadOnlyCollection GetRelationCollections(IDistanceLimits distanceLimits, int faceDistancePermyriad, int locationContainerDistanceTake, float distanceTolerance, List records) { List results = []; string fileName; FileHolder fileHolder; int distancePermyriad; List files = []; long ticks = DateTime.Now.Ticks; FaceDistance? faceDistanceEncoding; List mappedRelations; List faceDistanceLengths; List faceDistanceEncodings = []; foreach (Record record in records) { files.Add(record.FilePath.FullName); faceDistanceEncodings.Add(new(record.FaceRecognitionDotNetFaceEncoding)); } foreach (Record record in records) { mappedRelations = []; FaceDistance faceDistanceLength; fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(record.FilePath.FullName); if (files.Count > 1) { faceDistanceEncoding = new(record.FaceRecognitionDotNetFaceEncoding); if (faceDistanceEncoding is null) throw new NullReferenceException(nameof(faceDistanceEncoding)); faceDistanceLengths = FaceRecognition.FaceDistances(new(faceDistanceEncodings), faceDistanceEncoding); for (int i = 0; i < faceDistanceLengths.Count; i++) { fileName = Path.GetFileName(files[i]); if (fileName == fileHolder.Name) continue; faceDistanceLength = faceDistanceLengths[i]; if (faceDistanceLength.Length is null || faceDistanceLength.Length > distanceLimits.RangeDistanceToleranceUpperLimit) continue; if (faceDistanceLength.Length is null || faceDistanceLength.Length.Value > distanceTolerance) continue; distancePermyriad = (int)(faceDistanceLength.Length.Value * faceDistancePermyriad); mappedRelations.Add(new(distancePermyriad, files[i])); } } mappedRelations = (from l in mappedRelations orderby l.DistancePermyriad select l).Take(locationContainerDistanceTake).ToList(); results.Add(new(fileHolder, new(mappedRelations))); } return new(results); } ReadOnlyCollection IDistance.GetRelationContainers(IDistanceLimits distanceLimits, int faceDistancePermyriad, int locationContainerDistanceTake, float locationContainerDistanceTolerance, ReadOnlyCollection locationContainers) { ReadOnlyCollection result; string? json; List records = []; Shared.Models.FaceEncoding? modelsFaceEncoding; FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding; foreach (LocationContainer locationContainer in locationContainers) { json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.ExifDirectory); if (json is null) continue; modelsFaceEncoding = JsonSerializer.Deserialize(json); if (modelsFaceEncoding is null) throw new NotSupportedException(); faceRecognitionDotNetFaceEncoding = FaceRecognition.LoadFaceEncoding(modelsFaceEncoding.RawEncoding); records.Add(new(locationContainer.FilePath, faceRecognitionDotNetFaceEncoding)); } result = GetRelationCollections(distanceLimits, faceDistancePermyriad, locationContainerDistanceTake, locationContainerDistanceTolerance, records); return result; } }