From ad28ab2d381b860642c8e9280b47dde061dc3c2e Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Wed, 14 Sep 2022 19:00:57 -0700 Subject: [PATCH] Re-Write without checking mapped --- .vscode/settings.json | 4 + FaceRecognitionDotNet/FaceRecognition.cs | 36 +- Instance/DlibDotNet.cs | 457 +++---- Instance/Models/Binder/Configuration.cs | 4 - Instance/Models/Configuration.cs | 4 +- Instance/Models/_D2_FaceParts.cs | 22 +- Instance/Models/_D_Face.cs | 19 +- Instance/Models/_E_Distance.cs | 855 ++----------- Instance/appsettings.json | 3 - Map/Models/MapLogic.cs | 1063 ++++++++--------- Map/Models/Stateless/IMapLogic.cs | 8 +- Map/Models/Stateless/MapLogic.cs | 78 -- Map/Models/Stateless/Methods/IMapLogic.cs | 6 + .../Stateless/SetByDeterministicHashCode.cs | 192 +-- Shared/Models/Closest.cs | 45 - Shared/Models/Face.cs | 44 +- Shared/Models/FaceDistance.cs | 43 +- Shared/Models/FaceDistanceContainer.cs | 25 + Shared/Models/Item.cs | 10 +- Shared/Models/Location.cs | 77 +- Shared/Models/Mapping.cs | 119 +- Shared/Models/MappingContainer.cs | 48 - Shared/Models/Methods/IClosest.cs | 8 - Shared/Models/Methods/INamed.cs | 8 - Shared/Models/Properties/IClosest.cs | 14 - Shared/Models/Properties/IFace.cs | 4 +- Shared/Models/Properties/IFaceDistance.cs | 14 +- .../Properties/IFaceDistanceContainer.cs | 9 + Shared/Models/Properties/IItem.cs | 2 - Shared/Models/Properties/IMapping.cs | 41 +- Shared/Models/Properties/IMappingContainer.cs | 14 - Shared/Models/Properties/ISorting.cs | 8 +- Shared/Models/Properties/ISortingContainer.cs | 9 + Shared/Models/Sorting.cs | 38 +- Shared/Models/SortingContainer.cs | 25 + Shared/Models/Stateless/IClosest.cs | 17 - Shared/Models/Stateless/IFaceDistance.cs | 3 +- Shared/Models/Stateless/ILocation.cs | 1 - Shared/Models/Stateless/IMapping.cs | 1 + Shared/Models/Stateless/ISorting.cs | 13 + Shared/Models/Stateless/Methods/Age.cs | 20 +- Shared/Models/Stateless/Methods/Closest.cs | 39 - Shared/Models/Stateless/Methods/Face.cs | 15 - .../Models/Stateless/Methods/FaceDistance.cs | 8 + .../Methods/FaceDistanceContainer.cs | 6 + Shared/Models/Stateless/Methods/IAge.cs | 6 + Shared/Models/Stateless/Methods/IClosest.cs | 10 - Shared/Models/Stateless/Methods/IFace.cs | 3 - .../Models/Stateless/Methods/IFaceDistance.cs | 10 + .../Methods/IFaceDistanceContainer.cs | 6 + .../Stateless/Methods/IFaceFileSystem.cs | 12 +- Shared/Models/Stateless/Methods/IIMapping.cs | 18 - Shared/Models/Stateless/Methods/ILocation.cs | 17 +- Shared/Models/Stateless/Methods/IMapping.cs | 17 + .../Stateless/Methods/IPersonBirthday.cs | 28 +- Shared/Models/Stateless/Methods/ISorting.cs | 12 +- .../Stateless/Methods/ISortingContainer.cs | 11 + Shared/Models/Stateless/Methods/Location.cs | 63 + Shared/Models/Stateless/Methods/Mapping.cs | 45 +- .../Stateless/Methods/PersonBirthday.cs | 43 +- Shared/Models/Stateless/Methods/Sorting.cs | 77 +- .../Stateless/Methods/SortingContainer.cs | 6 + Tests/Models/Binder/Configuration.cs | 4 - Tests/Models/Configuration.cs | 4 +- Tests/UnitTestCalculations.cs | 38 + Tests/UnitTestResize.cs | 12 +- .../Models/Binder/Configuration.cs | 4 - .../Models/Configuration.cs | 4 +- .../UnitTestFace.cs | 6 +- package.json | 3 +- 70 files changed, 1596 insertions(+), 2342 deletions(-) create mode 100644 Map/Models/Stateless/Methods/IMapLogic.cs delete mode 100644 Shared/Models/Closest.cs create mode 100644 Shared/Models/FaceDistanceContainer.cs delete mode 100644 Shared/Models/MappingContainer.cs delete mode 100644 Shared/Models/Methods/IClosest.cs delete mode 100644 Shared/Models/Methods/INamed.cs delete mode 100644 Shared/Models/Properties/IClosest.cs create mode 100644 Shared/Models/Properties/IFaceDistanceContainer.cs delete mode 100644 Shared/Models/Properties/IMappingContainer.cs create mode 100644 Shared/Models/Properties/ISortingContainer.cs create mode 100644 Shared/Models/SortingContainer.cs delete mode 100644 Shared/Models/Stateless/IClosest.cs create mode 100644 Shared/Models/Stateless/ISorting.cs delete mode 100644 Shared/Models/Stateless/Methods/Closest.cs create mode 100644 Shared/Models/Stateless/Methods/FaceDistance.cs create mode 100644 Shared/Models/Stateless/Methods/FaceDistanceContainer.cs delete mode 100644 Shared/Models/Stateless/Methods/IClosest.cs create mode 100644 Shared/Models/Stateless/Methods/IFaceDistance.cs create mode 100644 Shared/Models/Stateless/Methods/IFaceDistanceContainer.cs delete mode 100644 Shared/Models/Stateless/Methods/IIMapping.cs create mode 100644 Shared/Models/Stateless/Methods/IMapping.cs create mode 100644 Shared/Models/Stateless/Methods/ISortingContainer.cs create mode 100644 Shared/Models/Stateless/Methods/SortingContainer.cs diff --git a/.vscode/settings.json b/.vscode/settings.json index f46ff2b..979dc8d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,9 +18,13 @@ "Greyscale", "jfif", "mmod", + "Nicéphore", + "Niépce", "nosj", "paramref", + "permyriad", "Phares", + "Phgtv", "RDHC", "Rects", "resnet", diff --git a/FaceRecognitionDotNet/FaceRecognition.cs b/FaceRecognitionDotNet/FaceRecognition.cs index 41a8b0e..1fe8b0c 100644 --- a/FaceRecognitionDotNet/FaceRecognition.cs +++ b/FaceRecognitionDotNet/FaceRecognition.cs @@ -413,22 +413,28 @@ public class FaceRecognition : DisposableObject return null; } - public static List FaceDistances(IEnumerable faceEncodings, FaceEncoding faceToCompare) + public static List FaceDistances(List faceDistances, FaceDistance faceDistanceToCompare) { - List? results = new(); - if (faceEncodings is null) - throw new NullReferenceException(nameof(faceEncodings)); - if (faceToCompare is null) - throw new NullReferenceException(nameof(faceToCompare)); - faceToCompare.ThrowIfDisposed(); - FaceEncoding[] array = faceEncodings.ToArray(); - if (array.Any(encoding => encoding.IsDisposed)) - throw new ObjectDisposedException($"{nameof(faceEncodings)} contains disposed object."); - if (array.Length == 0) - return results; - foreach (FaceEncoding faceEncoding in array) - using (Matrix diff = faceEncoding.Encoding - faceToCompare.Encoding) - results.Add(DlibDotNet.Dlib.Length(diff)); + List results = new(); + if (faceDistances is null) + throw new NullReferenceException(nameof(faceDistances)); + if (faceDistances.Count != 0) + { + double length; + FaceDistance result; + if (faceDistanceToCompare is null || faceDistanceToCompare.Encoding is not FaceEncoding faceEncodingToCompare) + throw new NullReferenceException(nameof(faceDistanceToCompare)); + faceEncodingToCompare.ThrowIfDisposed(); + foreach (FaceDistance faceDistance in faceDistances) + { + if (faceDistance.Encoding is not FaceEncoding faceEncoding || faceEncoding.IsDisposed) + throw new ObjectDisposedException($"{nameof(faceDistances)} contains disposed object."); + using (Matrix diff = faceEncoding.Encoding - faceEncodingToCompare.Encoding) + length = DlibDotNet.Dlib.Length(diff); + result = new(faceDistance, length); + results.Add(result); + } + } return results; } diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index d008b28..3e35377 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -26,7 +26,6 @@ public partial class DlibDotNet private readonly A2_People _People; private readonly E3_Rename _Rename; private readonly B_Metadata _Metadata; - private readonly E_Distance _Distance; private readonly Serilog.ILogger? _Log; private readonly D2_FaceParts _FaceParts; private readonly AppSettings _AppSettings; @@ -58,7 +57,6 @@ public partial class DlibDotNet _Random = new F_Random(configuration); _People = new A2_People(configuration); _Rename = new E3_Rename(configuration); - _Distance = new E_Distance(configuration); if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions)); string propertyRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(propertyConfiguration, nameof(A_Property), create: false); @@ -115,14 +113,6 @@ public partial class DlibDotNet _Log.Information(message); if (_Exceptions.Count != 0) throw new Exception(message); - if (!_FirstRun && configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions.Any()) - { - long ticks = DateTime.Now.Ticks; - foreach (string outputResolution in configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions) - _Distance.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions(propertyConfiguration, model, predictorModel, outputResolution); - if (appSettings.MaxDegreeOfParallelism < 2) - ticks = LogDelta(ticks, nameof(E_Distance.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions)); - } if (_FirstRun) _Log.Information("First run completed. Run again if wanted"); } @@ -210,8 +200,6 @@ public partial class DlibDotNet throw new NullReferenceException($"{nameof(configuration.OutputResolutions)} must be a valid outputResolution!"); if ((from l in configuration.OutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) throw new Exception($"One or more {nameof(configuration.OutputResolutions)} are not in the ValidResolutions list!"); - if ((from l in configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) - throw new Exception($"One or more {nameof(configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions)} are not in the ValidResolutions list!"); if ((from l in configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) throw new Exception($"One or more {nameof(configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions)} are not in the ValidResolutions list!"); if ((from l in configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) @@ -262,14 +250,14 @@ public partial class DlibDotNet throw new Exception("Input directory should be the source and not a resized directory!"); } - private void FullParallelForWork(A_Property propertyLogic, string outputResolution, string bResultsFullGroupDirectory, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, string eResultsFullGroupDirectory, List> sourceDirectoryChanges, List propertyFileHolderCollection, List propertyCollection, List>> metadataCollections, List> resizeKeyValuePairs, List?> imageFaceCollections, List faceEncodingRequired, Container container, int index, Item item) + private void FullParallelForWork(A_Property propertyLogic, string outputResolution, string bResultsFullGroupDirectory, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, List> sourceDirectoryChanges, List propertyFileHolderCollection, List propertyCollection, List>> metadataCollections, List> resizeKeyValuePairs, List?> imageFaceCollections, Container container, int index, Item item) { if (item.ImageFileHolder is null) throw new NullReferenceException(nameof(item.ImageFileHolder)); - Shared.Models.Property property; List? faceCollection; string original = "Original"; FileHolder? resizedFileHolder; + Shared.Models.Property property; long ticks = DateTime.Now.Ticks; DateTime dateTime = DateTime.Now; List parseExceptions = new(); @@ -332,12 +320,12 @@ public partial class DlibDotNet { bool saveRotated = _Configuration.SaveFaceLandmarkForOutputResolutions.Contains(outputResolution); - _FaceParts.SaveFaceLandmarkImages(d2ResultsFullGroupDirectory, container, subFileTuples, parseExceptions, item, faceCollection, saveRotated); + string sourceDirectorySegment = Property.Models.Stateless.IResult.GetRelativePath(_Configuration.PropertyConfiguration, container.SourceDirectory); + string facesDirectory = Path.GetFullPath(Path.Combine($"{Path.Combine(d2ResultsFullGroupDirectory, "()")}{sourceDirectorySegment}", item.ImageFileHolder.NameWithoutExtension)); + _FaceParts.SaveFaceLandmarkImages(facesDirectory, subFileTuples, parseExceptions, item, faceCollection, saveRotated); if (_AppSettings.MaxDegreeOfParallelism < 2) ticks = LogDelta(ticks, nameof(D2_FaceParts.SaveFaceLandmarkImages)); } - if (_Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)) - _Distance.GetFaceDistances(eResultsFullGroupDirectory, faceEncodingRequired, subFileTuples, parseExceptions, item, faceCollection); } lock (sourceDirectoryChanges) { @@ -350,11 +338,11 @@ public partial class DlibDotNet } } - private int FullParallelWork(long ticks, A_Property propertyLogic, string outputResolution, string bResultsFullGroupDirectory, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, string eResultsFullGroupDirectory, List> sourceDirectoryChanges, List propertyFileHolderCollection, List propertyCollection, List>> metadataCollection, List> resizeKeyValuePairs, List?> imageFaceCollections, List faceEncodingRequired, int containersCount, Container container, Item[] filteredItems) + private int FullParallelWork(long ticks, A_Property propertyLogic, string outputResolution, string bResultsFullGroupDirectory, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, List> sourceDirectoryChanges, List propertyFileHolderCollection, List propertyCollection, List>> metadataCollection, List> resizeKeyValuePairs, List?> imageFaceCollections, int containersCount, Container container, Item[] filteredItems) { - int result = 0; if (_Log is null) throw new NullReferenceException(nameof(_Log)); + int result = 0; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism }; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; if (imageFaceCollections.Count != filteredItems.Length || metadataCollection.Count != filteredItems.Length || resizeKeyValuePairs.Count != filteredItems.Length || propertyCollection.Count != filteredItems.Length) @@ -370,25 +358,23 @@ public partial class DlibDotNet } int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string message = $"{container.R:000}.{container.G} / {containersCount:000}) {filteredItems.Length:000} file(s) - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}"; - using (ProgressBar progressBar = new(filteredItems.Length, message, options)) - { - _ = Parallel.For(0, filteredItems.Length, parallelOptions, (i, state) => + using ProgressBar progressBar = new(filteredItems.Length, message, options); + _ = Parallel.For(0, filteredItems.Length, parallelOptions, (i, state) => + { + try { - try - { - FullParallelForWork(propertyLogic, outputResolution, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, eResultsFullGroupDirectory, sourceDirectoryChanges, propertyFileHolderCollection, propertyCollection, metadataCollection, resizeKeyValuePairs, imageFaceCollections, faceEncodingRequired, container, index: i, filteredItems[i]); - if (i == 0 || sourceDirectoryChanges.Any()) - progressBar.Tick(); - } - catch (Exception ex) - { - result += 1; - _Log.Error(string.Concat(container.SourceDirectory, Environment.NewLine, ex.Message, Environment.NewLine, ex.StackTrace), ex); - if (result == filteredItems.Length) - throw new Exception(string.Concat("All in [", container.SourceDirectory, "] failed!")); - } - }); - } + FullParallelForWork(propertyLogic, outputResolution, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, sourceDirectoryChanges, propertyFileHolderCollection, propertyCollection, metadataCollection, resizeKeyValuePairs, imageFaceCollections, container, index: i, filteredItems[i]); + if (i == 0 || sourceDirectoryChanges.Any()) + progressBar.Tick(); + } + catch (Exception ex) + { + result += 1; + _Log.Error(string.Concat(container.SourceDirectory, Environment.NewLine, ex.Message, Environment.NewLine, ex.StackTrace), ex); + if (result == filteredItems.Length) + throw new Exception(string.Concat("All in [", container.SourceDirectory, "] failed!")); + } + }); return result; } @@ -524,188 +510,64 @@ public partial class DlibDotNet return new(aResultsFullGroupDirectory, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, eResultsFullGroupDirectory); } - private void SetAngleBracketCollections(Property.Models.Configuration configuration, A_Property propertyLogic, string outputResolution, Container container, string aResultsFullGroupDirectory, string bResultsFullGroupDirectory, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string eResultsFullGroupDirectory, bool onlyE) + private void SetAngleBracketCollections(Property.Models.Configuration configuration, A_Property propertyLogic, string outputResolution, Container container, string aResultsFullGroupDirectory, string bResultsFullGroupDirectory, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, string eResultsFullGroupDirectory) { _Faces.AngleBracketCollection.Clear(); _Resize.AngleBracketCollection.Clear(); _Metadata.AngleBracketCollection.Clear(); - _Distance.AngleBracketCollection.Clear(); propertyLogic.AngleBracketCollection.Clear(); - if (!onlyE) - { - propertyLogic.AngleBracketCollection.AddRange(Property.Models.Stateless.IResult.GetDirectoryInfoCollection(configuration, + propertyLogic.AngleBracketCollection.AddRange(Property.Models.Stateless.IResult.GetDirectoryInfoCollection(configuration, + container.SourceDirectory, + aResultsFullGroupDirectory, + contentDescription: string.Empty, + singletonDescription: "Properties for each image", + collectionDescription: string.Empty, + converted: false)); + _Metadata.AngleBracketCollection.AddRange(Property.Models.Stateless.IResult.GetDirectoryInfoCollection(configuration, + container.SourceDirectory, + bResultsFullGroupDirectory, + contentDescription: string.Empty, + singletonDescription: "Metadata as key value pairs", + collectionDescription: string.Empty, + converted: true)); + _Resize.AngleBracketCollection.AddRange(Property.Models.Stateless.IResult.GetDirectoryInfoCollection(configuration, + container.SourceDirectory, + cResultsFullGroupDirectory, + contentDescription: "Resized image", + singletonDescription: "Resize dimensions for each resolution", + collectionDescription: string.Empty, + converted: true)); + if (_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution)) + _Faces.AngleBracketCollection.AddRange(Property.Models.Stateless.IResult.GetDirectoryInfoCollection(configuration, container.SourceDirectory, - aResultsFullGroupDirectory, - contentDescription: string.Empty, - singletonDescription: "Properties for each image", - collectionDescription: string.Empty, - converted: false)); - _Metadata.AngleBracketCollection.AddRange(Property.Models.Stateless.IResult.GetDirectoryInfoCollection(configuration, - container.SourceDirectory, - bResultsFullGroupDirectory, - contentDescription: string.Empty, - singletonDescription: "Metadata as key value pairs", - collectionDescription: string.Empty, + dResultsFullGroupDirectory, + contentDescription: "n png file(s) for each face found", + singletonDescription: string.Empty, + collectionDescription: "For each image a json file with all faces found", converted: true)); - _Resize.AngleBracketCollection.AddRange(Property.Models.Stateless.IResult.GetDirectoryInfoCollection(configuration, + if (_Configuration.SaveFaceLandmarkForOutputResolutions.Contains(outputResolution)) + _ = Property.Models.Stateless.IResult.GetDirectoryInfoCollection( + _Configuration.PropertyConfiguration, container.SourceDirectory, - cResultsFullGroupDirectory, - contentDescription: "Resized image", - singletonDescription: "Resize dimensions for each resolution", + d2ResultsFullGroupDirectory, + contentDescription: "n x 2 gif file(s) for each face found", + singletonDescription: string.Empty, collectionDescription: string.Empty, - converted: true)); - if (_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution)) - _Faces.AngleBracketCollection.AddRange(Property.Models.Stateless.IResult.GetDirectoryInfoCollection(configuration, - container.SourceDirectory, - dResultsFullGroupDirectory, - contentDescription: "n png file(s) for each face found", - singletonDescription: string.Empty, - collectionDescription: "For each image a json file with all faces found", - converted: true)); - } + converted: false); if (_Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)) - _Distance.AngleBracketCollection.AddRange(Property.Models.Stateless.IResult.GetDirectoryInfoCollection(configuration, + _ = Property.Models.Stateless.IResult.GetDirectoryInfoCollection(configuration, container.SourceDirectory, eResultsFullGroupDirectory, contentDescription: string.Empty, singletonDescription: "n json file(s) for each face found", collectionDescription: string.Empty, - converted: true)); - } - - private (Sorting Sorting, FaceRecognitionDotNet.FaceEncoding FaceEncoding)[] GetValues(string argZero, Container[] containers) - { - (Sorting Sorting, FaceRecognitionDotNet.FaceEncoding FaceEncoding)[] results; - Sorting sorting; - Item[] filteredItems; - List<(Sorting Sorting, FaceRecognitionDotNet.FaceEncoding FaceEncoding)> collection = new(); - foreach (Container container in containers) - { - if (!container.Items.Any()) - continue; - if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) - continue; - filteredItems = (from l in container.Items where l.ImageFileHolder is not null && (l.Abandoned is null || !l.Abandoned.Value) && l.ValidImageFormatExtension && !_Configuration.IgnoreExtensions.Contains(l.ImageFileHolder.ExtensionLowered) select l).ToArray(); - if (!filteredItems.Any()) - continue; - foreach (Item item in filteredItems) - { - if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) - continue; - if (!item.Faces.Any()) - continue; - foreach (Face face in item.Faces) - { - if (face.FaceEncoding is null || face.Location?.Confidence is null || face.Location?.NormalizedPixelPercentage is null) - continue; - if (!face.Tags.Any() || face.Tags[0] is not FaceRecognitionDotNet.FaceEncoding faceEncoding) - continue; - sorting = new(face.Location.Confidence, item.Property.Id.Value, face.Location.NormalizedPixelPercentage.Value); - collection.Add(new(sorting, faceEncoding)); - face.Tags.Clear(); - } - } - } - results = (from l in collection orderby l.FaceEncoding is not null, l.Sorting.Id select l).ToArray(); - if (results.Any() && results[0].FaceEncoding is null) - throw new Exception("Sorting failed!"); - return results; - } - - private void SaveFaceNumbers(string eResultsFullGroupDirectory, Item item, (Sorting Sorting, FaceRecognitionDotNet.FaceEncoding FaceEncoding)[] inputCollection, List faceEncodings) - { - Sorting sorting; - List doubles; - List faceNumbers; - List sortingOutputCollection; - FaceRecognitionDotNet.FaceEncoding faceEncoding; - if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) - throw new Exception(); - foreach (Face face in item.Faces) - { - if (face.FaceEncoding is null || face.Location?.Confidence is null || face.Location?.NormalizedPixelPercentage is null) - continue; - sortingOutputCollection = new(); - faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); - doubles = FaceRecognition.FaceDistances(faceEncodings, faceEncoding); - for (int j = 0; j < doubles.Count; j++) - { - sorting = new(inputCollection[j].Sorting, doubles[j]); - lock (sortingOutputCollection) - sortingOutputCollection.Add(sorting); - } - faceNumbers = Shared.Models.Stateless.Methods.ISorting.GetFaceNumbers(sortingOutputCollection); - lock (face) - face.SetFaceNumbers(faceNumbers); - _Distance.SaveFaceNumbers(eResultsFullGroupDirectory, item, face, faceNumbers); - } - } - - private void SaveFaceNumbers(string argZero, A_Property propertyLogic, string outputResolution, string aResultsFullGroupDirectory, string bResultsFullGroupDirectory, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string eResultsFullGroupDirectory, Container[] containers, (Sorting Sorting, FaceRecognitionDotNet.FaceEncoding FaceEncoding)[] collection) - { - if (collection.Any() && collection[0].FaceEncoding is null) - throw new Exception("Sorting failed!"); - Item[] filteredItems; - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism }; - foreach (Container container in containers) - { - if (!container.Items.Any()) - continue; - if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) - continue; - filteredItems = (from l in container.Items where l.ImageFileHolder is not null && (l.Abandoned is null || !l.Abandoned.Value) && l.ValidImageFormatExtension && !_Configuration.IgnoreExtensions.Contains(l.ImageFileHolder.ExtensionLowered) select l).ToArray(); - if (!filteredItems.Any()) - continue; - List faceEncodings = new(); - foreach ((Sorting _, FaceRecognitionDotNet.FaceEncoding faceEncoding) in collection) - { - if (faceEncoding is null) - continue; - faceEncodings.Add(faceEncoding); - } - SetAngleBracketCollections(_Configuration.PropertyConfiguration, propertyLogic, outputResolution, container, aResultsFullGroupDirectory, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, eResultsFullGroupDirectory, onlyE: true); - _ = Parallel.For(0, filteredItems.Length, parallelOptions, (i, state) => SaveFaceNumbers(eResultsFullGroupDirectory, filteredItems[i], collection, faceEncodings)); - } - } - - private static void SetFaceTags(Item item) - { - FaceRecognitionDotNet.FaceEncoding faceEncoding; - foreach (Face face in item.Faces) - { - if (face.FaceEncoding is null || face.Location?.Confidence is null || face.Location?.NormalizedPixelPercentage is null) - continue; - faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); - lock (face.Tags) - face.Tags.Add(faceEncoding); - } - } - - private void SetFaceTags(string argZero, List faceEncodingRequired, Container[] containers) - { - Item[] filteredItems; - faceEncodingRequired.Clear(); - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism }; - foreach (Container container in containers) - { - if (!container.Items.Any()) - continue; - if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) - continue; - filteredItems = (from l in container.Items where l.ImageFileHolder is not null && (l.Abandoned is null || !l.Abandoned.Value) && l.ValidImageFormatExtension && !_Configuration.IgnoreExtensions.Contains(l.ImageFileHolder.ExtensionLowered) select l).ToArray(); - if (!filteredItems.Any()) - continue; - _ = Parallel.For(0, filteredItems.Length, parallelOptions, (i, state) => SetFaceTags(filteredItems[i])); - } + converted: true); } private void FullDoWork(string argZero, Model? model, PredictorModel? predictorModel, string propertyRoot, long ticks, MapLogic mapLogic, A_Property propertyLogic, Container[] containers) { if (_Log is null) throw new NullReferenceException(nameof(_Log)); - int[] ids; - bool needToSave; - int distinctCount; int exceptionCount; Item[] filteredItems; string aResultsFullGroupDirectory; @@ -714,7 +576,6 @@ public partial class DlibDotNet string dResultsFullGroupDirectory; string eResultsFullGroupDirectory; string d2ResultsFullGroupDirectory; - List faceEncodingRequired = new(); Shared.Models.Property[] propertyCollection; List?> imageFaceCollections = new(); List propertyFileHolderCollection = new(); @@ -726,7 +587,6 @@ public partial class DlibDotNet foreach (string outputResolution in _Configuration.OutputResolutions) { _FileKeyValuePairs.Clear(); - faceEncodingRequired.Clear(); _FilePropertiesKeyValuePairs.Clear(); (aResultsFullGroupDirectory, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, eResultsFullGroupDirectory) = GetResultsFullGroupDirectories(_Configuration.PropertyConfiguration, model, predictorModel, outputResolution); foreach (Container container in containers) @@ -744,14 +604,8 @@ public partial class DlibDotNet sourceDirectoryChanges.Clear(); nullablePropertyCollection.Clear(); propertyFileHolderCollection.Clear(); - SetAngleBracketCollections(_Configuration.PropertyConfiguration, propertyLogic, outputResolution, container, aResultsFullGroupDirectory, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, eResultsFullGroupDirectory, onlyE: false); - exceptionCount = FullParallelWork(ticks, propertyLogic, outputResolution, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, eResultsFullGroupDirectory, sourceDirectoryChanges, propertyFileHolderCollection, nullablePropertyCollection, metadataCollection, resizeKeyValuePairs, imageFaceCollections, faceEncodingRequired, containers.Length, container, filteredItems); -#pragma warning disable - ids = (from l in filteredItems where l.Property?.Id is not null select l.Property.Id.Value).ToArray(); -#pragma warning restore - distinctCount = ids.Distinct().Count(); - if (ids.Length != distinctCount) - _Log.Information($"{ids.Length} != {distinctCount} <{container.SourceDirectory}>"); + SetAngleBracketCollections(_Configuration.PropertyConfiguration, propertyLogic, outputResolution, container, aResultsFullGroupDirectory, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, eResultsFullGroupDirectory); + exceptionCount = FullParallelWork(ticks, propertyLogic, outputResolution, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, sourceDirectoryChanges, propertyFileHolderCollection, nullablePropertyCollection, metadataCollection, resizeKeyValuePairs, imageFaceCollections, containers.Length, container, filteredItems); if (metadataCollection.Count != filteredItems.Length || nullablePropertyCollection.Count != filteredItems.Length || resizeKeyValuePairs.Count != filteredItems.Length || imageFaceCollections.Count != filteredItems.Length) throw new Exception("Counts don't match!"); if (exceptionCount != 0) @@ -761,8 +615,6 @@ public partial class DlibDotNet propertyCollection = (from l in nullablePropertyCollection where l is not null select l).ToArray(); if (_ArgZeroIsConfigurationRootDirectory && exceptionCount == 0) WriteGroup(_Configuration.PropertyConfiguration, propertyLogic, propertyCollection, metadataCollection, resizeKeyValuePairs, imageFaceCollections, outputResolution, container, filteredItems); - if (false && exceptionCount == 0 && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)) - _Distance.LoadOrCreateThenSaveDistanceResults(_Configuration.PropertyConfiguration, eResultsFullGroupDirectory, outputResolution, container, sourceDirectoryChanges, filteredItems); if (Directory.GetFiles(propertyRoot, "*.txt", SearchOption.TopDirectoryOnly).Any()) { for (int y = 0; y < int.MaxValue; y++) @@ -786,79 +638,9 @@ public partial class DlibDotNet // if (isSilent && container.SourceDirectory.EndsWith("Texas 2015")) //46#.4 // break; } - if (_Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)) - { - needToSave = faceEncodingRequired.Any(); - if (needToSave) - SetFaceTags(argZero, faceEncodingRequired, containers); - if (faceEncodingRequired.Any()) - throw new Exception("Why!"); - if (needToSave) - { - (Sorting Sorting, FaceRecognitionDotNet.FaceEncoding FaceEncoding)[] collection = GetValues(argZero, containers); - SaveFaceNumbers(argZero, propertyLogic, outputResolution, aResultsFullGroupDirectory, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, eResultsFullGroupDirectory, containers, collection); - } - } } } - private static List<(string, int, Mapping, DateTime, bool?, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>)> Convert(Dictionary> keyValuePairs) - { - List<(string, int, Mapping, DateTime, bool?, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>)> results = new(); - MappingContainer mc; - foreach (KeyValuePair> keyValuePair in keyValuePairs) - { - foreach ((FaceRecognitionDotNet.FaceEncoding _, MappingContainer mappingContainer) in keyValuePair.Value) - { - mc = mappingContainer; - results.Add(new(mc.Key, mc.Id, mc.Mapping, mc.MinimumDateTime, mc.IsWrongYear, keyValuePair.Value)); - } - } - return results; - } - - private static Dictionary> Strip(Dictionary> keyValuePairs) - { - Dictionary> results = new(); - foreach (KeyValuePair> keyValuePair in keyValuePairs) - { - foreach ((FaceRecognitionDotNet.FaceEncoding _, MappingContainer mappingContainer) in keyValuePair.Value) - { - if (!results.ContainsKey(mappingContainer.Id)) - results.Add(mappingContainer.Id, new()); - results[mappingContainer.Id].Add(mappingContainer); - } - } - return results; - } - - private void MapLogic(long ticks, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, MapLogic mapLogic, string outputResolution, Item[] items, int totalNotMapped) - { - mapLogic.UseKeyValuePairsSaveFaceEncoding(items); - if (_Configuration.SaveResizedSubfiles) - { - string dFacesContentDirectory; - dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, "()"); - mapLogic.SaveNotMappedTicks(); - _ = LogDeltaInMinutes(ticks, nameof(mapLogic.SaveNotMappedTicks)); - Dictionary> keyValuePairs = _Distance.ParallelWork(_AppSettings.MaxDegreeOfParallelism, ticks, mapLogic, items, totalNotMapped); - _ = LogDeltaInSeconds(ticks, nameof(E_Distance.ParallelWork)); - Dictionary> strippedKeyValuePairs = Strip(keyValuePairs); - List<(string, int, Mapping, DateTime, bool?, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>)> collection = Convert(keyValuePairs); - mapLogic.SaveMapping(items, dFacesContentDirectory, d2ResultsFullGroupDirectory); - _ = LogDeltaInMinutes(ticks, nameof(mapLogic.SaveMapping)); - E_Distance.AddToFaceDistance(_AppSettings.MaxDegreeOfParallelism, mapLogic, items, collection); - _ = LogDeltaInSeconds(ticks, nameof(_Distance.AddToFaceDistance)); - mapLogic.AddToClosest(_AppSettings.MaxDegreeOfParallelism, items); - _ = LogDeltaInMinutes(ticks, nameof(mapLogic.AddToClosest)); - mapLogic.SaveClosest(items, dFacesContentDirectory, d2ResultsFullGroupDirectory); - _ = LogDeltaInMinutes(ticks, nameof(mapLogic.SaveClosest)); - mapLogic.SavePropertyHolders(items); - _ = LogDeltaInSeconds(ticks, nameof(mapLogic.SavePropertyHolders)); - } - _ = LogDeltaInSeconds(ticks, nameof(MapLogic)); - } - private string GetMapLogicResultsFullGroupDirectory(Property.Models.Configuration configuration, Model? model, PredictorModel? predictorModel) { const int zero = 0; @@ -868,6 +650,79 @@ public partial class DlibDotNet return zResultsFullGroupDirectory; } + private List SetMappingThenGetDistinctFilteredFacesWithMapping(string argZero, Container[] containers) + { + List results = new(); + Mapping mapping; + bool? isWrongYear; + Item[] filteredItems; + DateTime minimumDateTime; + List distinctCheck = new(); + string deterministicHashCodeKeyDisplay; + MappingFromItem mappingFromItem; + MappingFromPerson mappingFromPerson; + MappingFromLocation mappingFromLocation; + foreach (Container container in containers) + { + if (!container.Items.Any()) + continue; + if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) + continue; + filteredItems = (from l in container.Items where l.ImageFileHolder is not null && (l.Abandoned is null || !l.Abandoned.Value) && l.ValidImageFormatExtension && !_Configuration.IgnoreExtensions.Contains(l.ImageFileHolder.ExtensionLowered) select l).ToArray(); + if (!filteredItems.Any()) + continue; + foreach (Item item in filteredItems) + { + if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) + continue; + if (!item.Faces.Any()) + continue; + foreach (Face face in item.Faces) + { + if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) + continue; + if (distinctCheck.Contains(item.Property.Id.Value)) + continue; + minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); + (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); + mappingFromItem = new(item.Property.Id.Value, item.ImageFileHolder, isWrongYear, minimumDateTime, item.ResizedFileHolder); + mappingFromPerson = new(approximateYears: null, by: null, displayDirectoryName: string.Empty, personBirthday: null, segmentB: string.Empty); + deterministicHashCodeKeyDisplay = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKeyDisplay(item.Property.Id.Value, face.Location.NormalizedPixelPercentage.Value); + mappingFromLocation = new(face.Location.Confidence, deterministicHashCodeKeyDisplay, face.Location.NormalizedPixelPercentage.Value); + mapping = new(mappingFromItem, mappingFromLocation, mappingFromPerson); + face.SetMapping(mapping); + results.Add(face); + } + } + } + return results; + } + + private Item[] GetIdSortedFilteredItems(string argZero, Container[] containers) + { + Item[] results; + List collection = new(); + foreach (Container container in containers) + { + if (!container.Items.Any()) + continue; + if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) + continue; + collection.AddRange(from l in container.Items where l.ImageFileHolder is not null && (l.Abandoned is null || !l.Abandoned.Value) && l.ValidImageFormatExtension && !_Configuration.IgnoreExtensions.Contains(l.ImageFileHolder.ExtensionLowered) select l); + } + results = (from l in collection orderby l.Property?.Id is not null, l.Property?.Id select l).ToArray(); + return results; + } + + private void OldMapLogic(long ticks, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, MapLogic mapLogic, string outputResolution, Item[] filteredItems) + { + string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, "()"); + if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) + mapLogic.SaveShortcuts(_Configuration.JuliePhares, filteredItems); + mapLogic.SavePropertyHolders(filteredItems); + _ = LogDeltaInSeconds(ticks, nameof(mapLogic.SavePropertyHolders)); + } + private void Search(Property.Models.Configuration configuration, Model? model, PredictorModel? predictorModel, string argZero, string propertyRoot, Person[] people) { Container[] containers; @@ -882,30 +737,36 @@ public partial class DlibDotNet string peopleDateGroupDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A2_People)); MapLogic mapLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FilenameExtension, _Faces.FilenameExtension, _Faces.HiddenFilenameExtension, _FaceParts.FilenameExtension, ticks, people, peopleDateGroupDirectory, zResultsFullGroupDirectory); A_Property propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FilenameExtension, _Configuration.Reverse, model, predictorModel, mapLogic.IndicesFromNew, mapLogic.KeyValuePairs); - if (string.IsNullOrEmpty(configuration.RootDirectory)) - containers = A_Property.Get(configuration, propertyLogic); - else - containers = Property.Models.Stateless.Container.GetContainers(configuration, _FirstRun, propertyLogic); + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + string message = $") Building Container(s) - {totalSeconds} total second(s)"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + using (ProgressBar progressBar = new(1, message, options)) + { + if (string.IsNullOrEmpty(configuration.RootDirectory)) + containers = A_Property.Get(configuration, propertyLogic); + else + containers = Property.Models.Stateless.Container.GetContainers(configuration, _FirstRun, propertyLogic); + } FullDoWork(argZero, model, predictorModel, propertyRoot, ticks, mapLogic, propertyLogic, containers); foreach (string outputResolution in _Configuration.OutputResolutions) { if (_FirstRun) break; (aResultsFullGroupDirectory, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, eResultsFullGroupDirectory) = GetResultsFullGroupDirectories(configuration, model, predictorModel, outputResolution); - if (_ArgZeroIsConfigurationRootDirectory && _Exceptions.Count == 0 && outputResolution == _Configuration.OutputResolutions[0]) + if (_ArgZeroIsConfigurationRootDirectory && _Configuration.SaveResizedSubfiles && outputResolution == _Configuration.OutputResolutions[0] && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution) && _Exceptions.Count == 0) { - int totalNotMapped = 0; - DateTime dateTime = new(ticks); - List<(int Id, Item Item)> idAndItemCollection = new(); - foreach (Container container in containers) - { - totalNotMapped += mapLogic.AddToMapping(ticks, container.Items); - Map.Models.MapLogic.AppendToItems(container.Items, idAndItemCollection); - if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) - mapLogic.SaveShortcuts(_Configuration.JuliePhares, container.Items); - } - Item[] items = (from l in idAndItemCollection orderby l.Id select l.Item).ToArray(); - MapLogic(ticks, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, mapLogic, outputResolution, items, totalNotMapped); + if (string.IsNullOrEmpty(eResultsFullGroupDirectory)) + throw new NullReferenceException(nameof(eResultsFullGroupDirectory)); + string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, "()"); + string eDistanceContentDirectory = Path.Combine(eResultsFullGroupDirectory, "([])"); + if (!Directory.Exists(eDistanceContentDirectory)) + _ = Directory.CreateDirectory(eDistanceContentDirectory); + string eDistanceContentFileName = Path.Combine(eDistanceContentDirectory, $"{Property.Models.Stateless.IResult.AllInOne}.tvs"); + List distinctFilteredFaces = SetMappingThenGetDistinctFilteredFacesWithMapping(argZero, containers); + SortingContainer[] sortingContainers = E_Distance.SetPersonTicksAndSetFaceDistancesAndSetFaceMappingSortingCollectionThenGetSortingContainers(_AppSettings.MaxDegreeOfParallelism, ticks, mapLogic, outputResolution, eDistanceContentFileName, distinctFilteredFaces); + mapLogic.CommonWork(_AppSettings.MaxDegreeOfParallelism, dFacesContentDirectory, d2ResultsFullGroupDirectory, distinctFilteredFaces, sortingContainers); + Item[] filteredItems = GetIdSortedFilteredItems(argZero, containers); + OldMapLogic(ticks, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, mapLogic, outputResolution, filteredItems); if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Any()) break; if (_FileKeyValuePairs.Any()) @@ -926,8 +787,6 @@ public partial class DlibDotNet _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(dResultsFullGroupDirectory, "[]")); if (_Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)) _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(eResultsFullGroupDirectory, "[]")); - if (_Configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions.Contains(outputResolution)) - _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(eResultsFullGroupDirectory, "[]")); if (_Configuration.SaveFaceLandmarkForOutputResolutions.Contains(outputResolution)) _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(d2ResultsFullGroupDirectory, "[]")); } diff --git a/Instance/Models/Binder/Configuration.cs b/Instance/Models/Binder/Configuration.cs index 7bfe922..22f6512 100644 --- a/Instance/Models/Binder/Configuration.cs +++ b/Instance/Models/Binder/Configuration.cs @@ -19,7 +19,6 @@ public class Configuration [Display(Name = "Ignore Extensions"), Required] public string[] IgnoreExtensions { get; set; } [Display(Name = "Ignore Relative Paths"), Required] public string[] IgnoreRelativePaths { get; set; } [Display(Name = "Julie Phares Copy Birthdays"), Required] public string[] JuliePhares { get; set; } - [Display(Name = "Load Or Create Then Save Directory Distance Results"), Required] public string[] LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions { get; set; } [Display(Name = "Load Or Create Then Save Distance Results"), Required] public string[] LoadOrCreateThenSaveDistanceResultsForOutputResolutions { get; set; } [Display(Name = "Load Or Create Then Save Image Faces Results"), Required] public string[] LoadOrCreateThenSaveImageFacesResultsForOutputResolutions { get; set; } [Display(Name = "Load Or Create Then Save Index"), Required] public bool? LoadOrCreateThenSaveIndex { get; set; } @@ -129,8 +128,6 @@ public class Configuration throw new NullReferenceException(nameof(configuration.TestDistanceResults)); if (configuration.ValidResolutions is null) throw new NullReferenceException(nameof(configuration.ValidResolutions)); - if (configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions is null) - configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions = Array.Empty(); if (configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions is null) configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions = Array.Empty(); if (configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions is null) @@ -149,7 +146,6 @@ public class Configuration configuration.IgnoreExtensions, configuration.IgnoreRelativePaths, configuration.JuliePhares, - configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions, configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions, configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions, configuration.LoadOrCreateThenSaveIndex.Value, diff --git a/Instance/Models/Configuration.cs b/Instance/Models/Configuration.cs index a3717c0..6095e5f 100644 --- a/Instance/Models/Configuration.cs +++ b/Instance/Models/Configuration.cs @@ -18,7 +18,6 @@ public class Configuration public string[] IgnoreExtensions { init; get; } public string[] IgnoreRelativePaths { init; get; } public string[] JuliePhares { init; get; } - public string[] LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions { init; get; } public string[] LoadOrCreateThenSaveDistanceResultsForOutputResolutions { init; get; } public string[] LoadOrCreateThenSaveImageFacesResultsForOutputResolutions { init; get; } public bool LoadOrCreateThenSaveIndex { init; get; } @@ -53,7 +52,7 @@ public class Configuration public string[] ValidResolutions { init; get; } [JsonConstructor] - public Configuration(Property.Models.Configuration propertyConfiguration, bool checkJsonForDistanceResults, int crossDirectoryMaxItemsInDistanceCollection, int distanceFactor, bool forceFaceLastWriteTimeToCreationTime, bool forceMetadataLastWriteTimeToCreationTime, bool forceResizeLastWriteTimeToCreationTime, string[] ignoreExtensions, string[] ignoreRelativePaths, string[] juliePhares, string[] loadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions, string[] loadOrCreateThenSaveDistanceResultsForOutputResolutions, string[] loadOrCreateThenSaveImageFacesResultsForOutputResolutions, bool loadOrCreateThenSaveIndex, int locationConfidenceFactor, int? mappedMaxIndex, int maxItemsInDistanceCollection, string[] mixedYearRelativePaths, string modelDirectory, string modelName, int numberOfJitters, int numberOfTimesToUpsample, string outputExtension, int outputQuality, string[] outputResolutions, bool overrideForFaceImages, bool overrideForFaceLandmarkImages, bool overrideForResizeImages, int paddingLoops, string predictorModelName, bool propertiesChangedForDistance, bool propertiesChangedForFaces, bool propertiesChangedForIndex, bool propertiesChangedForMetadata, bool propertiesChangedForResize, bool reverse, string[] saveFaceLandmarkForOutputResolutions, bool saveFullYearOfRandomFiles, bool saveResizedSubfiles, string[] saveShortcutsForOutputResolutions, bool skipSearch, bool testDistanceResults, string[] validResolutions) + public Configuration(Property.Models.Configuration propertyConfiguration, bool checkJsonForDistanceResults, int crossDirectoryMaxItemsInDistanceCollection, int distanceFactor, bool forceFaceLastWriteTimeToCreationTime, bool forceMetadataLastWriteTimeToCreationTime, bool forceResizeLastWriteTimeToCreationTime, string[] ignoreExtensions, string[] ignoreRelativePaths, string[] juliePhares, string[] loadOrCreateThenSaveDistanceResultsForOutputResolutions, string[] loadOrCreateThenSaveImageFacesResultsForOutputResolutions, bool loadOrCreateThenSaveIndex, int locationConfidenceFactor, int? mappedMaxIndex, int maxItemsInDistanceCollection, string[] mixedYearRelativePaths, string modelDirectory, string modelName, int numberOfJitters, int numberOfTimesToUpsample, string outputExtension, int outputQuality, string[] outputResolutions, bool overrideForFaceImages, bool overrideForFaceLandmarkImages, bool overrideForResizeImages, int paddingLoops, string predictorModelName, bool propertiesChangedForDistance, bool propertiesChangedForFaces, bool propertiesChangedForIndex, bool propertiesChangedForMetadata, bool propertiesChangedForResize, bool reverse, string[] saveFaceLandmarkForOutputResolutions, bool saveFullYearOfRandomFiles, bool saveResizedSubfiles, string[] saveShortcutsForOutputResolutions, bool skipSearch, bool testDistanceResults, string[] validResolutions) { _PropertyConfiguration = propertyConfiguration; CheckJsonForDistanceResults = checkJsonForDistanceResults; @@ -65,7 +64,6 @@ public class Configuration IgnoreExtensions = ignoreExtensions; IgnoreRelativePaths = ignoreRelativePaths; JuliePhares = juliePhares; - LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions = loadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions; LoadOrCreateThenSaveDistanceResultsForOutputResolutions = loadOrCreateThenSaveDistanceResultsForOutputResolutions; LoadOrCreateThenSaveImageFacesResultsForOutputResolutions = loadOrCreateThenSaveImageFacesResultsForOutputResolutions; LoadOrCreateThenSaveIndex = loadOrCreateThenSaveIndex; diff --git a/Instance/Models/_D2_FaceParts.cs b/Instance/Models/_D2_FaceParts.cs index 54c446d..3fbaa4f 100644 --- a/Instance/Models/_D2_FaceParts.cs +++ b/Instance/Models/_D2_FaceParts.cs @@ -118,7 +118,7 @@ internal class D2_FaceParts #pragma warning restore CA1416 - internal void SaveFaceLandmarkImages(string d2ResultsFullGroupDirectory, Container container, List> subFileTuples, List parseExceptions, Item item, List faceCollection, bool saveRotated) + internal void SaveFaceLandmarkImages(string facesDirectory, List> subFileTuples, List parseExceptions, Item item, List faceCollection, bool saveRotated) { if (item.ImageFileHolder is null) throw new NullReferenceException(nameof(item.ImageFileHolder)); @@ -130,33 +130,23 @@ internal class D2_FaceParts const int pointSize = 2; FileInfo rotatedFileInfo; DateTime? dateTime = null; - double deterministicHashCodeKey; long ticks = DateTime.Now.Ticks; bool updateDateWhenMatches = false; - List angleBracketCollection = new(); + string deterministicHashCodeKeyDisplay; List<(Face, string, string)> collection = new(); - angleBracketCollection.AddRange(Property.Models.Stateless.IResult.GetDirectoryInfoCollection( - _Configuration.PropertyConfiguration, - container.SourceDirectory, - d2ResultsFullGroupDirectory, - contentDescription: "n x 2 gif file(s) for each face found", - singletonDescription: string.Empty, - collectionDescription: string.Empty, - converted: false)); - string facesDirectory = Path.Combine(angleBracketCollection[0].Replace("<>", "()"), item.ImageFileHolder.NameWithoutExtension); string[] changesFrom = new string[] { nameof(Property.Models.A_Property), nameof(B_Metadata), nameof(C_Resize), nameof(D_Face) }; List dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList(); if (!Directory.Exists(facesDirectory)) _ = Directory.CreateDirectory(facesDirectory); foreach (Face face in faceCollection) { - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) + if (item.Property?.Id is null || face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) { collection.Add(new(face, string.Empty, string.Empty)); continue; } - deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); - fileInfo = new FileInfo(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}")); + deterministicHashCodeKeyDisplay = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKeyDisplay(item.Property.Id.Value, face.Location.NormalizedPixelPercentage.Value); + fileInfo = new FileInfo(Path.Combine(facesDirectory, $"{deterministicHashCodeKeyDisplay}{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}")); if (!fileInfo.Exists) { if (fileInfo.Directory?.Parent is null) @@ -167,7 +157,7 @@ internal class D2_FaceParts } if (string.IsNullOrEmpty(fileInfo.DirectoryName)) continue; - rotatedFileInfo = new FileInfo(Path.Combine(fileInfo.DirectoryName, $"{deterministicHashCodeKey} - R{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}")); + rotatedFileInfo = new FileInfo(Path.Combine(fileInfo.DirectoryName, $"{deterministicHashCodeKeyDisplay} - R{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}")); collection.Add(new(face, fileInfo.FullName, rotatedFileInfo.FullName)); if (check) continue; diff --git a/Instance/Models/_D_Face.cs b/Instance/Models/_D_Face.cs index 31018c9..541ded4 100644 --- a/Instance/Models/_D_Face.cs +++ b/Instance/Models/_D_Face.cs @@ -2,6 +2,7 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Text.Json; +using System.Text.Json.Serialization; using View_by_Distance.FaceRecognitionDotNet; using View_by_Distance.Metadata.Models; using View_by_Distance.Property.Models; @@ -35,7 +36,7 @@ public class D_Face private readonly EncoderParameters _EncoderParameters; private readonly ImageCodecInfo _HiddenImageCodecInfo; private readonly EncoderParameters _HiddenEncoderParameters; - private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions; + private readonly JsonSerializerOptions _WriteIndentedAndWhenWritingNull; internal D_Face(Configuration configuration, string argZero, Model model, ModelParameter modelParameter, PredictorModel predictorModel, ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension, ImageCodecInfo hiddenImageCodecInfo, EncoderParameters hiddenEncoderParameters, string hiddenFilenameExtension) { @@ -52,7 +53,7 @@ public class D_Face _HiddenImageCodecInfo = hiddenImageCodecInfo; _HiddenEncoderParameters = hiddenEncoderParameters; _HiddenFilenameExtension = hiddenFilenameExtension; - _WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true }; + _WriteIndentedAndWhenWritingNull = new JsonSerializerOptions { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; } private static void GetPointBounds(PointF[] points, out float xMinimum, out float xMaximum, out float yMinimum, out float yMaximum) @@ -321,7 +322,7 @@ public class D_Face if (results is null) { results = GetFaces(item, property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation); - json = JsonSerializer.Serialize(results, _WriteIndentedJsonSerializerOptions); + json = JsonSerializer.Serialize(results, _WriteIndentedAndWhenWritingNull); bool updateDateWhenMatches = dateTimes.Any() && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime; DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max(); if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime)) @@ -334,7 +335,7 @@ public class D_Face normalizedPixelPercentageDistinctCount = normalizedPixelPercentageCollection.Distinct().Count(); if (normalizedPixelPercentageDistinctCount != normalizedPixelPercentageCollection.Length) throw new Exception($"Not distinct! <{fileInfo.FullName}>"); - json = JsonSerializer.Serialize(results, _WriteIndentedJsonSerializerOptions); + json = JsonSerializer.Serialize(results, _WriteIndentedAndWhenWritingNull); bool updateDateWhenMatches = dateTimes.Any() && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime; DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max(); if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime)) @@ -356,7 +357,7 @@ public class D_Face FileInfo fileInfo; bool check = false; string parentCheck; - double deterministicHashCodeKey; + string deterministicHashCodeKeyDisplay; List<(Face, FileInfo?, string)> collection = new(); string[] changesFrom = new string[] { nameof(A_Property), nameof(B_Metadata), nameof(C_Resize) }; string facesDirectory = Path.Combine(AngleBracketCollection[0].Replace("<>", "()"), item.ImageFileHolder.NameWithoutExtension); @@ -366,13 +367,13 @@ public class D_Face _ = Directory.CreateDirectory(facesDirectory); foreach (Face face in faceCollection) { - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) + if (item.Property?.Id is null || face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) { collection.Add(new(face, null, string.Empty)); continue; } - deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); - fileInfo = new FileInfo(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}")); + deterministicHashCodeKeyDisplay = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKeyDisplay(item.Property.Id.Value, face.Location.NormalizedPixelPercentage.Value); + fileInfo = new FileInfo(Path.Combine(facesDirectory, $"{deterministicHashCodeKeyDisplay}{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}")); if (!fileInfo.Exists) { if (fileInfo.Directory?.Parent is null) @@ -381,7 +382,7 @@ public class D_Face if (File.Exists(parentCheck)) File.Delete(parentCheck); } - collection.Add(new(face, fileInfo, Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_HiddenFilenameExtension}"))); + collection.Add(new(face, fileInfo, Path.Combine(facesDirectory, $"{deterministicHashCodeKeyDisplay}{item.ImageFileHolder.ExtensionLowered}{_HiddenFilenameExtension}"))); if (_Configuration.OverrideForFaceImages) check = true; else if (!fileInfo.Exists) diff --git a/Instance/Models/_E_Distance.cs b/Instance/Models/_E_Distance.cs index 0b9299c..f1d7508 100644 --- a/Instance/Models/_E_Distance.cs +++ b/Instance/Models/_E_Distance.cs @@ -1,8 +1,6 @@ -using System.Text.Json; +using ShellProgressBar; 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.Map.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless; @@ -11,764 +9,135 @@ namespace View_by_Distance.Instance.Models; internal class E_Distance { - internal List AngleBracketCollection { get; } - - private readonly Serilog.ILogger? _Log; - private readonly Configuration _Configuration; - private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions; - - internal E_Distance(Configuration configuration) + private static void SaveFaceDistances(string eDistanceContentFileName, SortingContainer[] sortingContainers) { - _Configuration = configuration; - AngleBracketCollection = new List(); - _Log = Serilog.Log.ForContext(); - _WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true }; +#pragma warning disable + string[] results = (from l in sortingContainers select string.Concat(l.Sorting.WithinRange, '\t', l.Sorting.DistancePermyriad, '\t', l.Sorting.DaysDelta, '\t', l.Sorting.Id, '\t', l.Sorting.NormalizedPixelPercentage, '\t', l.Sorting.Older, '\t', l.Face.Mapping.MappingFromItem.Id, '\t', l.Face.Mapping.MappingFromLocation.NormalizedPixelPercentage)).ToArray(); +#pragma warning restore + File.WriteAllLines(eDistanceContentFileName, results); } - public override string ToString() + private static List GetSortingCollection(MapLogic mapLogic, List faceDistanceEncodings, int faceDistanceContainersLength, int i, FaceDistance faceDistanceEncoding) { - string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); - return result; - } - - private static List<(DistanceHolder, FaceRecognitionDotNet.FaceEncoding?)> GetDistanceHolder(Item[] items, List<(string JSONDirectory, string TSVDirectory)> directories) - { - List<(DistanceHolder, FaceRecognitionDotNet.FaceEncoding?)> results = new(); - Item item; - const int zero = 0; - string tsvDirectory; - string jsonDirectory; - FaceRecognitionDotNet.FaceEncoding? faceEncoding; - if (items.Length != directories.Count) - throw new Exception(); - for (int i = 0; i < items.Length; i++) - { - faceEncoding = null; - item = items[i]; - if (item.ImageFileHolder is null || item.Property?.Id is null || !item.Faces.Any()) - continue; - tsvDirectory = directories[i].TSVDirectory; - jsonDirectory = directories[i].JSONDirectory; - foreach (Face face in item.Faces) - { - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - continue; - faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); - results.Add(new(new(face, item.ImageFileHolder, item.Property.Id.Value, jsonDirectory, item.Faces[zero].Location, tsvDirectory), faceEncoding)); - } - if (faceEncoding is null) - results.Add(new(new(item.Faces[zero], item.ImageFileHolder, item.Property.Id.Value, jsonDirectory, item.Faces[zero].Location, tsvDirectory), null)); - } + List results; + List faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding); + if (faceDistanceLengths.Count != faceDistanceContainersLength) + throw new NotSupportedException(); + bool anyLowerThanTolerance = (from l in faceDistanceLengths where l.Length is not null && l.Length.Value != 0 && l.Length.Value < IFaceDistance.Tolerance select true).Any(); + results = mapLogic.GetSortingCollection(i, faceDistanceEncoding, faceDistanceLengths, anyLowerThanTolerance); return results; } - private void WriteNoFaceCollection(bool updateDateWhenMatches, DateTime? updateToWhenMatches, List> subFileTuples, List distanceHolders) + private static List GetSortingContainers(Face face, FaceDistance faceDistanceEncoding, List sortingCollection) { - string json; - string check; - string jsonFile; - const int zero = 0; - DistanceHolder distanceHolder; - List> tupleCollection; - for (int i = 0; i < distanceHolders.Count; i++) + List results = new(); + SortingContainer sortingContainer; + Sorting[] collection = Shared.Models.Stateless.Methods.ISorting.Sort(sortingCollection); + foreach (Sorting sorting in collection) { - distanceHolder = distanceHolders[i]; - if (distanceHolder.Face.Location?.NormalizedPixelPercentage is null) + if (faceDistanceEncoding.NormalizedPixelPercentage is null) + throw new NotSupportedException(); + if (sorting.DistancePermyriad > IFaceDistance.Permyriad || sorting.DaysDelta > ISorting.DaysDeltaTolerance) continue; - check = Path.Combine(distanceHolder.JSONDirectory, $"{zero} - {distanceHolder.FileHolder.NameWithoutExtension}.json"); - jsonFile = Path.Combine(distanceHolder.JSONDirectory, $"{distanceHolder.Id}.{zero}{distanceHolder.FileHolder.ExtensionLowered}.json"); - if (File.Exists(check)) - File.Move(check, jsonFile); - tupleCollection = new() { new(distanceHolders[i].Face, string.Empty) }; - for (int j = 0; j < distanceHolders.Count; j++) - { - if (j == i) - continue; - distanceHolder = distanceHolders[j]; - tupleCollection.Add(new(distanceHolder.Face, string.Empty)); - if (tupleCollection.Count > _Configuration.MaxItemsInDistanceCollection) - break; - } - json = JsonSerializer.Serialize(tupleCollection, _WriteIndentedJsonSerializerOptions); - if (Shared.Models.Stateless.Methods.IPath.WriteAllText(jsonFile, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: updateToWhenMatches)) - subFileTuples.Add(new Tuple(nameof(E_Distance), DateTime.Now)); - } - } - - private static List GetFaceEncodings((DistanceHolder, FaceRecognitionDotNet.FaceEncoding?)[] distanceHoldersAfterSort) - { - List results = new(); - foreach ((DistanceHolder distanceHolder, FaceRecognitionDotNet.FaceEncoding? faceEncoding) in distanceHoldersAfterSort) - { - if (distanceHolder.Face.FaceEncoding is null || distanceHolder.Face.Location?.NormalizedPixelPercentage is null || faceEncoding is null) - continue; - results.Add(faceEncoding); - } - return results; - } - - private void SaveDistanceResults(bool updateDateWhenMatches, DateTime? updateToWhenMatches, List> subFileTuples, List<(DistanceHolder DistanceHolder, FaceRecognitionDotNet.FaceEncoding? _)> distanceHolders) - { - string json; - string check; - string jsonFile; - int locationIndex; - List faceDistances; - DistanceHolder distanceHolder; - int normalizedPixelPercentage; - DistanceHolder[] sortedDistanceHolders; - List> tupleCollection; - List<(int Index, double Distance)> collection; - FaceRecognitionDotNet.FaceEncoding? faceEncoding; - (DistanceHolder DistanceHolder, FaceRecognitionDotNet.FaceEncoding? FaceEncoding)[] distanceHoldersAfterSort = - distanceHolders.OrderByDescending(l => l.DistanceHolder.Face.FaceEncoding is not null && l.DistanceHolder.Face.Location?.NormalizedPixelPercentage is not null).ToArray(); - List faceEncodings = GetFaceEncodings(distanceHoldersAfterSort); - for (int i = 0; i < distanceHoldersAfterSort.Length; i++) - { - faceEncoding = distanceHoldersAfterSort[i].FaceEncoding; - distanceHolder = distanceHoldersAfterSort[i].DistanceHolder; - collection = new(); - tupleCollection = new(); - distanceHolder.Sort = 0d; - if (distanceHolder.Face.LocationIndex is null) - locationIndex = 0; - else - locationIndex = distanceHolder.Face.LocationIndex.Value; - if (distanceHolder.Face.FaceEncoding is null || distanceHolder.Face.Location?.NormalizedPixelPercentage is null) - normalizedPixelPercentage = 0; - else - normalizedPixelPercentage = distanceHolder.Face.Location.NormalizedPixelPercentage.Value; - check = Path.Combine(distanceHolder.JSONDirectory, $"{locationIndex} - {distanceHolder.FileHolder.NameWithoutExtension}.json"); - jsonFile = Path.Combine(distanceHolder.JSONDirectory, $"{distanceHolder.Id}.{normalizedPixelPercentage}{distanceHolder.FileHolder.ExtensionLowered}.json"); - if (!Directory.Exists(distanceHolder.JSONDirectory)) - _ = Directory.CreateDirectory(distanceHolder.JSONDirectory); - if (File.Exists(check)) - File.Move(check, jsonFile); - if (faceEncodings.Count == 1) - faceDistances = new() { 0d }; - else if (faceEncoding is null) - faceDistances = Enumerable.Repeat(9d, faceEncodings.Count).ToList(); - else - faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncoding); - if (distanceHolder.Face.FaceEncoding is not null && faceEncoding is not null && faceDistances[i] != 0d) - faceDistances[i] = 0d; - for (int d = 0; d < faceDistances.Count; d++) - collection.Add(new(d, faceDistances[d])); - collection = collection.OrderBy(l => l.Distance).ToList(); - foreach ((int index, double distance) in collection) - { - distanceHolder = distanceHoldersAfterSort[index].DistanceHolder; - if (distanceHolder.Location is null) - continue; - distanceHolder.Sort = ((distance * _Configuration.DistanceFactor) + (distanceHolder.Location.Confidence * _Configuration.LocationConfidenceFactor)) / 10; - } - sortedDistanceHolders = (from l in distanceHoldersAfterSort orderby l.DistanceHolder.Sort select l.DistanceHolder).ToArray(); - for (int j = 0; j < sortedDistanceHolders.Length; j++) - { - distanceHolder = sortedDistanceHolders[j]; - tupleCollection.Add(new(distanceHoldersAfterSort[j].DistanceHolder.Face, string.Empty)); - if (tupleCollection.Count > _Configuration.MaxItemsInDistanceCollection) - break; - } - json = JsonSerializer.Serialize(tupleCollection, _WriteIndentedJsonSerializerOptions); - if (Shared.Models.Stateless.Methods.IPath.WriteAllText(jsonFile, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: updateToWhenMatches)) - subFileTuples.Add(new Tuple(nameof(E_Distance), DateTime.Now)); - } - } - - internal void LoadOrCreateThenSaveDistanceResults(Property.Models.Configuration configuration, string eResultsFullGroupDirectory, string outputResolution, Container container, List> sourceDirectoryChanges, Item[] filteredItems) - { - Item item; - string json; - bool check = false; - string parentCheck; - bool hasPopulatedFace; - string usingRelativePath; - DateTime? dateTime = null; - string dCollectionDirectory; - FileInfo[] fileInfoCollection; - bool updateDateWhenMatches = false; - System.IO.DirectoryInfo directoryInfo; - System.IO.DirectoryInfo tvsDirectoryInfo; - int?[] normalizedPixelPercentageCollection; - int normalizedPixelPercentageDistinctCount; - List<(string, string)> directories = new(); - 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, - container.SourceDirectory, - eResultsFullGroupDirectory, - contentDescription: ".tvs File", - singletonDescription: string.Empty, - collectionDescription: "n json file(s) for each face found (one to many)", - converted: true); - for (int i = 0; i < filteredItems.Length; i++) - { - item = filteredItems[i]; - if (item.ImageFileHolder is null || item.Property?.Id is null) - continue; - hasPopulatedFace = (from l in item.Faces where l.FaceEncoding is not null && l.Location?.NormalizedPixelPercentage is not null select true).Any(); - usingRelativePath = Path.Combine(directoryInfoCollection[0].Replace("<>", "[]"), item.ImageFileHolder.NameWithoutExtension); - dCollectionDirectory = Path.Combine(eResultsFullGroupDirectory, "[]", Property.Models.Stateless.IResult.AllInOne, $"{item.Property.Id.Value}{item.ImageFileHolder.ExtensionLowered}"); - directoryInfo = new System.IO.DirectoryInfo(dCollectionDirectory); - if (!directoryInfo.Exists) - { - if (Directory.Exists(usingRelativePath)) - { - Directory.Move(usingRelativePath, directoryInfo.FullName); - directoryInfo.Refresh(); - } - if (!Directory.Exists(dCollectionDirectory)) - { - 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("<>", "()"), item.ImageFileHolder.NameWithoutExtension)); - directories.Add(new(directoryInfo.FullName, tvsDirectoryInfo.FullName)); - if (directoryInfo.Exists && (!check || _Configuration.CheckJsonForDistanceResults)) - { - json = string.Empty; - normalizedPixelPercentageCollection = Shared.Models.Stateless.Methods.IFace.GetInts(item.Faces); - normalizedPixelPercentageDistinctCount = normalizedPixelPercentageCollection.Distinct().Count(); - if (normalizedPixelPercentageDistinctCount != normalizedPixelPercentageCollection.Length) - check = true; - fileInfoCollection = directoryInfo.GetFiles($"{item.Property.Id.Value}*.json", SearchOption.TopDirectoryOnly); - if (fileInfoCollection.Length < normalizedPixelPercentageDistinctCount) - check = true; - if (!check && _Configuration.CheckJsonForDistanceResults) - { - for (int j = 0; j < fileInfoCollection.Length; j++) - { - json = Shared.Models.Stateless.Methods.IIndex.GetJson(fileInfoCollection[j].FullName, fileInfoCollection[j]); - if (!_Configuration.PropertiesChangedForDistance && Shared.Models.Stateless.Methods.IFace.GetFace(fileInfoCollection[j].FullName) is null) - { - check = true; - break; - } - } - if (!check && string.IsNullOrEmpty(json)) - check = true; - } - } - if (check) - continue; - if (_Configuration.PropertiesChangedForDistance) - check = true; - else if (hasPopulatedFace && !directoryInfo.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) - { - DateTime? updateToWhenMatches = dateTime; - List> subFileTuples = new(); - List<(DistanceHolder, FaceRecognitionDotNet.FaceEncoding?)> distanceHolders = GetDistanceHolder(filteredItems, directories); - SaveDistanceResults(updateDateWhenMatches, updateToWhenMatches, subFileTuples, distanceHolders); - } - _ = Shared.Models.Stateless.Methods.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(); - FaceRecognitionDotNet.FaceEncoding faceEncoding; - List faces; - List faceEncodings; - foreach ((string, List>) file in files) - { - faces = new(); - faceEncodings = new(); - foreach (KeyValuePair keyValuePair in file.Item2) - { - foreach (Face face in keyValuePair.Value) - { - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - 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, 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"); - _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(jsonFile, json, updateDateWhenMatches: true, compareBeforeWrite: true); - } - - private static Tuple Get(FaceRecognitionDotNet.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 NullReferenceException(nameof(_Log)); - string? relativePath; - Face face; - ParallelOptions parallelOptions = new(); - FaceRecognitionDotNet.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++) - { - _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++) - { - face = files[i].Item2[j].Value[k]; - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - continue; - faceAndFaceDistanceCollection = new(matches.Count); - relativePath = Path.GetDirectoryName(face.RelativePath); - if (string.IsNullOrEmpty(relativePath)) - continue; - if (face.FaceEncoding is null) - 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).ToList(); - Save(configuration, model, predictorModel, outputResolution, eDistanceCollectionDirectory, k, relativePath, face, faceAndFaceDistanceCollection); - } - } - } - } - } - - internal void SaveFaceNumbers(string eResultsFullGroupDirectory, Item item, Face face, List faceDistances) - { - if (item.Property?.Id is null) - throw new NullReferenceException(nameof(item.Property.Id)); - if (item.ImageFileHolder is null) - throw new NullReferenceException(nameof(item.ImageFileHolder)); - if (face.Location?.NormalizedPixelPercentage is null) - throw new NullReferenceException(nameof(face.Location.NormalizedPixelPercentage)); - if (string.IsNullOrEmpty(eResultsFullGroupDirectory)) - throw new NullReferenceException(nameof(eResultsFullGroupDirectory)); - string json; - List<(Face, string, string)> collection = new(); - string[] changesFrom = new string[] { nameof(A_Property), nameof(B_Metadata), nameof(C_Resize), nameof(D_Face) }; - string eSingletonFile = Path.Combine(eResultsFullGroupDirectory, "{}", Property.Models.Stateless.IResult.AllInOne, $"{item.Property.Id.Value}.{face.Location.NormalizedPixelPercentage.Value}{item.ImageFileHolder.ExtensionLowered}.json"); - FileInfo fileInfo = new(eSingletonFile); - json = JsonSerializer.Serialize(faceDistances, _WriteIndentedJsonSerializerOptions); - _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); - } - - internal void GetFaceDistances(string eResultsFullGroupDirectory, List faceEncodingRequired, List> subFileTuples, List parseExceptions, Item item, List faceCollection) - { - List? results; - if (item.Property?.Id is null) - throw new NullReferenceException(nameof(item.Property.Id)); - if (item.ImageFileHolder is null) - throw new NullReferenceException(nameof(item.ImageFileHolder)); - if (string.IsNullOrEmpty(eResultsFullGroupDirectory)) - throw new NullReferenceException(nameof(eResultsFullGroupDirectory)); - string json; - FileInfo fileInfo; - string eSingletonFile; - List<(Face, string, string)> collection = new(); - string[] changesFrom = new string[] { nameof(A_Property), nameof(B_Metadata), nameof(C_Resize), nameof(D_Face) }; - List dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList(); - foreach (Face face in faceCollection) - { - results = null; - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - continue; - eSingletonFile = Path.Combine(eResultsFullGroupDirectory, "{}", Property.Models.Stateless.IResult.AllInOne, $"{item.Property.Id.Value}.{face.Location.NormalizedPixelPercentage.Value}{item.ImageFileHolder.ExtensionLowered}.json"); - fileInfo = new(eSingletonFile); - if (_Configuration.PropertiesChangedForDistance) - results = null; - else if (!fileInfo.Exists) - results = null; - else if (dateTimes.Any() && dateTimes.Max() > fileInfo.LastWriteTime) - results = null; - else - { - json = File.ReadAllText(fileInfo.FullName); - try - { - results = JsonSerializer.Deserialize>(json); - if (results is null) - throw new NullReferenceException(nameof(results)); - subFileTuples.Add(new Tuple(nameof(E_Distance), fileInfo.LastWriteTime)); - lock (face) - face.SetFaceNumbers(results); - } - catch (Exception) - { - results = null; - parseExceptions.Add(nameof(E_Distance)); - } - } - if (results is null && !faceEncodingRequired.Any()) - faceEncodingRequired.Add(true); - } - } - - 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; - } - - private static int GetSelectedIndex(int maxDegreeOfParallelism, Random random, List faceEncodings) - { - int? result; - int selectedIndex; - List<(int? Index, double? Sum)> faceDistanceCollections = new(); - if (maxDegreeOfParallelism == 1) - { - double sum; - List faceDistances; - for (int i = 0; i < faceEncodings.Count; i++) - { - faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncodings[i]); - sum = faceDistances.Sum(); - faceDistanceCollections.Add(new(i, sum)); - } - } - else - { - for (int i = 0; i < faceEncodings.Count; i++) - faceDistanceCollections.Add(new(null, null)); - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; - _ = Parallel.For(0, faceEncodings.Count, parallelOptions, (i, state) => - { - List faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncodings[i]); - double sum = faceDistances.Sum(); - lock (faceDistanceCollections) - faceDistanceCollections[i] = new(i, sum); - }); - } - faceDistanceCollections = faceDistanceCollections.OrderBy(l => l.Sum).ToList(); - if (faceDistanceCollections.Count != faceEncodings.Count) - throw new Exception(); - if (faceDistanceCollections.Count > 1000) - selectedIndex = random.Next(0, 36); - else if (faceDistanceCollections.Count > 500) - selectedIndex = random.Next(0, 31); - else if (faceDistanceCollections.Count > 200) - selectedIndex = random.Next(0, 26); - else if (faceDistanceCollections.Count > 100) - selectedIndex = random.Next(0, 21); - else if (faceDistanceCollections.Count > 50) - selectedIndex = random.Next(0, 16); - else if (faceDistanceCollections.Count > 25) - selectedIndex = random.Next(0, 11); - else if (faceDistanceCollections.Count > 10) - selectedIndex = random.Next(0, 6); - else if (faceDistanceCollections.Count > 5) - selectedIndex = random.Next(0, 3); - else - selectedIndex = 0; - result = faceDistanceCollections[selectedIndex].Index; - if (result is null) - throw new NullReferenceException(nameof(result)); - return result.Value; - } - - private static void SetFiltered(List<(FaceRecognitionDotNet.FaceEncoding FaceEncoding, MappingContainer MappingContainer)> collection) - { - double ucl; - bool check; - double average; - double[] doubles; - double standardDeviation; - double?[] nullableDoubles; - for (int i = 0; i < int.MaxValue; i++) - { - check = true; - nullableDoubles = (from l in collection where l.MappingContainer.Mapping.Filtered is not null && !l.MappingContainer.Mapping.Filtered.Value select l.MappingContainer.Distance).ToArray(); - doubles = (from l in nullableDoubles where l.HasValue select l.Value).ToArray(); - if (doubles.Length < 4) - break; - average = doubles.Average(); - standardDeviation = GetStandardDeviation(doubles, average); - ucl = average + (standardDeviation * 3); - if (ucl > IClosest.Tolerance) - ucl = IClosest.Tolerance; - foreach ((FaceRecognitionDotNet.FaceEncoding _, MappingContainer mappingContainer) in collection) - { - if (mappingContainer.Mapping.Filtered is null || mappingContainer.Mapping.Filtered.Value || mappingContainer.Distance <= ucl) - continue; - if (check) - check = false; - mappingContainer.Mapping.SetFiltered(); - } - if (check) + sortingContainer = new(face, sorting); + results.Add(sortingContainer); + if (results.Count >= ISorting.MaximumPerFaceShouldBeHigh) break; } + return results; } - private static FaceDistance GetFaceDistanceParallelFor(Face face, FaceRecognitionDotNet.FaceEncoding faceEncoding, Mapping mapping, DateTime minimumDateTime, bool? isWrongYear, string key, FaceRecognitionDotNet.FaceEncoding[] faceEncodings) + private static SortingContainer[] GetSortingContainersThenSetFaceMappingSortingCollection(int maxDegreeOfParallelism, long ticks, MapLogic mapLogic, string outputResolution, FaceDistanceContainer[] faceDistanceContainers) { - FaceDistance result; - if (face.Location?.NormalizedPixelPercentage is null) - throw new NullReferenceException(nameof(face.Location.NormalizedPixelPercentage)); - List faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncoding); - result = new(faceDistances, isWrongYear, key, mapping, minimumDateTime); - return result; - } - - private static List GetFaceDistanceCollection(int maxDegreeOfParallelism, List<(string Key, int Id, Mapping Mapping, DateTime MinimumDateTime, bool? IsWrongYear, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>)> collection, Face face) - { - List results; - if (face.FaceEncoding is null) - throw new NullReferenceException(nameof(face.FaceEncoding)); - FaceRecognitionDotNet.FaceEncoding faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); - if (maxDegreeOfParallelism == 1) + SortingContainer[] results; + List collection = new(); + List faceDistanceEncodings = new(); + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; + string message = $") {faceDistanceContainers.Length:000} faceDistanceContainer(s) - {totalSeconds} total second(s) - {outputResolution}"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers) + { + if (faceDistanceContainer.FaceDistance.Encoding is null) + continue; + faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance); + } + 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; + List sortingCollection = GetSortingCollection(mapLogic, faceDistanceEncodings, faceDistanceContainers.Length, i, faceDistanceEncoding); + List sortingContainers = GetSortingContainers(face, faceDistanceEncoding, sortingCollection); + lock (collection) + collection.AddRange(sortingContainers); + lock (face) + face.ReleaseFaceDistance(); + }); + results = Shared.Models.Stateless.Methods.ISortingContainer.Sort(collection); + return results; + } + + private static FaceDistanceContainer[] GetFaceDistanceContainers(Face[] firstFilteredFaces) + { + FaceDistanceContainer[] results; + FaceDistance faceDistance; + FaceDistanceContainer faceDistanceContainer; + List collection = new(); + foreach (Face face in firstFilteredFaces) + { + if (face.Mapping is null) + throw new NotSupportedException(); + if (face.FaceDistance?.Encoding is not FaceRecognitionDotNet.FaceEncoding faceEncoding) + throw new NotSupportedException(); + faceDistance = new(face.Mapping.MappingFromLocation.Confidence, 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 = (from l in collection orderby l.FaceDistance.Encoding is not null select l).ToArray(); + if (results.Any() && results[0].FaceDistance.Encoding is null) + throw new Exception("Sorting failed!"); + return results; + } + + private static void SetFaceDistances(int maxDegreeOfParallelism, long ticks, string outputResolution, Face[] selectedFilteredFaces) + { + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; + string message = $") {selectedFilteredFaces.Length:000} Load Face Encoding - {totalSeconds} total second(s) - {outputResolution}"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = false }; + using ProgressBar progressBar = new(selectedFilteredFaces.Length, message, options); + _ = Parallel.For(0, selectedFilteredFaces.Length, parallelOptions, (i, state) => { - results = new(); FaceDistance faceDistance; - List faceDistances; - FaceRecognitionDotNet.FaceEncoding[] faceEncodings; - if (face.Location?.NormalizedPixelPercentage is null) - throw new NullReferenceException(nameof(face.Location.NormalizedPixelPercentage)); - foreach ((string key, int id, Mapping mapping, DateTime minimumDateTime, bool? isWrongYear, List<(FaceRecognitionDotNet.FaceEncoding FaceEncoding, MappingContainer _)> faceEncodingContainers) in collection) - { - faceEncodings = (from l in faceEncodingContainers select l.FaceEncoding).ToArray(); - faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncoding); - faceDistance = new(faceDistances, isWrongYear, key, mapping, minimumDateTime); - results.Add(faceDistance); - if (results.Count > IFaceDistance.MaximumPer) - break; - } - } - else - { - results = new(); - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; - _ = Parallel.For(0, collection.Count, parallelOptions, (i, state) => - { - (string key, int id, Mapping mapping, DateTime minimumDateTime, bool? isWrongYear, List<(FaceRecognitionDotNet.FaceEncoding FaceEncoding, MappingContainer _)> faceEncodingContainers) = collection[i]; - FaceRecognitionDotNet.FaceEncoding[] faceEncodings = (from l in faceEncodingContainers select l.FaceEncoding).ToArray(); - FaceDistance? closest = GetFaceDistanceParallelFor(face, faceEncoding, mapping, minimumDateTime, isWrongYear, key, faceEncodings); - if (closest is not null) - { - lock (results) - { - results.Add(closest); - if (results.Count > IFaceDistance.MaximumPer) - state.Break(); - } - } - }); - } - return results; - } - - private static FaceRecognitionDotNet.FaceEncoding? GetFaceEncoding(Face face) - { - FaceRecognitionDotNet.FaceEncoding? result; - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - result = null; - else - result = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); - return result; - } - - private static List GetFaceEncodingsOnly(int maxDegreeOfParallelism, List collection) - { - List results; - if (maxDegreeOfParallelism == 1) - { - results = new(); FaceRecognitionDotNet.FaceEncoding faceEncoding; - foreach (MappingContainer mappingContainer in collection) - { - if (mappingContainer.Face?.FaceEncoding is null || mappingContainer.Face.Location?.NormalizedPixelPercentage is null) - continue; - faceEncoding = FaceRecognition.LoadFaceEncoding(mappingContainer.Face.FaceEncoding.RawEncoding); - results.Add(faceEncoding); - } - } - else - { - results = new(); - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; - _ = Parallel.For(0, collection.Count, parallelOptions, (i, state) => - { - Face? face = collection[i].Face; - if (face is null) - throw new Exception(); - FaceRecognitionDotNet.FaceEncoding? faceEncoding = GetFaceEncoding(face); - if (faceEncoding is not null) - { - lock (results) - results.Add(faceEncoding); - } - }); - } + Face face = selectedFilteredFaces[i]; + if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) + throw new NotSupportedException(); + if (face.Mapping is null) + throw new NotSupportedException(); + faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); + faceDistance = new(face.Location.Confidence, faceEncoding, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromItem.MinimumDateTime, face.Location.NormalizedPixelPercentage.Value); + lock (face) + face.FaceDistanceAdd(faceDistance); + }); + } + + internal static SortingContainer[] SetPersonTicksAndSetFaceDistancesAndSetFaceMappingSortingCollectionThenGetSortingContainers(int maxDegreeOfParallelism, long ticks, MapLogic mapLogic, string outputResolution, string eDistanceContentFileName, List distinctFilteredFaces) + { + SortingContainer[] results; + Face[] orderedFilteredFaces = (from l in distinctFilteredFaces orderby l.Mapping is not null, l.Mapping?.MappingFromItem.MinimumDateTime descending select l).ToArray(); + mapLogic.SetPersonTicks(outputResolution, orderedFilteredFaces); + Face[] selectedFilteredFaces = orderedFilteredFaces.Skip(ISorting.FacesToSkipAfterSortBeforeLoad).Take(ISorting.FacesToTakeAfterSortBeforeLoad).ToArray(); + SetFaceDistances(maxDegreeOfParallelism, ticks, outputResolution, selectedFilteredFaces); + FaceDistanceContainer[] faceDistanceContainers = GetFaceDistanceContainers(selectedFilteredFaces); + results = GetSortingContainersThenSetFaceMappingSortingCollection(maxDegreeOfParallelism, ticks, mapLogic, outputResolution, faceDistanceContainers); + if (results.Length < IFaceDistance.Permyriad) + SaveFaceDistances(eDistanceContentFileName, results); return results; } - private static void SetNonFiltered(List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)> collection) - { - foreach ((FaceRecognitionDotNet.FaceEncoding _, MappingContainer mappingContainer) in collection) - { - if (mappingContainer.Mapping.Filtered is null) - mappingContainer.Mapping.SetFiltered(value: false); - } - } - - private Dictionary> GetThreeSigmaFaceEncodings(int maxDegreeOfParallelism, long ticks, Random random, Dictionary> keyValuePairs) - { - if (_Log is null) - throw new NullReferenceException(nameof(_Log)); - Dictionary> results = new(); - int totalSeconds; - int selectedIndex; - List faceDistances; - MappingContainer mappingContainer; - int keyValuePairsCount = keyValuePairs.Count; - FaceRecognitionDotNet.FaceEncoding faceEncoding; - List faceEncodings; - List<(FaceRecognitionDotNet.FaceEncoding FaceEncoding, MappingContainer MappingContainer)> collection; - foreach (KeyValuePair> keyValuePair in keyValuePairs) - { - collection = new(); - faceEncodings = GetFaceEncodingsOnly(maxDegreeOfParallelism, keyValuePair.Value); - for (int i = 0; i < faceEncodings.Count; i++) - { - faceEncoding = faceEncodings[i]; - mappingContainer = keyValuePair.Value[i]; - collection.Add(new(faceEncoding, mappingContainer)); - } - results.Add(keyValuePair.Key, collection); - if (faceEncodings.Count == 1) - selectedIndex = 0; - else - selectedIndex = GetSelectedIndex(maxDegreeOfParallelism, random, faceEncodings); - faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncodings[selectedIndex]); - for (int i = 0; i < faceEncodings.Count; i++) - collection[i].MappingContainer.SetDistance(faceDistances[i]); - if (collection.Count > 3) - SetFiltered(collection); - SetNonFiltered(collection); - totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - _Log.Information($"{keyValuePairsCount:0000}) {totalSeconds} total second(s) - {keyValuePair.Key} - {collection[selectedIndex].MappingContainer.Mapping.DisplayDirectoryName}"); - } - return results; - } - - internal Dictionary> ParallelWork(int maxDegreeOfParallelism, long ticks, Map.Models.MapLogic mapLogic, Item[] items, int totalNotMapped) - { - Dictionary> results; - Random random = new((int)ticks); - mapLogic.ForceSingleImage(ticks, items, totalNotMapped, random); - Dictionary> keyValuePairs = Map.Models.Stateless.IMapLogic.GetKeyValuePairs(items); - results = GetThreeSigmaFaceEncodings(maxDegreeOfParallelism, ticks, random, keyValuePairs); - return results; - } - - public static void AddToFaceDistance(int maxDegreeOfParallelism, Map.Models.MapLogic mapLogic, Item[] items, List<(string, int, Mapping, DateTime, bool?, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>)> collection) - { - Face face; - double deterministicHashCodeKey; - DateTime dateTime = DateTime.Now; - List faceDistances; - foreach (Item item in items) - { - if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) - continue; - for (int i = 0; i < item.Faces.Count; i++) - { - face = item.Faces[i]; - face.FaceDistances.Clear(); - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - continue; - if ((from l in item.Mapping where l.NormalizedPixelPercentage.HasValue && l.NormalizedPixelPercentage.Value == face.Location.NormalizedPixelPercentage.Value select true).Any()) - continue; - deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); - if (mapLogic.Skip(deterministicHashCodeKey)) - continue; - faceDistances = GetFaceDistanceCollection(maxDegreeOfParallelism, collection, face); - face.FaceDistances.AddRange(faceDistances); - } - } - } - } \ No newline at end of file diff --git a/Instance/appsettings.json b/Instance/appsettings.json index 41d5f9b..3858bd1 100644 --- a/Instance/appsettings.json +++ b/Instance/appsettings.json @@ -152,9 +152,6 @@ "1976-01-05_00", "1982-05-02_00" ], - "LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions": [ - "1920 x 1080" - ], "LoadOrCreateThenSaveDistanceResultsForOutputResolutions": [ "1920 x 1080" ], diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index 26341c0..5cd64fb 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -1,7 +1,8 @@ +using ShellProgressBar; using System.Text.Json; using View_by_Distance.Property.Models; using View_by_Distance.Shared.Models; -using View_by_Distance.Shared.Models.Stateless; +using View_by_Distance.Shared.Models.Stateless.Methods; using WindowsShortcutFactory; namespace View_by_Distance.Map.Models; @@ -14,10 +15,11 @@ public class MapLogic protected readonly Dictionary _KeyValuePairs; protected readonly Dictionary _IndicesFromNew; protected readonly Dictionary _SixCharacterNamedFaceInfo; - protected readonly Dictionary _DeterministicHashCodeKeyValuePairs; protected readonly Dictionary _DeterministicHashCodeUnknownFaceKeyValuePairs; protected readonly Dictionary _IncorrectDeterministicHashCodeKeyValuePairs; - protected readonly Dictionary _PeopleKeyValuePairs; + protected readonly Dictionary _PersonKeysRanges; + protected readonly Dictionary> _IdThenNormalizedPixelPercentageKeyValuePairs; + protected readonly Dictionary _PeopleKeyValuePairs; public Dictionary KeyValuePairs => _KeyValuePairs; public Dictionary IndicesFromNew => _IndicesFromNew; @@ -36,31 +38,32 @@ public class MapLogic public MapLogic(int maxDegreeOfParallelism, Configuration configuration, string resizeFilenameExtension, string facesFilenameExtension, string facesHiddenFilenameExtension, string facePartsFilenameExtension, long ticks, Person[] people, string peopleDateGroupDirectory, string zResultsFullGroupDirectory) { _Ticks = ticks; + _PersonKeysRanges = new(); _Configuration = configuration; _Log = Serilog.Log.ForContext(); _FacesFilenameExtension = facesFilenameExtension; _ResizeFilenameExtension = resizeFilenameExtension; _FacePartsFilenameExtension = facePartsFilenameExtension; _FacesHiddenFilenameExtension = facesHiddenFilenameExtension; - Dictionary? deterministicHashCodeUnknownFaceKeyValuePairs; if (configuration.VerifyToSeason is null || !configuration.VerifyToSeason.Any()) throw new Exception(); string json; string[] files; string fullPath; + const int zero = 0; List notMappedTicks = new(); List skipCollection = new(); Dictionary? keyValuePairs; List>? collection; Dictionary indicesFromNew = new(); - ; Dictionary? sixCharacterNamedFaceInfo; - Dictionary deterministicHashCodeKeyValuePairs = new(); string? rootDirectoryParent = Path.GetDirectoryName(configuration.RootDirectory); + Dictionary? deterministicHashCodeUnknownFaceKeyValuePairs; Dictionary peopleKeyValuePairs = new(); string zPropertyHolderContentDirectory = Path.Combine(zResultsFullGroupDirectory, "()"); Dictionary incorrectDeterministicHashCodeKeyValuePairs = new(); string zPropertyHolderSingletonDirectory = Path.Combine(zResultsFullGroupDirectory, "{}"); + Dictionary> idThenNormalizedPixelPercentageKeyValuePairs = new(); string zPropertyHolderContentTicksDirectory = Path.Combine(zPropertyHolderContentDirectory, $"({ticks})"); string zPropertyHolderPeopleContentDirectory = Path.Combine(peopleDateGroupDirectory, "()", "(KnownPeople)"); string zPropertyHolderSingletonTicksDirectory = Path.Combine(zPropertyHolderSingletonDirectory, string.Concat('{', _Ticks, '}')); @@ -75,10 +78,10 @@ public class MapLogic deterministicHashCodeUnknownFaceKeyValuePairs = new(); else { - json = File.ReadAllText(files[0]); + json = File.ReadAllText(files[zero]); deterministicHashCodeUnknownFaceKeyValuePairs = GetKeyValuePairs(json); } - Stateless.ByDeterministicHashCode.SetByRef(_ResizeFilenameExtension, people, skipCollection, peopleKeyValuePairs, notMappedTicks, deterministicHashCodeUnknownFaceKeyValuePairs, deterministicHashCodeKeyValuePairs, incorrectDeterministicHashCodeKeyValuePairs, zPropertyHolderContentDirectory, zPropertyHolderPeopleContentDirectory); + Stateless.ByDeterministicHashCode.SetByRef(maxDegreeOfParallelism, ticks, _ResizeFilenameExtension, people, skipCollection, peopleKeyValuePairs, notMappedTicks, deterministicHashCodeUnknownFaceKeyValuePairs, idThenNormalizedPixelPercentageKeyValuePairs, incorrectDeterministicHashCodeKeyValuePairs, zPropertyHolderContentDirectory, zPropertyHolderPeopleContentDirectory); if (!Directory.Exists(zPropertyHolderContentTicksDirectory)) _ = Directory.CreateDirectory(zPropertyHolderContentTicksDirectory); if (!deterministicHashCodeUnknownFaceKeyValuePairs.Any()) @@ -90,7 +93,7 @@ public class MapLogic sixCharacterNamedFaceInfo = new(); else { - json = File.ReadAllText(files[0]); + json = File.ReadAllText(files[zero]); sixCharacterNamedFaceInfo = JsonSerializer.Deserialize>(json); if (sixCharacterNamedFaceInfo is null) throw new NullReferenceException(nameof(sixCharacterNamedFaceInfo)); @@ -101,7 +104,7 @@ public class MapLogic keyValuePairs = new(); else { - json = File.ReadAllText(files[0]); + json = File.ReadAllText(files[zero]); keyValuePairs = JsonSerializer.Deserialize>(json); if (keyValuePairs is null) throw new NullReferenceException(nameof(keyValuePairs)); @@ -131,10 +134,10 @@ public class MapLogic _PeopleKeyValuePairs = peopleKeyValuePairs; _SixCharacterNamedFaceInfo = sixCharacterNamedFaceInfo; _ZPropertyHolderContentDirectory = zPropertyHolderContentDirectory; - _DeterministicHashCodeKeyValuePairs = deterministicHashCodeKeyValuePairs; _ZPropertyHolderContentTicksDirectory = zPropertyHolderContentTicksDirectory; _ZPropertyHolderSingletonTicksDirectory = zPropertyHolderSingletonTicksDirectory; _IncorrectDeterministicHashCodeKeyValuePairs = incorrectDeterministicHashCodeKeyValuePairs; + _IdThenNormalizedPixelPercentageKeyValuePairs = idThenNormalizedPixelPercentageKeyValuePairs; _DeterministicHashCodeUnknownFaceKeyValuePairs = deterministicHashCodeUnknownFaceKeyValuePairs; } @@ -142,33 +145,82 @@ public class MapLogic this(maxDegreeOfParallelism, configuration, outputExtension, outputExtension, outputExtension, outputExtension, ticks, people, peopleDateGroupDirectory, zResultsFullGroupDirectory) { } + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + public bool Skip(double deterministicHashCodeKey) => _SkipCollection.Contains(deterministicHashCodeKey); - private static Dictionary GetKeyValuePairs(string json) + private long LogDelta(long ticks, string? methodName) { - Dictionary results = new(); - PersonBirthday? personBirthday; - List personBirthdays; - Dictionary? keyValuePairs = JsonSerializer.Deserialize>(json); - if (keyValuePairs is null) - throw new NullReferenceException(nameof(keyValuePairs)); - foreach (KeyValuePair keyValuePair in keyValuePairs) + long result; + if (_Log is null) + throw new NullReferenceException(nameof(_Log)); + double delta = new TimeSpan(DateTime.Now.Ticks - ticks).TotalMilliseconds; + _Log.Debug($"{methodName} took {Math.Floor(delta)} millisecond(s)"); + result = DateTime.Now.Ticks; + return result; + } + + private List<(long, long, long, long)> GetPersonKeysRangesCollection(PersonBirthday[] personBirthdays) + { + List<(long, long, long, long)> results = new(); + long personKey; + foreach (PersonBirthday personBirthday in personBirthdays) { - personBirthdays = new(); - foreach (string personKey in keyValuePair.Value) - { - personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKey); - if (personBirthday is null) - continue; - } - if (!personBirthdays.Any()) + personKey = personBirthday.Value.Ticks; + if (!_PersonKeysRanges.ContainsKey(personKey)) continue; - results.Add(keyValuePair.Key, personBirthdays.ToArray()); + results.Add(_PersonKeysRanges[personKey]); } return results; } - public void SaveShortcuts(string[] juliePhares, List items) + public List GetSortingCollection(int i, FaceDistance faceDistanceEncoding, List faceDistanceLengths, bool anyLowerThanTolerance) + { + List results = new(); + Sorting sorting; + FaceDistance faceDistanceLength; + Dictionary keyValuePairs; + List<(long lcl, long minimum, long maximum, long ucl)> personKeysRangesCollection; + for (int j = 0; j < faceDistanceLengths.Count; j++) + { + if (j == i) + continue; + faceDistanceLength = faceDistanceLengths[j]; + if (faceDistanceEncoding.NormalizedPixelPercentage is null || faceDistanceLength.NormalizedPixelPercentage is null || faceDistanceLength.Length is null) + throw new NotSupportedException(); + if (faceDistanceLength.Length == 0) + continue; + if (_IdThenNormalizedPixelPercentageKeyValuePairs.ContainsKey(faceDistanceEncoding.Id)) + { + keyValuePairs = _IdThenNormalizedPixelPercentageKeyValuePairs[faceDistanceEncoding.Id]; + if (keyValuePairs.ContainsKey(faceDistanceEncoding.NormalizedPixelPercentage.Value)) + continue; + } + if (!_IdThenNormalizedPixelPercentageKeyValuePairs.ContainsKey(faceDistanceLength.Id)) + continue; + keyValuePairs = _IdThenNormalizedPixelPercentageKeyValuePairs[faceDistanceLength.Id]; + if (!keyValuePairs.ContainsKey(faceDistanceLength.NormalizedPixelPercentage.Value)) + continue; + personKeysRangesCollection = GetPersonKeysRangesCollection(keyValuePairs[faceDistanceLength.NormalizedPixelPercentage.Value]); + sorting = ISorting.Get(faceDistanceEncoding, faceDistanceLength, anyLowerThanTolerance, personKeysRangesCollection); + if (sorting.DistancePermyriad == 0) + continue; + if (sorting.Id == faceDistanceEncoding.Id) + { + if (sorting.NormalizedPixelPercentage == faceDistanceEncoding.NormalizedPixelPercentage.Value) + throw new NotSupportedException(); + continue; + } + results.Add(sorting); + } + return results; + } + + public void SaveShortcuts(string[] juliePhares, Item[] filteredItems) { string fileName; string fullName; @@ -176,33 +228,33 @@ public class MapLogic string personKeyFormatted; PersonBirthday personBirthday; WindowsShortcut windowsShortcut; - (string DisplayDirectoryName, int? ApproximateYears, PersonBirthday[] _, long Ticks) person; - List<(Item, (long?, Face?, (string, string, string, string))[])> collections = GetCollection(items); - foreach ((Item item, (long? ticks, Face? _, (string, string, string, string))[] collection) in collections) + (string DisplayDirectoryName, int? ApproximateYears, PersonBirthday[] PersonBirthdays, long PersonKey) person; + List<(Item, (long?, Face?, (string, string, string, string))[])> collections = GetCollection(filteredItems); + foreach ((Item item, (long? personKey, Face? _, (string, string, string, string))[] collection) in collections) { if (collection.Length != 1) continue; - foreach ((long? ticks, Face? _, (string directory, string copyDirectory, string copyFileName, string shortcutFileName)) in collection) + foreach ((long? personKey, Face? _, (string directory, string copyDirectory, string copyFileName, string shortcutFileName)) in collection) { - if (ticks is null) + if (personKey is null) continue; if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) continue; - minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); + minimumDateTime = IProperty.GetMinimumDateTime(item.Property); if (!Directory.Exists(directory)) { _ = Directory.CreateDirectory(directory); - if (ticks is not null && _PeopleKeyValuePairs.ContainsKey(ticks.Value)) + if (personKey is not null && _PeopleKeyValuePairs.ContainsKey(personKey.Value)) { - person = _PeopleKeyValuePairs[ticks.Value]; + person = _PeopleKeyValuePairs[personKey.Value]; fullName = string.Concat(person.DisplayDirectoryName, ".txt"); File.WriteAllText(Path.Combine(directory, fullName), string.Empty); } } - if (ticks is null) + if (personKey is null) continue; - personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(ticks.Value); - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); + personBirthday = IPersonBirthday.GetPersonBirthday(personKey.Value); + personKeyFormatted = IPersonBirthday.GetFormatted(personBirthday); if (juliePhares.Contains(personKeyFormatted) && !string.IsNullOrEmpty(copyDirectory)) { if (!Directory.Exists(copyDirectory)) @@ -224,125 +276,148 @@ public class MapLogic } } - public void UseKeyValuePairsSaveFaceEncoding(Item[] items) + private void UseKeyValuePairsSaveFaceEncoding(int maxDegreeOfParallelism, SortingContainer[] sortingContainers) { + Dictionary personKeyAliases = new(); Dictionary> keyValuePairs = new(); - List<(PersonBirthday PersonBirthday, double IdAndNormalizedPixelPercentage)> deterministicHashCodeCollection = new(); + List<(PersonBirthday, int, int)> idThenNormalizedPixelPercentageCollection = new(); List<(PersonBirthday PersonBirthday, double IdAndNormalizedPixelPercentage)> incorrectDeterministicHashCodeCollection = new(); - foreach (Item item in items) + foreach (SortingContainer sortingContainer in sortingContainers) { - if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) - continue; - if (!item.Faces.Any()) - continue; - if (keyValuePairs.ContainsKey(item.Property.Id.Value)) + if (sortingContainer.Face.FaceEncoding is null || sortingContainer.Face.Location?.NormalizedPixelPercentage is null) + throw new NotSupportedException(); + if (sortingContainer.Face.Mapping is null) + throw new NotSupportedException(); + if (!keyValuePairs.ContainsKey(sortingContainer.Face.Mapping.MappingFromItem.Id)) + keyValuePairs.Add(sortingContainer.Face.Mapping.MappingFromItem.Id, new()); + keyValuePairs[sortingContainer.Face.Mapping.MappingFromItem.Id].Add(sortingContainer.Face); + } + Stateless.ByDeterministicHashCode.SetCollections(maxDegreeOfParallelism, _Ticks, _ZPropertyHolderContentDirectory, personKeyAliases, idThenNormalizedPixelPercentageCollection, incorrectDeterministicHashCodeCollection, keyValuePairs); + } + + private static string GetMappingSegmentB(long ticks, PersonBirthday personBirthday, int? approximateYears, long minimumDateTimeTicks, bool? isWrongYear) + { + int years; + string result; + TimeSpan? timeSpan = IPersonBirthday.GetTimeSpan(minimumDateTimeTicks, isWrongYear, personBirthday); + if (timeSpan.HasValue && timeSpan.Value.Ticks < 0) + result = "!---"; + else if (timeSpan.HasValue) + { + (years, _) = IPersonBirthday.GetAge(minimumDateTimeTicks, personBirthday); + result = $"^{years:000}"; + } + else if (approximateYears.HasValue) + { + DateTime dateTime = new(ticks); + (years, _) = IAge.GetAge(minimumDateTimeTicks, dateTime.AddYears(-approximateYears.Value)); + result = $"~{years:000}"; + } + else + { + string isWrongYearFlag = IItem.GetWrongYearFlag(isWrongYear); + result = $"{isWrongYearFlag}{new DateTime(minimumDateTimeTicks):yyyy}"; + } + return result; + } + + private static string GetMappingSegmentB(long ticks, PersonBirthday personBirthday, int? approximateYears, DateTime minimumDateTime, bool? isWrongYear) + { + string result = GetMappingSegmentB(ticks, personBirthday, approximateYears, minimumDateTime.Ticks, isWrongYear); + return result; + } + + private static string GetMappingSegmentB(long ticks, PersonBirthday personBirthday, int? approximateYears, MappingFromItem mappingFromItem) + { + string result = GetMappingSegmentB(ticks, personBirthday, approximateYears, mappingFromItem.MinimumDateTime, mappingFromItem.IsWrongYear); + return result; + } + + private static Dictionary GetKeyValuePairs(string json) + { + Dictionary results = new(); + PersonBirthday? personBirthday; + List personBirthdays; + Dictionary? keyValuePairs = JsonSerializer.Deserialize>(json); + if (keyValuePairs is null) + throw new NullReferenceException(nameof(keyValuePairs)); + foreach (KeyValuePair keyValuePair in keyValuePairs) + { + personBirthdays = new(); + foreach (string personKey in keyValuePair.Value) { - if (keyValuePairs[item.Property.Id.Value].Count != item.Faces.Count) - throw new Exception(); - continue; + personBirthday = IPersonBirthday.GetPersonBirthday(personKey); + if (personBirthday is null) + continue; } - keyValuePairs.Add(item.Property.Id.Value, item.Faces); - } - Stateless.ByDeterministicHashCode.SetKeyValuePairs(_ZPropertyHolderContentDirectory, deterministicHashCodeCollection, incorrectDeterministicHashCodeCollection, keyValuePairs); - } - - public override string ToString() - { - string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); - return result; - } - - private long LogDelta(long ticks, string? methodName) - { - long result; - if (_Log is null) - throw new NullReferenceException(nameof(_Log)); - double delta = new TimeSpan(DateTime.Now.Ticks - ticks).TotalMilliseconds; - _Log.Debug($"{methodName} took {Math.Floor(delta)} millisecond(s)"); - result = DateTime.Now.Ticks; - return result; - } - - public static void AppendToItems(List items, List<(int, Item)> idAndItemCollection) - { - foreach (Item item in items) - { - if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) + if (!personBirthdays.Any()) continue; - idAndItemCollection.Add(new(item.Property.Id.Value, item)); + results.Add(keyValuePair.Key, personBirthdays.ToArray()); } + return results; } - public int AddToMapping(long ticks, List items) + private int AddToMapping(List distinctFilteredFaces) { + int by; int result = 0; long personKey; - Mapping mapping; - bool? isWrongYear; - bool forced = false; + const int zero = 0; int? approximateYears; string mappingSegmentB; - DateTime minimumDateTime; string personKeyFormatted; string displayDirectoryName; PersonBirthday personBirthday; - double deterministicHashCodeKey; List personBirthdays = new(); - (string DisplayDirectoryName, int? ApproximateYears, PersonBirthday[] PersonBirthdays, long Ticks) person; - foreach (Item item in items) + Dictionary keyValuePairs; + (string DisplayDirectoryName, int? ApproximateYears, PersonBirthday[] PersonBirthdays, long PersonKey) person; + foreach (Face face in distinctFilteredFaces) { - if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) - continue; - foreach (Face face in item.Faces) + by = 2; + personBirthdays.Clear(); + if (face.Mapping is null) + throw new NotSupportedException(); + if (face.Mapping is null) + throw new NotSupportedException(); + if (!_IdThenNormalizedPixelPercentageKeyValuePairs.ContainsKey(face.Mapping.MappingFromItem.Id)) + result += 1; + else { - personBirthdays.Clear(); - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - continue; - deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); - if (!_DeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey)) - { + keyValuePairs = _IdThenNormalizedPixelPercentageKeyValuePairs[face.Mapping.MappingFromItem.Id]; + if (!keyValuePairs.ContainsKey(face.Mapping.MappingFromLocation.NormalizedPixelPercentage)) result += 1; - continue; - } - personBirthdays.AddRange(_DeterministicHashCodeKeyValuePairs[deterministicHashCodeKey]); - for (int i = 0; i < personBirthdays.Count; i++) - { - personKey = personBirthdays[i].Value.Ticks; - if (!_PeopleKeyValuePairs.ContainsKey(personKey)) - continue; - person = _PeopleKeyValuePairs[personKey]; - personBirthday = person.PersonBirthdays[0]; - approximateYears = person.ApproximateYears; - displayDirectoryName = person.DisplayDirectoryName; - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); - minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); - mappingSegmentB = Stateless.MapLogic.GetMappingSegmentB(ticks, personBirthday, approximateYears, minimumDateTime, isWrongYear); - mapping = new(approximateYears, displayDirectoryName, forced, face.Location.NormalizedPixelPercentage, personBirthday, mappingSegmentB); - item.Mapping.Add(mapping); - } + else + personBirthdays.AddRange(keyValuePairs[face.Mapping.MappingFromLocation.NormalizedPixelPercentage]); } - if (IMapping.UseDeterministicHashCodeUnknownFaceKeyValuePairsForAddToMapping && !personBirthdays.Any()) + for (int i = 0; i < personBirthdays.Count; i++) { - if (!_DeterministicHashCodeUnknownFaceKeyValuePairs.ContainsKey(item.Property.Id.Value)) + personKey = personBirthdays[i].Value.Ticks; + if (!_PeopleKeyValuePairs.ContainsKey(personKey)) continue; - personBirthdays.AddRange(_DeterministicHashCodeUnknownFaceKeyValuePairs[item.Property.Id.Value]); - for (int i = 0; i < personBirthdays.Count; i++) - { - personKey = personBirthdays[i].Value.Ticks; - if (!_PeopleKeyValuePairs.ContainsKey(personKey)) - continue; - person = _PeopleKeyValuePairs[personKey]; - personBirthday = person.PersonBirthdays[0]; - approximateYears = person.ApproximateYears; - displayDirectoryName = person.DisplayDirectoryName; - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); - minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); - mappingSegmentB = Stateless.MapLogic.GetMappingSegmentB(ticks, personBirthday, approximateYears, minimumDateTime, isWrongYear); - mapping = new(approximateYears, displayDirectoryName, forced, personBirthday, mappingSegmentB); - item.Mapping.Add(mapping); - } + person = _PeopleKeyValuePairs[personKey]; + approximateYears = person.ApproximateYears; + personBirthday = person.PersonBirthdays[zero]; + displayDirectoryName = person.DisplayDirectoryName; + personKeyFormatted = IPersonBirthday.GetFormatted(personBirthday); + mappingSegmentB = GetMappingSegmentB(_Ticks, personBirthday, approximateYears, face.Mapping.MappingFromItem); + face.Mapping.UpdateMappingFromPerson(approximateYears, by, displayDirectoryName, personBirthday, mappingSegmentB); + } + if (!Shared.Models.Stateless.IMapping.UseDeterministicHashCodeUnknownFaceKeyValuePairsForAddToMapping || !_DeterministicHashCodeUnknownFaceKeyValuePairs.ContainsKey(face.Mapping.MappingFromItem.Id)) + continue; + by = 1; + personBirthdays.AddRange(_DeterministicHashCodeUnknownFaceKeyValuePairs[face.Mapping.MappingFromItem.Id]); + for (int i = 0; i < personBirthdays.Count; i++) + { + personKey = personBirthdays[i].Value.Ticks; + if (!_PeopleKeyValuePairs.ContainsKey(personKey)) + continue; + person = _PeopleKeyValuePairs[personKey]; + approximateYears = person.ApproximateYears; + personBirthday = person.PersonBirthdays[zero]; + displayDirectoryName = person.DisplayDirectoryName; + personKeyFormatted = IPersonBirthday.GetFormatted(personBirthday); + mappingSegmentB = GetMappingSegmentB(_Ticks, personBirthday, approximateYears, face.Mapping.MappingFromItem); + face.Mapping.UpdateMappingFromPerson(approximateYears, by, displayDirectoryName, personBirthday, mappingSegmentB); } } return result; @@ -352,6 +427,9 @@ public class MapLogic { WindowsShortcut windowsShortcut; string[] directories = (from l in saveContainers select l.Directory).Distinct().ToArray(); + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - _Ticks).TotalSeconds); + string message = $") {saveContainers.Count:000} save(s) - {totalSeconds} total second(s)"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = false }; foreach (string directory in directories) { if (string.IsNullOrEmpty(directory)) @@ -359,8 +437,10 @@ public class MapLogic if (!Directory.Exists(directory)) _ = Directory.CreateDirectory(directory); } + using ProgressBar progressBar = new(saveContainers.Count, message, options); foreach (SaveContainer saveContainer in saveContainers) { + progressBar.Tick(); if (string.IsNullOrEmpty(saveContainer.Directory) || string.IsNullOrEmpty(saveContainer.CheckFile) || saveContainer.ResizedFileHolder is null || saveContainer.FaceFileHolder is null || !string.IsNullOrEmpty(saveContainer.Json)) continue; if (File.Exists(saveContainer.CheckFile)) @@ -380,7 +460,7 @@ public class MapLogic continue; if (File.Exists(saveContainer.CheckFile)) continue; - _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(saveContainer.CheckFile, saveContainer.Json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + _ = IPath.WriteAllText(saveContainer.CheckFile, saveContainer.Json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); } foreach (SaveContainer saveContainer in saveContainers) { @@ -399,18 +479,18 @@ public class MapLogic } } - public void SaveNotMappedTicks() + private void SaveNotMappedTicks() { string directory; string personKeyFormatted; SaveContainer saveContainer; PersonBirthday personBirthday; List saveContainers = new(); - const string facePopulatedKey = nameof(Closest); + const string facePopulatedKey = nameof(Sorting); foreach (long personKey in _NotMappedPersonKeys) { - personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKey); - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); + personBirthday = IPersonBirthday.GetPersonBirthday(personKey); + personKeyFormatted = IPersonBirthday.GetFormatted(personBirthday); directory = Path.Combine(_ZPropertyHolderContentTicksDirectory, $"{facePopulatedKey}NotMapped", personKeyFormatted, Property.Models.Stateless.IResult.AllInOne); saveContainer = new(directory); saveContainers.Add(saveContainer); @@ -418,7 +498,7 @@ public class MapLogic SaveContainers(saveContainers); } - public List<(Item, (long?, Face?, (string, string, string, string))[])> GetCollection(List items) + public List<(Item, (long?, Face?, (string, string, string, string))[])> GetCollection(Item[] filteredItems) { List<(Item, (long?, Face?, (string, string, string, string))[])> results = new(); int years; @@ -443,12 +523,12 @@ public class MapLogic PersonBirthday[] personBirthdays; DateTime dateTime = DateTime.Now; List<(long?, Face?, (string, string, string, string))> collection; - for (int i = 0; i < items.Count; i++) + for (int i = 0; i < filteredItems.Length; i++) { indices.Clear(); copyFileName = string.Empty; copyDirectory = string.Empty; - item = items[i]; + item = filteredItems[i]; if (item.ImageFileHolder is null) continue; relativePath = Path.GetDirectoryName($"C:{item.RelativePath}"); @@ -467,9 +547,9 @@ public class MapLogic { faceCollection = item.Faces; personBirthdays = _DeterministicHashCodeUnknownFaceKeyValuePairs[item.Property.Id.Value]; - minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); + minimumDateTime = IProperty.GetMinimumDateTime(item.Property); (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); - isWrongYearFlag = Shared.Models.Stateless.Methods.IItem.GetWrongYearFlag(isWrongYear); + isWrongYearFlag = IItem.GetWrongYearFlag(isWrongYear); subDirectoryName = $"{isWrongYearFlag}{minimumDateTime:yyyy}"; if (!faceCollection.Any()) { @@ -491,19 +571,19 @@ public class MapLogic indices.Add(zero); personBirthday = personBirthdays[zero]; personKey = personBirthday.Value.Ticks; - timeSpan = Shared.Models.Stateless.Methods.IPersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, personBirthday); + timeSpan = IPersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, personBirthday); if (timeSpan.HasValue) { if (timeSpan.Value.Ticks < 0) subDirectoryName = "!---"; else { - (years, _) = Shared.Models.Stateless.Methods.IPersonBirthday.GetAge(dateTime, personBirthday); + (years, _) = IPersonBirthday.GetAge(dateTime, personBirthday); subDirectoryName = $"^{years:000}"; } } face = faceCollection[zero]; - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); + personKeyFormatted = IPersonBirthday.GetFormatted(personBirthday); directory = Path.Combine(_ZPropertyHolderContentTicksDirectory, "Shortcuts", personKeyFormatted, subDirectoryName); if (face.FaceEncoding is not null && face.Location?.NormalizedPixelPercentage is not null) copyDirectory = Path.Combine(_ZPropertyHolderContentTicksDirectory, "Images", personKeyFormatted, subDirectoryName); @@ -525,397 +605,6 @@ public class MapLogic return results; } - public void AddToClosest(int maxDegreeOfParallelism, Item[] items) - { - string key; - Closest closest; - DateTime minimumDateTime; - string personKeyFormatted; - Closest[] closestCollection; - double deterministicHashCodeKey; - Dictionary keyValuePairs = new(); - foreach (Item item in items) - { - if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) - continue; - foreach (Face face in item.Faces) - { - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - continue; - deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); - if (_DeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey)) - continue; - minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - closestCollection = Shared.Models.Stateless.Methods.IClosest.GetCollection(face, minimumDateTime, face.FaceDistances); - face.FaceDistances.Clear(); - for (int j = 0; j < closestCollection.Length; j++) - { - closest = closestCollection[j]; - if (_IncorrectDeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey) && _IncorrectDeterministicHashCodeKeyValuePairs[deterministicHashCodeKey].Contains(closest.Mapping.PersonBirthday)) - continue; - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(closest.Mapping.PersonBirthday); - key = string.Concat(personKeyFormatted, closest.Mapping.SegmentB); - if (!keyValuePairs.ContainsKey(key)) - keyValuePairs.Add(key, 0); - else if (keyValuePairs[key] > IClosest.MaximumPer) - continue; - keyValuePairs[key] += 1; - item.Closest.Add(closest); - break; - } - } - } - } - - private List GetMappingSaveContainers(Item[] items, string dFacesContentDirectory, string d2ResultsFullGroupDirectory) - { - List results = new(); - string json; - Mapping? match; - string checkFile; - string directory; - string shortcutFile; - string? directoryName; - string facesDirectory; - string personDirectory; - List used = new(); - string personKeyFormatted; - FileHolder faceFileHolder; - string facePartsDirectory; - SaveContainer saveContainer; - FileHolder facePartsFileHolder; - FileHolder hiddenFaceFileHolder; - double deterministicHashCodeKey; - const string facePopulatedKey = nameof(Mapping); - bool deterministicHashCodeKeyValuePairsAny = _DeterministicHashCodeKeyValuePairs.Any(); - foreach (Item item in items) - { - used.Clear(); - if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) - continue; - directoryName = Path.GetDirectoryName(item.RelativePath); - if (directoryName is null) - throw new Exception(); - foreach (Face face in item.Faces) - { - match = null; - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - continue; - foreach (Mapping mapping in item.Mapping) - { - if (mapping.NormalizedPixelPercentage is null || mapping.NormalizedPixelPercentage.Value != face.Location.NormalizedPixelPercentage.Value) - continue; - match = mapping; - break; - } - if (match is null) - continue; - foreach (Closest closest in item.Closest) - { - if (closest.Mapping.Forced) - continue; - if (closest.NormalizedPixelPercentage != face.Location.NormalizedPixelPercentage.Value) - continue; - throw new Exception(); - } - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(match.PersonBirthday); - if (match.Filtered is null) - directory = Path.Combine(_ZPropertyHolderContentTicksDirectory, $"{facePopulatedKey}Null", personKeyFormatted, match.SegmentB); - else if (match.Filtered.Value) - directory = Path.Combine(_ZPropertyHolderContentTicksDirectory, $"{facePopulatedKey}OutOfControl", personKeyFormatted, match.SegmentB); - else - continue; - personDirectory = Path.Combine(directory, match.DisplayDirectoryName[..1], "lnk"); - saveContainer = new(personDirectory); - results.Add(saveContainer); - facesDirectory = string.Concat(dFacesContentDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); - facePartsDirectory = string.Concat(d2ResultsFullGroupDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); - deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); - checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}"); - faceFileHolder = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacesFilenameExtension}")); - hiddenFaceFileHolder = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacesHiddenFilenameExtension}")); - facePartsFileHolder = new(Path.Combine(facePartsDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacePartsFilenameExtension}")); - if (string.IsNullOrEmpty(personDirectory)) - shortcutFile = string.Empty; - else - shortcutFile = Path.Combine(personDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.lnk"); - saveContainer = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, string.Empty, facePartsFileHolder, item.ResizedFileHolder, shortcutFile); - results.Add(saveContainer); - if (!string.IsNullOrEmpty(checkFile) && IMapping.SaveFaceEncoding) - { - checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.json"); - json = JsonSerializer.Serialize(face.FaceEncoding); - saveContainer = new(checkFile, directory, json); - results.Add(saveContainer); - } - personDirectory = Path.Combine(directory, match.DisplayDirectoryName[..1], "lnk", match.DisplayDirectoryName); - saveContainer = new(personDirectory); - results.Add(saveContainer); - used.Add(face.Location.NormalizedPixelPercentage.Value); - } - if (deterministicHashCodeKeyValuePairsAny && IMapping.UseDeterministicHashCodeUnknownFaceKeyValuePairsForSaveMapping) - { - foreach (Face face in item.Faces) - { - match = null; - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - continue; - if (used.Contains(face.Location.NormalizedPixelPercentage.Value)) - continue; - // if (item.Faces.Count != 1 || item.Mapping.Count != 1) - // continue; - foreach (Mapping mapping in item.Mapping) - { - if (mapping.NormalizedPixelPercentage is not null) - continue; - match = mapping; - break; - } - if (match is null) - continue; - foreach (Closest closest in item.Closest) - { - if (closest.Mapping.Forced) - continue; - if (closest.NormalizedPixelPercentage != face.Location.NormalizedPixelPercentage.Value) - continue; - throw new Exception(); - } - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(match.PersonBirthday); - if (match.Filtered is null) - directory = Path.Combine(_ZPropertyHolderContentTicksDirectory, $"{facePopulatedKey}WithButNull", personKeyFormatted, match.SegmentB); - else if (!match.Filtered.Value) - directory = Path.Combine(_ZPropertyHolderContentTicksDirectory, $"{facePopulatedKey}WithButOutOfControl", personKeyFormatted, match.SegmentB); - else - continue; - personDirectory = Path.Combine(directory, match.DisplayDirectoryName[..1], "lnk"); - saveContainer = new(personDirectory); - results.Add(saveContainer); - facesDirectory = string.Concat(dFacesContentDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); - facePartsDirectory = string.Concat(d2ResultsFullGroupDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); - deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); - checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}"); - faceFileHolder = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacesFilenameExtension}")); - hiddenFaceFileHolder = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacesHiddenFilenameExtension}")); - facePartsFileHolder = new(Path.Combine(facePartsDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacePartsFilenameExtension}")); - if (string.IsNullOrEmpty(personDirectory)) - shortcutFile = string.Empty; - else - shortcutFile = Path.Combine(personDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.lnk"); - saveContainer = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, string.Empty, facePartsFileHolder, item.ResizedFileHolder, shortcutFile); - results.Add(saveContainer); - if (!string.IsNullOrEmpty(checkFile) && IMapping.SaveFaceEncoding) - { - checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.json"); - json = JsonSerializer.Serialize(face.FaceEncoding); - saveContainer = new(checkFile, directory, json); - results.Add(saveContainer); - } - personDirectory = Path.Combine(directory, match.DisplayDirectoryName[..1], "lnk", match.DisplayDirectoryName); - saveContainer = new(personDirectory); - results.Add(saveContainer); - used.Add(face.Location.NormalizedPixelPercentage.Value); - } - } - } - return results; - } - - private List GetClosestSaveContainers(Item[] items, string dFacesContentDirectory, string d2ResultsFullGroupDirectory) - { - List results = new(); - Closest? match; - string checkFile; - string directory; - string shortcutFile; - string? directoryName; - string facesDirectory; - string personDirectory; - List used = new(); - FileHolder faceFileHolder; - string personKeyFormatted; - string facePartsDirectory; - SaveContainer saveContainer; - PersonBirthday personBirthday; - FileHolder facePartsFileHolder; - FileHolder hiddenFaceFileHolder; - double deterministicHashCodeKey; - DateTime dateTime = DateTime.Now; - const string facePopulatedKey = nameof(Closest); - foreach (Item item in items) - { - used.Clear(); - if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) - continue; - if (!item.Closest.Any()) - continue; - directoryName = Path.GetDirectoryName(item.RelativePath); - if (directoryName is null) - throw new Exception(); - foreach (Face face in item.Faces) - { - match = null; - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - continue; - foreach (Closest closest in item.Closest) - { - if (closest.NormalizedPixelPercentage != face.Location.NormalizedPixelPercentage.Value) - continue; - match = closest; - break; - } - if (match is null) - continue; - foreach (Mapping mapping in item.Mapping) - { - if (mapping.Forced) - continue; - if (mapping.NormalizedPixelPercentage is null || mapping.NormalizedPixelPercentage.Value != face.Location.NormalizedPixelPercentage.Value) - continue; - throw new Exception(); - } - if (!match.AboveTolerance) - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(match.Mapping.PersonBirthday); - else - { - personKeyFormatted = string.Empty; - foreach (long personKey in _NotMappedPersonKeys) - { - if (personKey == match.Mapping.PersonBirthday.Value.Ticks) - continue; - personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKey); - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); - break; - } - if (string.IsNullOrEmpty(personKeyFormatted)) - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(match.Mapping.PersonBirthday); - } - directory = Path.Combine(_ZPropertyHolderContentTicksDirectory, facePopulatedKey, personKeyFormatted, match.Mapping.SegmentB); - personDirectory = Path.Combine(directory, match.Mapping.DisplayDirectoryName[..1], "lnk"); - saveContainer = new(personDirectory); - results.Add(saveContainer); - facesDirectory = string.Concat(dFacesContentDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); - facePartsDirectory = string.Concat(d2ResultsFullGroupDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); - deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); - checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}"); - faceFileHolder = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacesFilenameExtension}")); - hiddenFaceFileHolder = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacesHiddenFilenameExtension}")); - facePartsFileHolder = new(Path.Combine(facePartsDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacePartsFilenameExtension}")); - if (string.IsNullOrEmpty(personDirectory)) - shortcutFile = string.Empty; - else - shortcutFile = Path.Combine(personDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.lnk"); - saveContainer = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, string.Empty, facePartsFileHolder, item.ResizedFileHolder, shortcutFile); - results.Add(saveContainer); - personDirectory = Path.Combine(directory, match.Mapping.DisplayDirectoryName[..1], "lnk", match.Mapping.DisplayDirectoryName); - saveContainer = new(personDirectory); - results.Add(saveContainer); - used.Add(face.Location.NormalizedPixelPercentage.Value); - } - foreach (Closest closest in item.Closest) - { - if (used.Contains(closest.NormalizedPixelPercentage)) - continue; - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(closest.Mapping.PersonBirthday); - directory = Path.Combine(_ZPropertyHolderContentTicksDirectory, facePopulatedKey, personKeyFormatted, closest.Mapping.SegmentB); - personDirectory = Path.Combine(directory, closest.Mapping.DisplayDirectoryName, "lnk"); - saveContainer = new(personDirectory); - results.Add(saveContainer); - facesDirectory = string.Concat(dFacesContentDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); - facePartsDirectory = string.Concat(d2ResultsFullGroupDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); - deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, closest); - checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}"); - faceFileHolder = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacesFilenameExtension}")); - hiddenFaceFileHolder = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacesHiddenFilenameExtension}")); - facePartsFileHolder = new(Path.Combine(facePartsDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FacePartsFilenameExtension}")); - if (string.IsNullOrEmpty(personDirectory)) - shortcutFile = string.Empty; - else - shortcutFile = Path.Combine(personDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.lnk"); - saveContainer = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, string.Empty, facePartsFileHolder, item.ResizedFileHolder, shortcutFile); - results.Add(saveContainer); - personDirectory = Path.Combine(directory, closest.Mapping.DisplayDirectoryName[..1], "lnk", closest.Mapping.DisplayDirectoryName); - saveContainer = new(personDirectory); - results.Add(saveContainer); - used.Add(closest.NormalizedPixelPercentage); - } - } - return results; - } - - public void SaveMapping(Item[] items, string dFacesContentDirectory, string d2ResultsFullGroupDirectory) - { - List saveContainers = GetMappingSaveContainers(items, dFacesContentDirectory, d2ResultsFullGroupDirectory); - SaveContainers(saveContainers); - } - - public void SaveClosest(Item[] items, string dFacesContentDirectory, string d2ResultsFullGroupDirectory) - { - List saveContainers = GetClosestSaveContainers(items, dFacesContentDirectory, d2ResultsFullGroupDirectory); - SaveContainers(saveContainers); - } - - public void ForceSingleImage(long ticks, Item[] items, int totalNotMapped, Random random) - { - int count = 0; - Closest closest; - Mapping mapping; - bool? isWrongYear; - bool forced = true; - const int zero = 0; - int randomSelection; - string mappingSegmentB; - long? personKey = null; - DateTime minimumDateTime; - string personKeyFormatted; - bool aboveTolerance = false; - int? approximateYears = null; - PersonBirthday personBirthday; - const string displayDirectoryName = Property.Models.Stateless.IResult.AllInOne; - for (int i = 0; i < int.MaxValue; i++) - { - count = 0; - randomSelection = random.Next(0, totalNotMapped); - foreach (Item item in items) - { - if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) - continue; - if (item.Mapping.Any()) - continue; - foreach (Face face in item.Faces) - { - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - continue; - if (face.Location.Confidence < ILocation.MinimumConfidence) - continue; - count += 1; - if (count < randomSelection) - continue; - personKey = _NotMappedPersonKeys[zero]; - minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); - personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKey.Value); - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); - minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); - mappingSegmentB = Stateless.MapLogic.GetMappingSegmentB(ticks, personBirthday, approximateYears, minimumDateTime, isWrongYear); - mapping = new(approximateYears, displayDirectoryName, forced, face.Location.NormalizedPixelPercentage, personBirthday, mappingSegmentB); - closest = new(aboveTolerance, zero, face.Location.NormalizedPixelPercentage.Value, isWrongYear, mapping, zero, minimumDateTime, null); - item.Closest.Add(closest); - item.Mapping.Add(mapping); - if (personKey is not null) - break; - } - if (personKey is not null) - break; - } - if (personKey is not null || count == 0) - break; - } - if (personKey is null && count != 0) - throw new Exception(); - } - public void SavePropertyHolders(Item[] items) { string json; @@ -926,7 +615,7 @@ public class MapLogic { if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) continue; - if (!item.Faces.Any() || !item.Closest.Any()) + if (!item.Faces.Any()) continue; json = JsonSerializer.Serialize(item, jsonSerializerOptions); fileInfo = new(Path.GetFullPath(string.Concat(_ZPropertyHolderSingletonTicksDirectory, item.RelativePath, ".json"))); @@ -934,8 +623,302 @@ public class MapLogic continue; if (!fileInfo.Directory.Exists) continue; - _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true); + _ = IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true); } } + private static Dictionary> Convert(List<(int Id, Face Face)> collection) + { + Dictionary> results = new(); + Dictionary keyValuePairs; + foreach ((int id, Face face) in collection) + { + if (!results.ContainsKey(id)) + results.Add(id, new()); + if (face.Location?.NormalizedPixelPercentage is null) + throw new NotSupportedException(); + keyValuePairs = results[id]; + if (keyValuePairs.ContainsKey(face.Location.NormalizedPixelPercentage.Value)) + continue; + results[id].Add(face.Location.NormalizedPixelPercentage.Value, face); + } + return results; + } + + private Dictionary> GetUsedIdThenNormalizedPixelPercentageKeyValuePairs() + { + Dictionary> results = new(); + foreach (KeyValuePair> keyValuePair in _IdThenNormalizedPixelPercentageKeyValuePairs) + results.Add(keyValuePair.Key, (from l in keyValuePair.Value select l.Key).ToList()); + return results; + } + + private int UpdateFromSortingContainers(SortingContainer[] sortingContainers) + { + int result = 0; + Dictionary> results = new(); + string key; + long personKey; + const int by = 3; + const int zero = 0; + HashSet hashSet; + string mappingSegmentB; + string personKeyFormatted; + List checkCollection; + PersonBirthday personBirthday; + PersonBirthday[] personBirthdays; + Dictionary keyValuePairs; + Dictionary> checkKeyValuePairs = new(); + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - _Ticks).TotalSeconds); + (string DisplayDirectoryName, int? ApproximateYears, PersonBirthday[] PersonBirthdays, long PersonKey) person; + string message = $") {sortingContainers.Length:000} Update From Sorting Container(s) - {totalSeconds} total second(s)"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = false }; + foreach (KeyValuePair> keyValuePair in _IdThenNormalizedPixelPercentageKeyValuePairs) + results.Add(keyValuePair.Key, (from l in keyValuePair.Value select l.Key).ToHashSet()); + using ProgressBar progressBar = new(sortingContainers.Length, message, options); + foreach (SortingContainer sortingContainer in sortingContainers) + { + if (sortingContainer.Face.Mapping is null) + throw new NotSupportedException(); + if (!_IdThenNormalizedPixelPercentageKeyValuePairs.ContainsKey(sortingContainer.Sorting.Id)) + throw new NotSupportedException(); + if (!results.ContainsKey(sortingContainer.Face.Mapping.MappingFromItem.Id)) + results.Add(sortingContainer.Face.Mapping.MappingFromItem.Id, new()); + hashSet = results[sortingContainer.Face.Mapping.MappingFromItem.Id]; + if (hashSet.Contains(sortingContainer.Face.Mapping.MappingFromLocation.NormalizedPixelPercentage)) + continue; + keyValuePairs = _IdThenNormalizedPixelPercentageKeyValuePairs[sortingContainer.Sorting.Id]; + if (!keyValuePairs.ContainsKey(sortingContainer.Sorting.NormalizedPixelPercentage)) + throw new NotSupportedException(); + personBirthdays = keyValuePairs[sortingContainer.Sorting.NormalizedPixelPercentage]; + if (sortingContainer.Sorting.DistancePermyriad > Shared.Models.Stateless.IFaceDistance.Permyriad || sortingContainer.Sorting.DaysDelta > Shared.Models.Stateless.ISorting.DaysDeltaTolerance) + continue; + for (int i = 0; i < personBirthdays.Length; i++) + { + personKey = personBirthdays[i].Value.Ticks; + if (!_PeopleKeyValuePairs.ContainsKey(personKey)) + continue; + person = _PeopleKeyValuePairs[personKey]; + personBirthday = person.PersonBirthdays[zero]; + personKeyFormatted = IPersonBirthday.GetFormatted(personBirthday); + mappingSegmentB = GetMappingSegmentB(_Ticks, personBirthday, person.ApproximateYears, sortingContainer.Face.Mapping.MappingFromItem); + key = string.Concat(personKeyFormatted, '\t', mappingSegmentB); + if (!checkKeyValuePairs.ContainsKey(key)) + checkKeyValuePairs.Add(key, new()); + checkCollection = checkKeyValuePairs[key]; + if (checkCollection.Count > Shared.Models.Stateless.ISorting.MaximumPerKey) + continue; + _ = hashSet.Add(sortingContainer.Sorting.NormalizedPixelPercentage); + sortingContainer.Face.Mapping.UpdateMappingFromPerson(person.ApproximateYears, by, person.DisplayDirectoryName, personBirthday, mappingSegmentB); + checkCollection.Add(sortingContainer.Face.Mapping); + result += 1; + break; + } + } + return result; + } + + internal void ForceSingleImage(IEnumerable distinctFilteredFaces) + { + int by = 4; + long? personKey; + const int zero = 0; + string mappingSegmentB; + int? approximateYears = null; + PersonBirthday personBirthday; + Face[] orderedDistinctFilteredFaces = (from l in distinctFilteredFaces orderby l.Mapping is not null, l.Mapping?.MappingFromLocation.Confidence descending select l).ToArray(); + const string displayDirectoryName = Property.Models.Stateless.IResult.AllInOne; + foreach (Face face in orderedDistinctFilteredFaces) + { + if (face.Mapping is null) + throw new NotSupportedException(); + if (face.Mapping.MappingFromPerson.PersonBirthday is not null) + continue; + personKey = _NotMappedPersonKeys[zero]; + personBirthday = IPersonBirthday.GetPersonBirthday(personKey.Value); + mappingSegmentB = GetMappingSegmentB(_Ticks, personBirthday, approximateYears, face.Mapping.MappingFromItem); + face.Mapping.UpdateMappingFromPerson(approximateYears, by, displayDirectoryName, personBirthday, mappingSegmentB); + break; + } + } + + private List GetMappingSaveContainers(string dFacesContentDirectory, string d2ResultsFullGroupDirectory, List filteredFaces) + { + List results = new(); + string by; + string json; + string checkFile; + string directory; + string shortcutFile; + string facesDirectory; + string? directoryName; + string personDirectory; + FileHolder faceFileHolder; + string facePartsDirectory; + string personKeyFormatted; + SaveContainer saveContainer; + FileHolder facePartsFileHolder; + FileHolder hiddenFaceFileHolder; + foreach (Face face in filteredFaces) + { + if (face.Mapping is null) + throw new NotSupportedException(); + directoryName = Path.GetDirectoryName(face.RelativePath); + if (directoryName is null) + throw new NotSupportedException(); + if (face.Mapping.MappingFromPerson.PersonBirthday is null) + continue; + if (string.IsNullOrEmpty(face.Mapping.MappingFromPerson.SegmentB)) + throw new NotSupportedException(); + personKeyFormatted = IPersonBirthday.GetFormatted(face.Mapping.MappingFromPerson.PersonBirthday); + if (face.Mapping.MappingFromPerson.By is null) + by = $"{nameof(Mapping)}Null"; + else + by = face.Mapping.MappingFromPerson.By.Value switch + { + 1 => "ByUnknownFaceKey", + 2 => nameof(Mapping), + 3 => nameof(Sorting), + 4 => nameof(MapLogic.ForceSingleImage), + _ => throw new NotImplementedException() + }; + if (face.Mapping.MappingFromPerson.By is not null and not 2) + { + directory = Path.Combine(_ZPropertyHolderContentTicksDirectory, by, personKeyFormatted, face.Mapping.MappingFromPerson.SegmentB); + personDirectory = Path.Combine(directory, face.Mapping.MappingFromPerson.DisplayDirectoryName, "lnk"); + } + else + { + directory = Path.Combine(_ZPropertyHolderContentTicksDirectory, by, personKeyFormatted, face.Mapping.MappingFromPerson.SegmentB); + personDirectory = Path.Combine(directory, face.Mapping.MappingFromPerson.DisplayDirectoryName[..1], "lnk"); + if (face.Mapping.MappingFromPerson.By == 2 && !Shared.Models.Stateless.IMapping.SaveMapped) + continue; + } + saveContainer = new(personDirectory); + results.Add(saveContainer); + facesDirectory = string.Concat(dFacesContentDirectory, Path.Combine(directoryName, face.Mapping.MappingFromItem.ImageFileHolder.NameWithoutExtension)); + facePartsDirectory = string.Concat(d2ResultsFullGroupDirectory, Path.Combine(directoryName, face.Mapping.MappingFromItem.ImageFileHolder.NameWithoutExtension)); + checkFile = Path.Combine(directory, $"{face.Mapping.MappingFromLocation.DeterministicHashCodeKeyDisplay}{face.Mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}"); + faceFileHolder = new(Path.Combine(facesDirectory, $"{face.Mapping.MappingFromLocation.DeterministicHashCodeKeyDisplay}{face.Mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_FacesFilenameExtension}")); + hiddenFaceFileHolder = new(Path.Combine(facesDirectory, $"{face.Mapping.MappingFromLocation.DeterministicHashCodeKeyDisplay}{face.Mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_FacesHiddenFilenameExtension}")); + facePartsFileHolder = new(Path.Combine(facePartsDirectory, $"{face.Mapping.MappingFromLocation.DeterministicHashCodeKeyDisplay}{face.Mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_FacePartsFilenameExtension}")); + if (string.IsNullOrEmpty(personDirectory)) + shortcutFile = string.Empty; + else + shortcutFile = Path.Combine(personDirectory, $"{face.Mapping.MappingFromLocation.DeterministicHashCodeKeyDisplay}{face.Mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}.lnk"); + saveContainer = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, string.Empty, facePartsFileHolder, face.Mapping.MappingFromItem.ResizedFileHolder, shortcutFile); + results.Add(saveContainer); + if (!string.IsNullOrEmpty(checkFile) && Shared.Models.Stateless.IMapping.SaveFaceEncoding) + { + checkFile = Path.Combine(directory, $"{face.Mapping.MappingFromLocation.DeterministicHashCodeKeyDisplay}{face.Mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}.json"); + json = JsonSerializer.Serialize(face.FaceEncoding); + saveContainer = new(checkFile, directory, json); + results.Add(saveContainer); + } + results.Add(saveContainer); + } + return results; + } + + public void CommonWork(int maxDegreeOfParallelism, string dFacesContentDirectory, string d2ResultsFullGroupDirectory, List distinctFilteredFaces, SortingContainer[] sortingContainers) + { + List saveContainers; + if (_DeterministicHashCodeUnknownFaceKeyValuePairs.Any()) + UseKeyValuePairsSaveFaceEncoding(maxDegreeOfParallelism, sortingContainers); + int totalNotMapped = AddToMapping(distinctFilteredFaces); + if (totalNotMapped == 0) + saveContainers = new(); + else if (!sortingContainers.Any()) + { + ForceSingleImage(distinctFilteredFaces); + saveContainers = GetMappingSaveContainers(dFacesContentDirectory, d2ResultsFullGroupDirectory, distinctFilteredFaces); + } + else + { + int updated = UpdateFromSortingContainers(sortingContainers); + if (totalNotMapped - updated > 0) + ForceSingleImage(distinctFilteredFaces); + saveContainers = GetMappingSaveContainers(dFacesContentDirectory, d2ResultsFullGroupDirectory, distinctFilteredFaces); + } + SaveContainers(saveContainers); + for (int i = 1; i < 5; i++) + _ = IPath.DeleteEmptyDirectories(_ZPropertyHolderContentTicksDirectory); + _ = IPath.DeleteEmptyDirectories(_ZPropertyHolderContentTicksDirectory); + SaveNotMappedTicks(); + } + + private 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; + } + + private void SetPersonKeysRanges(Dictionary> personTicks) + { + long lcl; + long ucl; + long maximum; + long minimum; + double average; + long[] collection; + double standardDeviation; + foreach (KeyValuePair> keyValuePair in personTicks) + { + minimum = keyValuePair.Value.Min(); + if (keyValuePair.Value.Count < 3) + { + maximum = keyValuePair.Value.Max(); + _PersonKeysRanges.Add(keyValuePair.Key, new(new DateTime(minimum).AddYears(-1).Ticks, minimum, maximum, new DateTime(maximum).AddYears(1).Ticks)); + } + else + { + collection = (from l in keyValuePair.Value select l - minimum).ToArray(); + maximum = collection.Max() + minimum; + average = (collection.Sum() / collection.Length) + minimum; + standardDeviation = GetStandardDeviation(collection, average); + ucl = (long)(average + (standardDeviation * Stateless.IMapLogic.Sigma)); + lcl = (long)(average - (standardDeviation * Stateless.IMapLogic.Sigma)); + if (lcl < 0) + lcl = 0; + if (ucl > 0) + ucl = _Ticks; + _PersonKeysRanges.Add(keyValuePair.Key, new(lcl, minimum, maximum, ucl)); + } + } + } + + public void SetPersonTicks(string outputResolution, Face[] filteredFaces) + { + PersonBirthday[] personBirthdays; + Dictionary keyValuePairs; + Dictionary> personTicks = new(); + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - _Ticks).TotalSeconds); + string message = $") {filteredFaces.Length:000} Set Person Ticks - {totalSeconds} total second(s) - {outputResolution}"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + using ProgressBar progressBar = new(filteredFaces.Length, message, options); + foreach (Face face in filteredFaces) + { + progressBar.Tick(); + if (face.Mapping is null) + throw new NotSupportedException(); + if (!_IdThenNormalizedPixelPercentageKeyValuePairs.ContainsKey(face.Mapping.MappingFromItem.Id)) + continue; + keyValuePairs = _IdThenNormalizedPixelPercentageKeyValuePairs[face.Mapping.MappingFromItem.Id]; + if (!keyValuePairs.ContainsKey(face.Mapping.MappingFromLocation.NormalizedPixelPercentage)) + continue; + personBirthdays = keyValuePairs[face.Mapping.MappingFromLocation.NormalizedPixelPercentage]; + foreach (PersonBirthday personBirthday in personBirthdays) + { + if (!personTicks.ContainsKey(personBirthday.Value.Ticks)) + personTicks.Add(personBirthday.Value.Ticks, new()); + personTicks[personBirthday.Value.Ticks].Add(face.Mapping.MappingFromItem.MinimumDateTime.Ticks); + } + } + SetPersonKeysRanges(personTicks); + } + } \ No newline at end of file diff --git a/Map/Models/Stateless/IMapLogic.cs b/Map/Models/Stateless/IMapLogic.cs index cb3198a..2046d49 100644 --- a/Map/Models/Stateless/IMapLogic.cs +++ b/Map/Models/Stateless/IMapLogic.cs @@ -3,12 +3,6 @@ namespace View_by_Distance.Map.Models.Stateless; public interface IMapLogic { // ... - (bool?, string[]) TestStatic_IsWrongYear(Shared.Models.Item item); - static (bool?, string[]) IsWrongYear(Shared.Models.Item item) => - MapLogic.IsWrongYear(item); - - Dictionary> TestStatic_GetKeyValuePairs(Shared.Models.Item[] items); - static Dictionary> GetKeyValuePairs(Shared.Models.Item[] items) => - MapLogic.GetKeyValuePairs(items); + const int Sigma = 3; } \ No newline at end of file diff --git a/Map/Models/Stateless/MapLogic.cs b/Map/Models/Stateless/MapLogic.cs index 3349d3a..b161f6b 100644 --- a/Map/Models/Stateless/MapLogic.cs +++ b/Map/Models/Stateless/MapLogic.cs @@ -1,84 +1,6 @@ -using View_by_Distance.Shared.Models; - namespace View_by_Distance.Map.Models.Stateless; internal abstract class MapLogic { - internal static string GetMappingSegmentB(long ticks, PersonBirthday personBirthday, int? approximateYears, DateTime minimumDateTime, bool? isWrongYear) - { - int years; - string result; - TimeSpan? timeSpan = Shared.Models.Stateless.Methods.IPersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, personBirthday); - if (timeSpan.HasValue && timeSpan.Value.Ticks < 0) - result = "!---"; - else if (timeSpan.HasValue) - { - (years, _) = Shared.Models.Stateless.Methods.IPersonBirthday.GetAge(minimumDateTime, personBirthday); - result = $"^{years:000}"; - } - else if (approximateYears.HasValue) - { - DateTime dateTime = new(ticks); - (years, _) = Shared.Models.Stateless.Methods.IAge.GetAge(minimumDateTime, dateTime.AddYears(-approximateYears.Value)); - result = $"~{years:000}"; - } - else - { - string isWrongYearFlag = Shared.Models.Stateless.Methods.IItem.GetWrongYearFlag(isWrongYear); - result = $"{isWrongYearFlag}{minimumDateTime:yyyy}"; - } - return result; - } - - internal static Dictionary> GetKeyValuePairs(Item[] items) - { - Dictionary> results = new(); - string key; - bool? isWrongYear; - string personKeyFormatted; - DateTime minimumDateTime; - MappingContainer mappingContainer; - foreach (Item item in items) - { - if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) - continue; - if (!item.Mapping.Any()) - continue; - foreach (Face face in item.Faces) - { - if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) - continue; - foreach (Mapping mapping in item.Mapping) - { - if (mapping.NormalizedPixelPercentage.HasValue && mapping.NormalizedPixelPercentage.Value != face.Location.NormalizedPixelPercentage.Value) - continue; - // if (named.NormalizedPixelPercentage is null && (Shared.Models.Stateless.INamed.OnlyUseNamedWithNormalizedPixelPercentagePopulatedForGetKeyValuePairs || item.Named.Count != 1 || item.Faces.Count != 1)) - // continue; - minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); - personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(mapping.PersonBirthday); - key = string.Concat(personKeyFormatted, mapping.SegmentB); - if (!results.ContainsKey(key)) - results.Add(key, new()); - mappingContainer = new(face, item.Property.Id.Value, isWrongYear, key, mapping, minimumDateTime); - results[key].Add(mappingContainer); - // if (named.NormalizedPixelPercentage is null) - // break; - } - } - } - return results; - } - - internal static (bool?, string[]) IsWrongYear(Item item) - { - (bool?, string[]) result; - if (item.Property is null || item.ImageFileHolder is null) - throw new NullReferenceException(); - DateTime? minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - result = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); - return result; - } - } \ No newline at end of file diff --git a/Map/Models/Stateless/Methods/IMapLogic.cs b/Map/Models/Stateless/Methods/IMapLogic.cs new file mode 100644 index 0000000..0f6456e --- /dev/null +++ b/Map/Models/Stateless/Methods/IMapLogic.cs @@ -0,0 +1,6 @@ +namespace View_by_Distance.Map.Models.Stateless.Methods; + +public interface IMapLogic +{ // ... + +} \ No newline at end of file diff --git a/Map/Models/Stateless/SetByDeterministicHashCode.cs b/Map/Models/Stateless/SetByDeterministicHashCode.cs index 845d191..8554d8a 100644 --- a/Map/Models/Stateless/SetByDeterministicHashCode.cs +++ b/Map/Models/Stateless/SetByDeterministicHashCode.cs @@ -1,3 +1,4 @@ +using ShellProgressBar; using System.Globalization; using System.Text.Json; using View_by_Distance.Shared.Models; @@ -7,11 +8,14 @@ namespace View_by_Distance.Map.Models.Stateless; public class ByDeterministicHashCode { - private static void SetOther(string resizeFilenameExtension, Person[] people, string deterministicHashCodePeopleDirectory, List skipCollection, List<(string, int?, PersonBirthday[], long)> peopleCollection) + private static void SetOther(string resizeFilenameExtension, Person[] people, string deterministicHashCodePeopleDirectory, List skipCollection, Dictionary personKeyAliases, List<(string, int?, PersonBirthday[], long)> peopleCollection) { - long ticks; + long pK; string json; + long personKey; + PersonBirthday pB; string[] segments; + const int zero = 0; int? approximateYears; string groupDirectoryName; string personKeyFormatted; @@ -37,12 +41,12 @@ public class ByDeterministicHashCode foreach (string groupDirectory in groupDirectories) { groupDirectoryName = Path.GetFileName(groupDirectory); - if (groupDirectoryName[0] == '!') + if (groupDirectoryName[zero] == '!') { skipCollection.AddRange(from l in Directory.GetFiles(groupDirectory, $"*{resizeFilenameExtension}", SearchOption.AllDirectories) select double.Parse(Path.GetFileNameWithoutExtension(l))); continue; } - else if (groupDirectoryName[0] is not '_' and not '~' and not '^') + else if (groupDirectoryName[zero] is not '_' and not '~' and not '^') continue; skipCollection.AddRange(from l in Directory.GetFiles(groupDirectory, $"*{resizeFilenameExtension}", SearchOption.AllDirectories) select double.Parse(Path.GetFileNameWithoutExtension(l))); personDisplayDirectories = Directory.GetDirectories(groupDirectory, "*", SearchOption.TopDirectoryOnly); @@ -52,12 +56,12 @@ public class ByDeterministicHashCode personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory); if (string.IsNullOrEmpty(personDisplayDirectoryName)) continue; - if (groupDirectoryName[0] != '~') + if (groupDirectoryName[zero] != '~') approximateYears = null; else { segments = personDisplayDirectoryName.Split('~'); - if (segments.Length == 1 || !int.TryParse(segments[1].Split('-')[0], out int years)) + if (segments.Length == 1 || !int.TryParse(segments[1].Split('-')[zero], out int years)) approximateYears = null; else approximateYears = years; @@ -98,12 +102,22 @@ public class ByDeterministicHashCode _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(personKeyJsonFileName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); } collection = personBirthdays.OrderByDescending(l => l.Value).ToArray(); - ticks = collection[0].Value.Ticks; - peopleCollection.Add(new(personDisplayDirectoryName, approximateYears, collection, ticks)); + personKey = collection[zero].Value.Ticks; + if (personBirthdays.Count > 1) + { + for (int i = 1; i < collection.Length; i++) + { + pB = collection[i]; + pK = pB.Value.Ticks; + if (!personKeyAliases.ContainsKey(pK)) + personKeyAliases.Add(pK, personKey); + } + } + peopleCollection.Add(new(personDisplayDirectoryName, approximateYears, collection, personKey)); for (int i = 0; i < collection.Length; i++) { - ticks = collection[i].Value.Ticks; - personKeys.Add(ticks); + personKey = collection[i].Value.Ticks; + personKeys.Add(personKey); } } } @@ -113,48 +127,62 @@ public class ByDeterministicHashCode DateTime incrementDate = new(Shared.Models.Stateless.IPersonBirthday.FirstYear, 1, 1); for (int i = 0; i < 500; i++) { - ticks = incrementDate.Ticks; + personKey = incrementDate.Ticks; personBirthday = new(incrementDate); incrementDate = incrementDate.AddDays(1); personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); - if (personKeys.Contains(ticks)) + if (personKeys.Contains(personKey)) continue; - personKeys.Add(ticks); - peopleCollection.Add(new(displayDirectoryName, approximateYears, new PersonBirthday[] { personBirthday }, ticks)); + personKeys.Add(personKey); + peopleCollection.Add(new(displayDirectoryName, approximateYears, new PersonBirthday[] { personBirthday }, personKey)); } } - - internal static void SetKeyValuePairs(string zPropertyHolderContentDirectory, List<(PersonBirthday, double)> deterministicHashCodeCollection, List<(PersonBirthday, double)> incorrectDeterministicHashCodeCollection, Dictionary> keyValuePairs) + internal static void SetCollections(int maxDegreeOfParallelism, long ticks, string zPropertyHolderContentDirectory, Dictionary personKeyAliases, List<(PersonBirthday, int, int)> idThenNormalizedPixelPercentageCollection, List<(PersonBirthday, double)> incorrectDeterministicHashCodeCollection, Dictionary> keyValuePairs) { + int? id; string[] files; - string personKey; + long personKey; + const int zero = 0; string[] yearDirectories; + string personKeyFormatted; string ticksDirectoryName; string? personFirstInitial; string[] personKeyDirectories; + int? normalizedPixelPercentage; string[] personNameDirectories; PersonBirthday? personBirthday; string[] personNameLinkDirectories; string? personFirstInitialDirectory; - double? reversedDeterministicHashCodeKey; bool keyValuePairsAny = keyValuePairs.Any(); + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string[] ticksDirectories = Directory.GetDirectories(zPropertyHolderContentDirectory, "*", SearchOption.TopDirectoryOnly); + string message = $") {ticksDirectories.Length:000} ticks Director(ies) - {totalSeconds} total second(s)"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + using ProgressBar progressBar = new(ticksDirectories.Length, message, options); foreach (string ticksDirectory in ticksDirectories) { + progressBar.Tick(); ticksDirectoryName = Path.GetFileName(ticksDirectory); - if (ticksDirectoryName.Length < 3 || ticksDirectoryName[0] != '(' || ticksDirectoryName[^1] != ')') + if (ticksDirectoryName.Length < 3 || ticksDirectoryName[zero] != '(' || ticksDirectoryName[^1] != ')') continue; personKeyDirectories = Directory.GetDirectories(ticksDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string personKeyDirectory in personKeyDirectories) { - personKey = Path.GetFileName(personKeyDirectory); - if (personKey.Length != Shared.Models.Stateless.IPersonBirthday.Format.Length) + personKeyFormatted = Path.GetFileName(personKeyDirectory); + if (personKeyFormatted.Length != Shared.Models.Stateless.IPersonBirthday.Format.Length) personBirthday = null; else { - personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKey); + personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKeyFormatted); if (personBirthday is null) continue; + personKey = personBirthday.Value.Ticks; + if (personKeyAliases.ContainsKey(personKey)) + { + personKey = personKeyAliases[personKey]; + personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKey); + } } yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string yearDirectory in yearDirectories) @@ -170,18 +198,20 @@ public class ByDeterministicHashCode continue; personFirstInitialDirectory = Path.Combine(yearDirectory, personFirstInitial); files = Directory.GetFiles(personNameDirectory, "*", SearchOption.TopDirectoryOnly); - if (personKey == nameof(Closest) && files.Any()) - throw new Exception($"Move personKey directories up one from {nameof(Closest)} and delete {nameof(Closest)} directory!"); + if (personKeyFormatted == nameof(Models.MapLogic.ForceSingleImage) && files.Any()) + throw new Exception($"Move personKey directories up one from {nameof(Models.MapLogic.ForceSingleImage)} and delete {nameof(Models.MapLogic.ForceSingleImage)} directory!"); + if (personKeyFormatted == nameof(Sorting) && files.Any()) + throw new Exception($"Move personKey directories up one from {nameof(Sorting)} and delete {nameof(Sorting)} directory!"); if (personBirthday is null) continue; foreach (string file in files) { if (file.EndsWith(".lnk") || file.EndsWith(".json")) continue; - reversedDeterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); - if (reversedDeterministicHashCodeKey is null) + (id, normalizedPixelPercentage) = Shared.Models.Stateless.Methods.IMapping.GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); + if (id is null || normalizedPixelPercentage is null) continue; - deterministicHashCodeCollection.Add(new(personBirthday, reversedDeterministicHashCodeKey.Value)); + idThenNormalizedPixelPercentageCollection.Add(new(personBirthday, id.Value, normalizedPixelPercentage.Value)); } personNameLinkDirectories = Directory.GetDirectories(personNameDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string personNameLinkDirectory in personNameLinkDirectories) @@ -209,7 +239,7 @@ public class ByDeterministicHashCode } } - private static List GetTicks(Dictionary deterministicHashCodeUnknownFaceKeyValuePairs) + private static List GetPersonKeys(Dictionary deterministicHashCodeUnknownFaceKeyValuePairs) { List results = new(); long personKey; @@ -225,58 +255,86 @@ public class ByDeterministicHashCode return results; } - private static void Set(List<(PersonBirthday PersonBirthday, double IdAndNormalizedPixelPercentage)> deterministicHashCodeCollection, List deterministicHashCodePersonKeys, Dictionary> deterministicHashCodeScope) + private static void SetKeyValuePairs(List<(PersonBirthday, int, int)> idThenNormalizedPixelPercentageCollection, List<(PersonBirthday PersonBirthday, double IdAndNormalizedPixelPercentage)> incorrectDeterministicHashCodeCollection, Dictionary incorrectDeterministicHashCodeKeyValuePairs, Dictionary> idThenNormalizedPixelPercentageKeyValuePairs) { - long personKey; - deterministicHashCodeCollection = (from l in deterministicHashCodeCollection orderby l.IdAndNormalizedPixelPercentage select l).ToList(); - foreach ((PersonBirthday personBirthday, double idAndNormalizedPixelPercentage) in deterministicHashCodeCollection) - { - if (!deterministicHashCodeScope.ContainsKey(idAndNormalizedPixelPercentage)) - deterministicHashCodeScope.Add(idAndNormalizedPixelPercentage, new()); - deterministicHashCodeScope[idAndNormalizedPixelPercentage].Add(personBirthday); - personKey = personBirthday.Value.Ticks; - deterministicHashCodePersonKeys.Add(personKey); - } - } - - internal static void SetByRef(string resizeFilenameExtension, Person[] people, List skipCollection, Dictionary peopleKeyValuePairs, List notMappedTicks, Dictionary deterministicHashCodeUnknownFaceKeyValuePairs, Dictionary deterministicHashCodeKeyValuePairs, Dictionary incorrectDeterministicHashCodeKeyValuePairs, string zPropertyHolderContentDirectory, string zPropertyHolderPeopleContentDirectory) - { - Dictionary> keyValuePairs = new(); - List deterministicHashCodePersonKeys = new(); - List<(string, int?, PersonBirthday[], long)> peopleCollection = new(); - Dictionary> deterministicHashCodeScope = new(); Dictionary> incorrectDeterministicHashCodeScope = new(); - List deterministicHashCodeUnknownFacePersonKeys = GetTicks(deterministicHashCodeUnknownFaceKeyValuePairs); - List<(PersonBirthday PersonBirthday, double IdAndNormalizedPixelPercentage)> incorrectDeterministicHashCodeCollection = new(); - List<(PersonBirthday PersonBirthday, double IdAndNormalizedPixelPercentage)> deterministicHashCodeCollection = new(); - SetOther(resizeFilenameExtension, people, zPropertyHolderPeopleContentDirectory, skipCollection, peopleCollection); - SetKeyValuePairs(zPropertyHolderContentDirectory, deterministicHashCodeCollection, incorrectDeterministicHashCodeCollection, keyValuePairs); - Set(deterministicHashCodeCollection, deterministicHashCodePersonKeys, deterministicHashCodeScope); - incorrectDeterministicHashCodeCollection = (from l in incorrectDeterministicHashCodeCollection orderby l.IdAndNormalizedPixelPercentage select l).ToList(); - deterministicHashCodePersonKeys = deterministicHashCodePersonKeys.Distinct().ToList(); + Dictionary>> idThenNormalizedPixelPercentageScope = new(); foreach ((PersonBirthday personBirthday, double idAndNormalizedPixelPercentage) in incorrectDeterministicHashCodeCollection) { if (!incorrectDeterministicHashCodeScope.ContainsKey(idAndNormalizedPixelPercentage)) incorrectDeterministicHashCodeScope.Add(idAndNormalizedPixelPercentage, new()); incorrectDeterministicHashCodeScope[idAndNormalizedPixelPercentage].Add(personBirthday); } - foreach (KeyValuePair> keyValuePair in deterministicHashCodeScope) - deterministicHashCodeKeyValuePairs.Add(keyValuePair.Key, keyValuePair.Value.Distinct().ToArray()); foreach (KeyValuePair> keyValuePair in incorrectDeterministicHashCodeScope) incorrectDeterministicHashCodeKeyValuePairs.Add(keyValuePair.Key, keyValuePair.Value.Distinct().ToArray()); - if (peopleCollection.Any()) + string check; + int normalizedPixelPercentageInDecimalForm; + foreach ((PersonBirthday personBirthday, int id, int normalizedPixelPercentage) in idThenNormalizedPixelPercentageCollection) { - foreach ((string displayDirectoryName, int? approximateYears, PersonBirthday[] personBirthdays, long ticks) in peopleCollection) + if (!idThenNormalizedPixelPercentageScope.ContainsKey(id)) + idThenNormalizedPixelPercentageScope.Add(id, new()); + check = normalizedPixelPercentage.ToString(); + if (check.Length == Shared.Models.Stateless.ILocation.Digits) { - if (peopleKeyValuePairs.ContainsKey(ticks) && peopleKeyValuePairs[ticks].Item1 != displayDirectoryName) - throw new NotImplementedException(); - if (deterministicHashCodeUnknownFacePersonKeys.Contains(ticks) || deterministicHashCodePersonKeys.Contains(ticks)) - peopleKeyValuePairs.Add(ticks, new(displayDirectoryName, approximateYears, personBirthdays, ticks)); - else - notMappedTicks.Add(ticks); + if (!idThenNormalizedPixelPercentageScope[id].ContainsKey(normalizedPixelPercentage)) + idThenNormalizedPixelPercentageScope[id].Add(normalizedPixelPercentage, new()); + idThenNormalizedPixelPercentageScope[id][normalizedPixelPercentage].Add(personBirthday); + } + else + { + normalizedPixelPercentageInDecimalForm = int.Parse(check.PadRight(Shared.Models.Stateless.ILocation.Digits, '0')); + if (!idThenNormalizedPixelPercentageScope[id].ContainsKey(normalizedPixelPercentageInDecimalForm)) + idThenNormalizedPixelPercentageScope[id].Add(normalizedPixelPercentageInDecimalForm, new()); + idThenNormalizedPixelPercentageScope[id][normalizedPixelPercentageInDecimalForm].Add(personBirthday); } } - if (deterministicHashCodeUnknownFacePersonKeys.Any() || deterministicHashCodePersonKeys.Any()) + foreach (KeyValuePair>> keyValuePair in idThenNormalizedPixelPercentageScope) + { + idThenNormalizedPixelPercentageKeyValuePairs.Add(keyValuePair.Key, new()); + foreach (KeyValuePair> innerKeyValuePair in keyValuePair.Value) + idThenNormalizedPixelPercentageKeyValuePairs[keyValuePair.Key].Add(innerKeyValuePair.Key, innerKeyValuePair.Value.Distinct().ToArray()); + } + } + + internal static void SetByRef(int maxDegreeOfParallelism, long ticks, string resizeFilenameExtension, Person[] people, List skipCollection, Dictionary peopleKeyValuePairs, List notMappedPersonKeys, Dictionary deterministicHashCodeUnknownFaceKeyValuePairs, Dictionary> idThenNormalizedPixelPercentageKeyValuePairs, Dictionary incorrectDeterministicHashCodeKeyValuePairs, string zPropertyHolderContentDirectory, string zPropertyHolderPeopleContentDirectory) + { + Dictionary personKeyAliases = new(); + Dictionary> keyValuePairs = new(); + List idThenNormalizedPixelPercentagePersonKeys = new(); + List<(string, int?, PersonBirthday[], long)> peopleCollection = new(); + List<(PersonBirthday, int, int)> idThenNormalizedPixelPercentageCollection = new(); + List deterministicHashCodeUnknownFacePersonKeys = GetPersonKeys(deterministicHashCodeUnknownFaceKeyValuePairs); + List<(PersonBirthday PersonBirthday, double IdAndNormalizedPixelPercentage)> incorrectDeterministicHashCodeCollection = new(); + SetOther(resizeFilenameExtension, people, zPropertyHolderPeopleContentDirectory, skipCollection, personKeyAliases, peopleCollection); + SetCollections(maxDegreeOfParallelism, ticks, zPropertyHolderContentDirectory, personKeyAliases, idThenNormalizedPixelPercentageCollection, incorrectDeterministicHashCodeCollection, keyValuePairs); + SetKeyValuePairs(idThenNormalizedPixelPercentageCollection, incorrectDeterministicHashCodeCollection, incorrectDeterministicHashCodeKeyValuePairs, idThenNormalizedPixelPercentageKeyValuePairs); + incorrectDeterministicHashCodeCollection = (from l in incorrectDeterministicHashCodeCollection orderby l.IdAndNormalizedPixelPercentage select l).ToList(); + foreach (KeyValuePair> keyValuePair in idThenNormalizedPixelPercentageKeyValuePairs) + { + foreach (KeyValuePair keyValue in keyValuePair.Value) + idThenNormalizedPixelPercentagePersonKeys.AddRange(from l in keyValue.Value select l.Value.Ticks); + } + idThenNormalizedPixelPercentagePersonKeys = idThenNormalizedPixelPercentagePersonKeys.Distinct().ToList(); + foreach (KeyValuePair keyValuePair in deterministicHashCodeUnknownFaceKeyValuePairs) + deterministicHashCodeUnknownFacePersonKeys.AddRange(from l in keyValuePair.Value select l.Value.Ticks); + deterministicHashCodeUnknownFacePersonKeys = deterministicHashCodeUnknownFacePersonKeys.Distinct().ToList(); + if (peopleCollection.Any()) + { + foreach ((string displayDirectoryName, int? approximateYears, PersonBirthday[] personBirthdays, long personKey) in peopleCollection) + { + if (peopleKeyValuePairs.ContainsKey(personKey)) + { + if (peopleKeyValuePairs[personKey].DisplayDirectoryName == displayDirectoryName) + continue; + throw new NotImplementedException(); + } + if (!deterministicHashCodeUnknownFacePersonKeys.Contains(personKey) && !idThenNormalizedPixelPercentagePersonKeys.Contains(personKey)) + notMappedPersonKeys.Add(personKey); + else + peopleKeyValuePairs.Add(personKey, new(displayDirectoryName, approximateYears, personBirthdays, personKey)); + } + } + if (deterministicHashCodeUnknownFacePersonKeys.Any() || idThenNormalizedPixelPercentagePersonKeys.Any()) { int? approximateYears = null; PersonBirthday? personBirthday; @@ -289,7 +347,7 @@ public class ByDeterministicHashCode peopleKeyValuePairs.Add(personKey, new(displayDirectoryName, approximateYears, new PersonBirthday[] { personBirthday }, personKey)); } } - foreach (long personKey in deterministicHashCodePersonKeys) + foreach (long personKey in idThenNormalizedPixelPercentagePersonKeys) { if (!peopleKeyValuePairs.ContainsKey(personKey)) { diff --git a/Shared/Models/Closest.cs b/Shared/Models/Closest.cs deleted file mode 100644 index 7869288..0000000 --- a/Shared/Models/Closest.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace View_by_Distance.Shared.Models; - -public class Closest : Properties.IClosest -{ - - protected readonly bool _AboveTolerance; - protected readonly int _Average; - protected readonly bool? _IsWrongYear; - protected Mapping _Mapping; - protected readonly double _Minimum; - protected readonly DateTime _MinimumDateTime; - protected readonly int _NormalizedPixelPercentage; - protected readonly long? _TicksDelta; - public bool AboveTolerance => _AboveTolerance; - public double Average => _Average; - public bool? IsWrongYear => _IsWrongYear; - public Mapping Mapping => _Mapping; - public double Minimum => _Minimum; - public DateTime MinimumDateTime => _MinimumDateTime; - public int NormalizedPixelPercentage => _NormalizedPixelPercentage; - public long? TicksDelta => _TicksDelta; - - [JsonConstructor] - public Closest(bool aboveTolerance, int average, int normalizedPixelPercentage, bool? isWrongYear, Mapping mapping, double minimum, DateTime minimumDateTime, long? ticksDelta) - { - _AboveTolerance = aboveTolerance; - _Average = average; - _NormalizedPixelPercentage = normalizedPixelPercentage; - _IsWrongYear = isWrongYear; - _Mapping = mapping; - _Minimum = minimum; - _MinimumDateTime = minimumDateTime; - _TicksDelta = ticksDelta; - } - - public override string ToString() - { - string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); - return result; - } - -} \ No newline at end of file diff --git a/Shared/Models/Face.cs b/Shared/Models/Face.cs index e850dc4..a2358f3 100644 --- a/Shared/Models/Face.cs +++ b/Shared/Models/Face.cs @@ -7,67 +7,47 @@ public class Face : Properties.IFace { protected DateTime _DateTime; - protected List _FaceDistances; + protected FaceDistance? _FaceDistance; protected FaceEncoding? _FaceEncoding; protected Dictionary? _FaceParts; - protected List? _FaceNumbers; protected readonly OutputResolution? _OutputResolution; protected Location? _Location; protected readonly int? _LocationIndex; + protected Mapping? _Mapping; protected readonly string _RelativePath; - protected List _Tags; public DateTime DateTime => _DateTime; - public List FaceDistances => _FaceDistances; + public FaceDistance? FaceDistance => _FaceDistance; public FaceEncoding? FaceEncoding => _FaceEncoding; public Dictionary? FaceParts => _FaceParts; - public List? FaceNumbers => _FaceNumbers; public Location? Location => _Location; public int? LocationIndex => _LocationIndex; + public Mapping? Mapping => _Mapping; public OutputResolution? OutputResolution => _OutputResolution; public string RelativePath => _RelativePath; - public List Tags => _Tags; [JsonConstructor] - public Face(DateTime dateTime, List faceDistances, FaceEncoding? faceEncoding, Dictionary? faceParts, List? faceNumbers, Location? location, int? locationIndex, OutputResolution? outputResolution, string relativePath) + public Face(DateTime dateTime, FaceDistance? faceDistance, FaceEncoding? faceEncoding, Dictionary? faceParts, Location? location, int? locationIndex, Mapping? mapping, OutputResolution? outputResolution, string relativePath) { - if (faceDistances is null) - faceDistances = new(); _DateTime = dateTime; - _FaceDistances = faceDistances; + _FaceDistance = faceDistance; _FaceEncoding = faceEncoding; _FaceParts = faceParts; - _FaceNumbers = faceNumbers; _Location = location; _LocationIndex = locationIndex; + _Mapping = mapping; _OutputResolution = outputResolution; _RelativePath = relativePath; - _Tags = new(); } - public Face() : - this(DateTime.MinValue, new(), null, null, null, null, null, null, string.Empty) - { } - - public Face(Location location) : - this(DateTime.MinValue, new(), null, null, null, location, null, null, string.Empty) - { } - public Face(int facesCount, Face face) : - this(face.DateTime, new(), face.FaceEncoding, face.FaceParts, null, face.Location, face.LocationIndex, face.OutputResolution, face.RelativePath) + this(face.DateTime, null, face.FaceEncoding, face.FaceParts, face.Location, face.LocationIndex, null, face.OutputResolution, face.RelativePath) { if (face.Location?.Confidence is not null && face.OutputResolution is not null) _Location = new(face.Location.Confidence, face.OutputResolution.Height, face.Location, face.OutputResolution.Width, facesCount); } - public Face(int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, Face face) : - this(face.DateTime, new(), face.FaceEncoding, face.FaceParts, null, face.Location, face.LocationIndex, null, face.RelativePath) - { - if (outputResolutionHeight > 0) - _OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth); - } - public Face(Property property, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, string relativePath, int? i, Location? location) : - this(DateTime.MinValue, new(), null, null, null, location, i, null, relativePath) + this(DateTime.MinValue, null, null, null, location, i, null, null, relativePath) { DateTime?[] dateTimes; _OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth); @@ -85,6 +65,10 @@ public class Face : Properties.IFace public void SetFaceParts(Dictionary faceParts) => _FaceParts = faceParts; - public void SetFaceNumbers(List faceNumbers) => _FaceNumbers = faceNumbers; + public void ReleaseFaceDistance() => _FaceDistance = null; + + public void SetMapping(Mapping mapping) => _Mapping = mapping; + + public void FaceDistanceAdd(FaceDistance faceDistance) => _FaceDistance = faceDistance; } \ No newline at end of file diff --git a/Shared/Models/FaceDistance.cs b/Shared/Models/FaceDistance.cs index 9350229..ffd063f 100644 --- a/Shared/Models/FaceDistance.cs +++ b/Shared/Models/FaceDistance.cs @@ -3,30 +3,39 @@ using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; -public class FaceDistance : Properties.IFaceDistance +public record class FaceDistance : Properties.IFaceDistance { - protected readonly List _Distances; - protected readonly bool? _IsWrongYear; - protected readonly string _Key; - protected readonly Mapping _Mapping; - protected readonly DateTime _MinimumDateTime; - public List Distances => _Distances; - public bool? IsWrongYear => _IsWrongYear; - public string Key => _Key; - public Mapping Mapping => _Mapping; - public DateTime MinimumDateTime => _MinimumDateTime; + public double? Confidence { init; get; } + public object? Encoding { init; get; } + public int Id { init; get; } + public bool? IsWrongYear { init; get; } + public double? Length { init; get; } + public DateTime? MinimumDateTime { init; get; } + public int? NormalizedPixelPercentage { init; get; } [JsonConstructor] - public FaceDistance(List distances, bool? isWrongYear, string key, Mapping mapping, DateTime minimumDateTime) + public FaceDistance(double? confidence, object? encoding, int id, bool? isWrongYear, double? length, DateTime? minimumDateTime, int? normalizedPixelPercentage) { - _Distances = distances; - _IsWrongYear = isWrongYear; - _Key = key; - _Mapping = mapping; - _MinimumDateTime = minimumDateTime; + Confidence = confidence; + Encoding = encoding; + Id = id; + IsWrongYear = isWrongYear; + Length = length; + MinimumDateTime = minimumDateTime; + NormalizedPixelPercentage = normalizedPixelPercentage; } + public FaceDistance(double? confidence, object? encoding, int id, bool? isWrongYear, DateTime? minimumDateTime, int? normalizedPixelPercentage) : + this(confidence, encoding, id, isWrongYear, null, minimumDateTime, normalizedPixelPercentage) + { } + + public FaceDistance(FaceDistance faceDistance, double length) : + this(faceDistance.Confidence, null, faceDistance.Id, faceDistance.IsWrongYear, length, faceDistance.MinimumDateTime, faceDistance.NormalizedPixelPercentage) + { } + + public FaceDistance(object encoding) => Encoding = encoding; + public override string ToString() { string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); diff --git a/Shared/Models/FaceDistanceContainer.cs b/Shared/Models/FaceDistanceContainer.cs new file mode 100644 index 0000000..13f418e --- /dev/null +++ b/Shared/Models/FaceDistanceContainer.cs @@ -0,0 +1,25 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace View_by_Distance.Shared.Models; + +public record class FaceDistanceContainer : Properties.IFaceDistanceContainer +{ + + public Face Face { init; get; } + public FaceDistance FaceDistance { init; get; } + + [JsonConstructor] + public FaceDistanceContainer(Face face, FaceDistance faceDistance) + { + Face = face; + FaceDistance = faceDistance; + } + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + +} \ No newline at end of file diff --git a/Shared/Models/Item.cs b/Shared/Models/Item.cs index a45cb18..5eec194 100644 --- a/Shared/Models/Item.cs +++ b/Shared/Models/Item.cs @@ -8,10 +8,8 @@ public class Item : Properties.IItem protected readonly bool? _Abandoned; protected readonly bool? _Changed; - protected List _Closest; protected List _Faces; protected readonly FileHolder? _ImageFileHolder; - protected List _Mapping; protected bool? _Moved; protected readonly bool? _NoJson; protected Property? _Property; @@ -21,10 +19,8 @@ public class Item : Properties.IItem protected bool _ValidImageFormatExtension; public bool? Abandoned => _Abandoned; public bool? Changed => _Changed; - public List Closest => _Closest; public List Faces => _Faces; public FileHolder? ImageFileHolder => _ImageFileHolder; - public List Mapping => _Mapping; public bool? Moved => _Moved; public bool? NoJson => _NoJson; public Property? Property => _Property; @@ -34,14 +30,12 @@ public class Item : Properties.IItem public bool ValidImageFormatExtension => _ValidImageFormatExtension; [JsonConstructor] - public Item(bool? abandoned, bool? changed, List closest, List faces, FileHolder? imageFileHolder, List mapping, bool? moved, bool? noJson, Property? property, string relativePath, FileHolder? resizedFileHolder, string sourceDirectoryFile, bool validImageFormatExtension) + public Item(bool? abandoned, bool? changed, List faces, FileHolder? imageFileHolder, bool? moved, bool? noJson, Property? property, string relativePath, FileHolder? resizedFileHolder, string sourceDirectoryFile, bool validImageFormatExtension) { _Abandoned = abandoned; _Changed = changed; - _Closest = closest; _Faces = faces; _ImageFileHolder = imageFileHolder; - _Mapping = mapping; _Moved = moved; _NoJson = noJson; _Property = property; @@ -54,8 +48,6 @@ public class Item : Properties.IItem public Item(string sourceDirectoryFile, string relativePath, FileHolder? imageFileInfo, bool isValidImageFormatExtension, Property? property, bool? abandoned, bool? changed) { _Faces = new(); - _Mapping = new(); - _Closest = new(); _Changed = changed; _Property = property; _Abandoned = abandoned; diff --git a/Shared/Models/Location.cs b/Shared/Models/Location.cs index ace41cd..193f09b 100644 --- a/Shared/Models/Location.cs +++ b/Shared/Models/Location.cs @@ -28,16 +28,16 @@ public class Location : Properties.ILocation, IEquatable _NormalizedPixelPercentage = normalizedPixelPercentage; _Right = right; _Top = top; - Check(bottom, left, normalizedPixelPercentage, right, top, zCount: 1); + Stateless.Methods.Location.Check(bottom, left, normalizedPixelPercentage, right, top, zCount: 1); } public Location(double confidence, int height, Location location, int width, int zCount) : - this(location.Bottom, confidence, location.Left, GetNormalizedPixelPercentage(location.Bottom, height, location.Left, location.Right, location.Top, width, zCount), location.Right, location.Top) => - Check(_Bottom, _Left, _NormalizedPixelPercentage, _Right, _Top, zCount); + this(location.Bottom, confidence, location.Left, Stateless.Methods.Location.GetNormalizedPixelPercentage(location.Bottom, height, location.Left, location.Right, location.Top, width, zCount), location.Right, location.Top) => + Stateless.Methods.Location.Check(_Bottom, _Left, _NormalizedPixelPercentage, _Right, _Top, zCount); public Location(int bottom, double confidence, int height, int left, int right, int top, int width, int zCount) : - this(bottom, confidence, left, GetNormalizedPixelPercentage(bottom, height, left, right, top, width, zCount), right, top) => - Check(_Bottom, height, _Left, _NormalizedPixelPercentage, _Right, _Top, width, zCount); + this(bottom, confidence, left, Stateless.Methods.Location.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, zCount), right, top) => + Stateless.Methods.Location.Check(_Bottom, height, _Left, _NormalizedPixelPercentage, _Right, _Top, width, zCount); public Location(double confidence, int factor, int height, Location location, int width, int zCount) { @@ -47,8 +47,8 @@ public class Location : Properties.ILocation, IEquatable int left = Math.Max(location.Left - x, 0); int right = Math.Min(location.Right + x, width); int top = Math.Max(location.Top - y, 0); - int normalizedPixelPercentage = GetNormalizedPixelPercentage(location.Bottom, height, location.Left, location.Right, location.Top, width, zCount); - Check(bottom, left, _NormalizedPixelPercentage, right, top, zCount); + int normalizedPixelPercentage = Stateless.Methods.Location.GetNormalizedPixelPercentage(location.Bottom, height, location.Left, location.Right, location.Top, width, zCount); + Stateless.Methods.Location.Check(bottom, left, _NormalizedPixelPercentage, right, top, zCount); _Confidence = confidence; _Bottom = bottom; _Left = left; @@ -65,53 +65,6 @@ public class Location : Properties.ILocation, IEquatable return result; } - private static void Check(int bottom, int left, int right, int top, int zCount) - { - if (left < 0) - throw new Exception(); - if (right < 0) - throw new Exception(); - if (right < left) - throw new Exception(); - if (top < 0) - throw new Exception(); - if (bottom < 0) - throw new Exception(); - if (bottom < top) - throw new Exception(); - if (zCount < 0) - throw new Exception(); - } - - private static void Check(int bottom, int height, int left, int right, int top, int width, int zCount) - { - if (bottom > height) - throw new Exception(); - if (left > width) - throw new Exception(); - if (right > width) - throw new Exception(); - if (top > height) - throw new Exception(); - if (zCount < 0) - throw new Exception(); - } - - private static void Check(int bottom, int left, int? normalizedPixelPercentage, int right, int top, int zCount) - { - Check(bottom, left, right, top, zCount); - if (normalizedPixelPercentage.HasValue && normalizedPixelPercentage.Value < 0) - throw new Exception(); - } - - private static void Check(int bottom, int height, int left, int? normalizedPixelPercentage, int right, int top, int width, int zCount) - { - Check(bottom, left, right, top, zCount); - Check(bottom, height, left, right, top, width, zCount); - if (normalizedPixelPercentage.HasValue && normalizedPixelPercentage.Value < 0) - throw new Exception(); - } - public override int GetHashCode() { int hashCode = -773114317; @@ -122,22 +75,6 @@ public class Location : Properties.ILocation, IEquatable return hashCode; } - public static int GetNormalizedPixelPercentage(int bottom, int height, int left, int right, int top, int width, int zCount) - { - int result; - double value; - double total = width * height; - Check(bottom, left, right, top, zCount); - double xCenter = left + ((right - left) / 2); - double yCenter = top + ((bottom - top) / 2); - double at = ((yCenter - 1) * width) + xCenter; - value = at / total; - if (value < 0) - value = 3; - result = (int)(Math.Round(value, Stateless.ILocation.Digits) * Stateless.ILocation.Factor); - return result; - } - public bool Equals(Location? location) { return location is not null diff --git a/Shared/Models/Mapping.cs b/Shared/Models/Mapping.cs index e57d7d5..77cbf26 100644 --- a/Shared/Models/Mapping.cs +++ b/Shared/Models/Mapping.cs @@ -3,52 +3,105 @@ using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; -public class Mapping : Properties.IMapping +public class MappingFromItem : Properties.IMappingFromItem { - protected int? _ApproximateYears; - protected readonly string _DisplayDirectoryName; - protected bool? _Filtered; - protected bool _Forced; - protected readonly int? _NormalizedPixelPercentage; - protected PersonBirthday _PersonBirthday; - protected readonly string _SegmentB; - public int? ApproximateYears => _ApproximateYears; - public string DisplayDirectoryName => _DisplayDirectoryName; - public bool? Filtered => _Filtered; - public bool Forced => _Forced; - public int? NormalizedPixelPercentage => _NormalizedPixelPercentage; - public PersonBirthday PersonBirthday => _PersonBirthday; - public string SegmentB => _SegmentB; + public int Id { init; get; } + public FileHolder ImageFileHolder { init; get; } + public bool? IsWrongYear { init; get; } + public DateTime MinimumDateTime { init; get; } + public FileHolder ResizedFileHolder { init; get; } [JsonConstructor] - public Mapping(int? approximateYears, string displayDirectoryName, bool? filtered, bool forced, int? normalizedPixelPercentage, PersonBirthday personBirthday, string segmentB) + public MappingFromItem(int id, FileHolder imageFileHolder, bool? isWrongYear, DateTime minimumDateTime, FileHolder resizedFileHolder) { - _ApproximateYears = approximateYears; - _DisplayDirectoryName = displayDirectoryName; - _Filtered = filtered; - _Forced = forced; - _NormalizedPixelPercentage = normalizedPixelPercentage; - _PersonBirthday = personBirthday; - _SegmentB = segmentB; + Id = id; + ImageFileHolder = imageFileHolder; + IsWrongYear = isWrongYear; + MinimumDateTime = minimumDateTime; + ResizedFileHolder = resizedFileHolder; } - public Mapping(int? approximateYears, string displayDirectoryName, bool forced, int? normalizedPixelPercentage, PersonBirthday personBirthday, string segmentB) : - this(approximateYears, displayDirectoryName, null, forced, normalizedPixelPercentage, personBirthday, segmentB) - { } - - public Mapping(int? approximateYears, string displayDirectoryName, bool forced, PersonBirthday personBirthday, string segmentB) : - this(approximateYears, displayDirectoryName, null, forced, null, personBirthday, segmentB) - { } - public override string ToString() { string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); return result; } - public void SetFiltered() => _Filtered = true; +} - public void SetFiltered(bool value) => _Filtered = value; +public class MappingFromLocation : Properties.IMappingFromLocation +{ + + public double Confidence { init; get; } + public string DeterministicHashCodeKeyDisplay { init; get; } + public int NormalizedPixelPercentage { init; get; } + + [JsonConstructor] + public MappingFromLocation(double confidence, string deterministicHashCodeKeyDisplay, int normalizedPixelPercentage) + { + Confidence = confidence; + DeterministicHashCodeKeyDisplay = deterministicHashCodeKeyDisplay; + NormalizedPixelPercentage = normalizedPixelPercentage; + } + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + +} + +public class MappingFromPerson : Properties.IMappingFromPerson +{ + + public int? ApproximateYears { init; get; } + public int? By { init; get; } + public string DisplayDirectoryName { init; get; } + public PersonBirthday? PersonBirthday { init; get; } + public string SegmentB { init; get; } + + [JsonConstructor] + public MappingFromPerson(int? approximateYears, int? by, string displayDirectoryName, PersonBirthday? personBirthday, string segmentB) + { + ApproximateYears = approximateYears; + By = by; + DisplayDirectoryName = displayDirectoryName; + PersonBirthday = personBirthday; + SegmentB = segmentB; + } + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + +} + +public class Mapping : Properties.IMapping +{ + + protected MappingFromPerson _MappingFromPerson; + public MappingFromItem MappingFromItem { init; get; } + public MappingFromLocation MappingFromLocation { init; get; } + public MappingFromPerson MappingFromPerson => _MappingFromPerson; + + [JsonConstructor] + public Mapping(MappingFromItem mappingFromItem, MappingFromLocation mappingFromLocation, MappingFromPerson mappingFromPerson) + { + MappingFromItem = mappingFromItem; + MappingFromLocation = mappingFromLocation; + _MappingFromPerson = mappingFromPerson; + } + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + + public void UpdateMappingFromPerson(int? approximateYears, int? by, string displayDirectoryName, PersonBirthday personBirthday, string segmentB) => _MappingFromPerson = new(approximateYears, by, displayDirectoryName, personBirthday, segmentB); } \ No newline at end of file diff --git a/Shared/Models/MappingContainer.cs b/Shared/Models/MappingContainer.cs deleted file mode 100644 index afbf6cc..0000000 --- a/Shared/Models/MappingContainer.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace View_by_Distance.Shared.Models; - -public class MappingContainer : Properties.IMappingContainer -{ - - protected double? _Distance; - protected readonly Face? _Face; - protected readonly int _Id; - protected readonly bool? _IsWrongYear; - protected readonly string _Key; - protected readonly Mapping _Mapping; - protected readonly DateTime _MinimumDateTime; - public double? Distance => _Distance; - public Face? Face => _Face; - public int Id => _Id; - public bool? IsWrongYear => _IsWrongYear; - public string Key => _Key; - public Mapping Mapping => _Mapping; - public DateTime MinimumDateTime => _MinimumDateTime; - - [JsonConstructor] - public MappingContainer(double? distance, Face? face, int id, bool? isWrongYear, string key, Mapping mapping, DateTime minimumDateTime) - { - _Distance = distance; - _Face = face; - _Id = id; - _IsWrongYear = isWrongYear; - _Key = key; - _Mapping = mapping; - _MinimumDateTime = minimumDateTime; - } - - public MappingContainer(Face face, int id, bool? isWrongYear, string key, Mapping mapping, DateTime minimumDateTime) : - this(null, face, id, isWrongYear, key, mapping, minimumDateTime) - { } - - public override string ToString() - { - string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); - return result; - } - - public void SetDistance(double v) => _Distance = v; - -} \ No newline at end of file diff --git a/Shared/Models/Methods/IClosest.cs b/Shared/Models/Methods/IClosest.cs deleted file mode 100644 index e5c1c14..0000000 --- a/Shared/Models/Methods/IClosest.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace View_by_Distance.Shared.Models.Methods; - -public interface IClosest : Stateless.Methods.IClosest -{ // ... - - // - -} \ No newline at end of file diff --git a/Shared/Models/Methods/INamed.cs b/Shared/Models/Methods/INamed.cs deleted file mode 100644 index 5086173..0000000 --- a/Shared/Models/Methods/INamed.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace View_by_Distance.Shared.Models.Methods; - -public interface INamed : Stateless.Methods.IMapping -{ // ... - - // - -} \ No newline at end of file diff --git a/Shared/Models/Properties/IClosest.cs b/Shared/Models/Properties/IClosest.cs deleted file mode 100644 index ed29d80..0000000 --- a/Shared/Models/Properties/IClosest.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace View_by_Distance.Shared.Models.Properties; - -public interface IClosest -{ - - public double Average { get; } - public bool? IsWrongYear { get; } - public Mapping Mapping { get; } - public double Minimum { get; } - public DateTime MinimumDateTime { get; } - public int NormalizedPixelPercentage { get; } - public long? TicksDelta { get; } - -} \ No newline at end of file diff --git a/Shared/Models/Properties/IFace.cs b/Shared/Models/Properties/IFace.cs index 204be53..9f68085 100644 --- a/Shared/Models/Properties/IFace.cs +++ b/Shared/Models/Properties/IFace.cs @@ -4,12 +4,12 @@ public interface IFace { public DateTime DateTime { get; } - public List FaceDistances { get; } + public FaceDistance? FaceDistance { get; } public FaceEncoding? FaceEncoding { get; } public Dictionary? FaceParts { get; } - public List? FaceNumbers { get; } public Location? Location { get; } public int? LocationIndex { get; } + public Mapping? Mapping { get; } public OutputResolution? OutputResolution { get; } public string RelativePath { get; } diff --git a/Shared/Models/Properties/IFaceDistance.cs b/Shared/Models/Properties/IFaceDistance.cs index b0a0f09..8bbbc96 100644 --- a/Shared/Models/Properties/IFaceDistance.cs +++ b/Shared/Models/Properties/IFaceDistance.cs @@ -3,10 +3,12 @@ namespace View_by_Distance.Shared.Models.Properties; public interface IFaceDistance { - public List Distances { get; } - public bool? IsWrongYear { get; } - public string Key { get; } - public Mapping Mapping { get; } - public DateTime MinimumDateTime { get; } - + public double? Confidence { init; get; } + public object? Encoding { init; get; } + public int Id { init; get; } + public bool? IsWrongYear { init; get; } + public double? Length { init; get; } + public DateTime? MinimumDateTime { init; get; } + public int? NormalizedPixelPercentage { init; get; } + } \ No newline at end of file diff --git a/Shared/Models/Properties/IFaceDistanceContainer.cs b/Shared/Models/Properties/IFaceDistanceContainer.cs new file mode 100644 index 0000000..320d30e --- /dev/null +++ b/Shared/Models/Properties/IFaceDistanceContainer.cs @@ -0,0 +1,9 @@ +namespace View_by_Distance.Shared.Models.Properties; + +public interface IFaceDistanceContainer +{ + + public Face Face { init; get; } + public FaceDistance FaceDistance { init; get; } + +} \ No newline at end of file diff --git a/Shared/Models/Properties/IItem.cs b/Shared/Models/Properties/IItem.cs index 422e8f5..4f48e82 100644 --- a/Shared/Models/Properties/IItem.cs +++ b/Shared/Models/Properties/IItem.cs @@ -5,10 +5,8 @@ public interface IItem public bool? Abandoned { get; } public bool? Changed { get; } - public List Closest { get; } public List Faces { get; } public FileHolder? ImageFileHolder { get; } - public List Mapping { get; } public bool? Moved { get; } public bool? NoJson { get; } public Property? Property { get; } diff --git a/Shared/Models/Properties/IMapping.cs b/Shared/Models/Properties/IMapping.cs index f7edbd4..85a0a31 100644 --- a/Shared/Models/Properties/IMapping.cs +++ b/Shared/Models/Properties/IMapping.cs @@ -1,14 +1,41 @@ namespace View_by_Distance.Shared.Models.Properties; +public interface IMappingFromItem +{ + + public int Id { init; get; } + public FileHolder ImageFileHolder { init; get; } + public bool? IsWrongYear { init; get; } + public DateTime MinimumDateTime { init; get; } + public FileHolder ResizedFileHolder { init; get; } + +} + +public interface IMappingFromLocation +{ + + public double Confidence { init; get; } + public string DeterministicHashCodeKeyDisplay { init; get; } + public int NormalizedPixelPercentage { init; get; } + +} + +public interface IMappingFromPerson +{ + + public int? ApproximateYears { init; get; } + public int? By { init; get; } + public string DisplayDirectoryName { init; get; } + public PersonBirthday? PersonBirthday { init; get; } + public string SegmentB { init; get; } + +} + public interface IMapping { - public int? ApproximateYears { get; } - public string DisplayDirectoryName { get; } - public bool? Filtered { get; } - public bool Forced { get; } - public int? NormalizedPixelPercentage { get; } - public PersonBirthday PersonBirthday { get; } - public string SegmentB { get; } + public MappingFromItem MappingFromItem { init; get; } + public MappingFromLocation MappingFromLocation { init; get; } + public MappingFromPerson MappingFromPerson { get; } } \ No newline at end of file diff --git a/Shared/Models/Properties/IMappingContainer.cs b/Shared/Models/Properties/IMappingContainer.cs deleted file mode 100644 index 570681a..0000000 --- a/Shared/Models/Properties/IMappingContainer.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace View_by_Distance.Shared.Models.Properties; - -public interface IMappingContainer -{ - - public double? Distance { get; } - public Face? Face { get; } - public int Id { get; } - public bool? IsWrongYear { get; } - public string Key { get; } - public Mapping Mapping { get; } - public DateTime MinimumDateTime { get; } - -} \ No newline at end of file diff --git a/Shared/Models/Properties/ISorting.cs b/Shared/Models/Properties/ISorting.cs index 264997f..ac28ff1 100644 --- a/Shared/Models/Properties/ISorting.cs +++ b/Shared/Models/Properties/ISorting.cs @@ -3,10 +3,10 @@ namespace View_by_Distance.Shared.Models.Properties; public interface ISorting { - public double? Confidence { init; get; } - public double? Distance { init; get; } - public int? FaceEncoding { init; get; } + public int DaysDelta { init; get; } + public int DistancePermyriad { init; get; } public int Id { init; get; } - public int? NormalizedPixelPercentage { init; get; } + public int NormalizedPixelPercentage { init; get; } + public int WithinRange { init; get; } } \ No newline at end of file diff --git a/Shared/Models/Properties/ISortingContainer.cs b/Shared/Models/Properties/ISortingContainer.cs new file mode 100644 index 0000000..655ad4c --- /dev/null +++ b/Shared/Models/Properties/ISortingContainer.cs @@ -0,0 +1,9 @@ +namespace View_by_Distance.Shared.Models.Properties; + +public interface ISortingContainer +{ + + public Face Face { init; get; } + public Sorting Sorting { init; get; } + +} \ No newline at end of file diff --git a/Shared/Models/Sorting.cs b/Shared/Models/Sorting.cs index 68a0d75..c2d4644 100644 --- a/Shared/Models/Sorting.cs +++ b/Shared/Models/Sorting.cs @@ -3,32 +3,25 @@ using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; -public class Sorting : Properties.ISorting +public record class Sorting : Properties.ISorting { - public double? Confidence { init; get; } - public double? Distance { init; get; } - public int? FaceEncoding { init; get; } + public int DaysDelta { init; get; } + public int DistancePermyriad { init; get; } public int Id { init; get; } - public int? NormalizedPixelPercentage { init; get; } + public int NormalizedPixelPercentage { init; get; } + public bool Older { init; get; } + public int WithinRange { init; get; } [JsonConstructor] - public Sorting(double? confidence, double? distance, int? faceEncoding, int id, int? normalizedPixelPercentage) + public Sorting(int daysDelta, int distancePermyriad, int id, int normalizedPixelPercentage, bool older, int withinRange) { - Confidence = confidence; - Distance = distance; - FaceEncoding = faceEncoding; - Id = id; - NormalizedPixelPercentage = normalizedPixelPercentage; - } - - public Sorting(double? confidence, int id, int? normalizedPixelPercentage) - { - Confidence = confidence; - Distance = null; - FaceEncoding = int.MaxValue; + DaysDelta = daysDelta; + DistancePermyriad = distancePermyriad; Id = id; NormalizedPixelPercentage = normalizedPixelPercentage; + Older = older; + WithinRange = withinRange; } public override string ToString() @@ -37,13 +30,4 @@ public class Sorting : Properties.ISorting return result; } - public Sorting(Sorting sorting, double? distance) - { - Confidence = sorting.Confidence; - Distance = distance; - FaceEncoding = null; - Id = sorting.Id; - NormalizedPixelPercentage = sorting.NormalizedPixelPercentage; - } - } \ No newline at end of file diff --git a/Shared/Models/SortingContainer.cs b/Shared/Models/SortingContainer.cs new file mode 100644 index 0000000..1ac3181 --- /dev/null +++ b/Shared/Models/SortingContainer.cs @@ -0,0 +1,25 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace View_by_Distance.Shared.Models; + +public record class SortingContainer : Properties.ISortingContainer +{ + + public Face Face { init; get; } + public Sorting Sorting { init; get; } + + [JsonConstructor] + public SortingContainer(Face face, Sorting sorting) + { + Face = face; + Sorting = sorting; + } + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/IClosest.cs b/Shared/Models/Stateless/IClosest.cs deleted file mode 100644 index 02c993a..0000000 --- a/Shared/Models/Stateless/IClosest.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace View_by_Distance.Shared.Models.Stateless; - -public interface IClosest -{ - - // 637972153144596958 - // const int Digits = 3; - // const int Factor = 1000; - // const int MaximumPer = 50; - // const bool SkipIsWrongYear = false; - - const int Digits = 3; - const int Factor = 1000; - const int MaximumPer = 50; - const double Tolerance = 0.6d; - -} \ No newline at end of file diff --git a/Shared/Models/Stateless/IFaceDistance.cs b/Shared/Models/Stateless/IFaceDistance.cs index 889a894..4b4309d 100644 --- a/Shared/Models/Stateless/IFaceDistance.cs +++ b/Shared/Models/Stateless/IFaceDistance.cs @@ -6,6 +6,7 @@ public interface IFaceDistance // const int MaximumPer = 999; const int HiddenImageFactor = 2; - const int MaximumPer = 9999; + const int Permyriad = 10000; + const double Tolerance = 0.2d; } \ No newline at end of file diff --git a/Shared/Models/Stateless/ILocation.cs b/Shared/Models/Stateless/ILocation.cs index c6965d0..f65c625 100644 --- a/Shared/Models/Stateless/ILocation.cs +++ b/Shared/Models/Stateless/ILocation.cs @@ -5,7 +5,6 @@ public interface ILocation const double MinimumConfidence = 0.8d; const int Digits = 6; - const double Tolerance = 0.6d; const int Factor = 1000000; } \ No newline at end of file diff --git a/Shared/Models/Stateless/IMapping.cs b/Shared/Models/Stateless/IMapping.cs index 56d156f..f403ea1 100644 --- a/Shared/Models/Stateless/IMapping.cs +++ b/Shared/Models/Stateless/IMapping.cs @@ -10,5 +10,6 @@ public interface IMapping const bool UseDeterministicHashCodeUnknownFaceKeyValuePairsForAddToMapping = false; const bool UseDeterministicHashCodeUnknownFaceKeyValuePairsForSaveMapping = false; const bool SaveFaceEncoding = false; + const bool SaveMapped = false; } \ No newline at end of file diff --git a/Shared/Models/Stateless/ISorting.cs b/Shared/Models/Stateless/ISorting.cs new file mode 100644 index 0000000..dd06d51 --- /dev/null +++ b/Shared/Models/Stateless/ISorting.cs @@ -0,0 +1,13 @@ +namespace View_by_Distance.Shared.Models.Stateless; + +public interface ISorting +{ + + const int FacesToSkipAfterSortBeforeLoad = 0; + const int FacesToTakeAfterSortBeforeLoad = 21000; + const int MaximumPerFaceShouldBeHigh = 1000; + const int DaysDeltaTolerance = 700; + const int MaximumPerKey = 27; + const int Sigma = 3; + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Age.cs b/Shared/Models/Stateless/Methods/Age.cs index 08e17a3..f7fe36c 100644 --- a/Shared/Models/Stateless/Methods/Age.cs +++ b/Shared/Models/Stateless/Methods/Age.cs @@ -3,19 +3,31 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Age { - internal static (int, TimeSpan) GetAge(DateTime minuend, DateTime subtrahend) + internal static (int, TimeSpan) GetAge(long minuendTicks, long subtrahendTicks) { TimeSpan result; int years = 0; - DateTime check = new(subtrahend.Ticks); + DateTime check = new(subtrahendTicks); for (int i = 0; i < int.MaxValue; i++) { check = check.AddYears(1); - if (check > minuend) + if (check.Ticks > minuendTicks) break; years += 1; } - result = new(minuend.Ticks - check.AddYears(-1).Ticks); + result = new(minuendTicks - check.AddYears(-1).Ticks); + return (years, result); + } + + internal static (int, TimeSpan) GetAge(long minuendTicks, DateTime subtrahend) + { + (int years, TimeSpan result) = GetAge(minuendTicks, subtrahend.Ticks); + return (years, result); + } + + internal static (int, TimeSpan) GetAge(DateTime minuend, DateTime subtrahend) + { + (int years, TimeSpan result) = GetAge(minuend.Ticks, subtrahend.Ticks); return (years, result); } diff --git a/Shared/Models/Stateless/Methods/Closest.cs b/Shared/Models/Stateless/Methods/Closest.cs deleted file mode 100644 index b79e311..0000000 --- a/Shared/Models/Stateless/Methods/Closest.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace View_by_Distance.Shared.Models.Stateless.Methods; - -internal abstract class Closest -{ - - private static int Get(double rawAverage) => (int)(Math.Round(rawAverage, Stateless.IClosest.Digits) * Stateless.ILocation.Factor); - - private static Models.Closest Get(Models.Face face, DateTime minimumDateTime, FaceDistance faceDistance) - { - Models.Closest result; - long? ticksDelta; - double minimum = faceDistance.Distances.Min(); - double rawAverage = faceDistance.Distances.Average(); - bool aboveTolerance = minimum >= Stateless.IClosest.Tolerance || rawAverage >= Stateless.IClosest.Tolerance; - if (faceDistance.IsWrongYear is null || faceDistance.IsWrongYear.Value) - ticksDelta = null; - else - { - ticksDelta = Math.Abs(faceDistance.MinimumDateTime.Ticks - minimumDateTime.Ticks); - if (faceDistance.MinimumDateTime < faceDistance.Mapping.PersonBirthday.Value) - ticksDelta *= 2; - else if (faceDistance.Mapping.ApproximateYears.HasValue && faceDistance.MinimumDateTime < DateTime.Now.AddYears(-faceDistance.Mapping.ApproximateYears.Value)) - ticksDelta *= 2; - } - if (face.Location?.NormalizedPixelPercentage is null) - throw new NullReferenceException(nameof(face.Location.NormalizedPixelPercentage)); - int average = Get(rawAverage); - result = new(aboveTolerance, average, face.Location.NormalizedPixelPercentage.Value, faceDistance.IsWrongYear, faceDistance.Mapping, minimum, faceDistance.MinimumDateTime, ticksDelta); - return result; - } - - internal static Models.Closest[] GetCollection(Models.Face face, DateTime minimumDateTime, List faceDistances) - { - Models.Closest[] results; - Models.Closest[] closestCollection = (from l in faceDistances select Get(face, minimumDateTime, l)).ToArray(); - results = (from l in closestCollection orderby l.Average, l.TicksDelta.HasValue, l.TicksDelta select l).ToArray(); - return results; - } -} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Face.cs b/Shared/Models/Stateless/Methods/Face.cs index 7345348..6958b7d 100644 --- a/Shared/Models/Stateless/Methods/Face.cs +++ b/Shared/Models/Stateless/Methods/Face.cs @@ -37,21 +37,6 @@ internal abstract class Face return jsonElements; } - internal static List GetSortingCollection(Models.Face face) - { - List results = new(); - Models.Sorting sorting; - if (face.FaceNumbers is not null) - { - foreach (int[] numbers in face.FaceNumbers) - { - sorting = Sorting.Get(numbers); - results.Add(sorting); - } - } - return results; - } - private static List GetFaces(string jsonFileFullName, int? maximum) { List results = new(); diff --git a/Shared/Models/Stateless/Methods/FaceDistance.cs b/Shared/Models/Stateless/Methods/FaceDistance.cs new file mode 100644 index 0000000..0a6b837 --- /dev/null +++ b/Shared/Models/Stateless/Methods/FaceDistance.cs @@ -0,0 +1,8 @@ +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +internal abstract class FaceDistance +{ + + internal static double GetDefaultValue() => throw new Exception(); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/FaceDistanceContainer.cs b/Shared/Models/Stateless/Methods/FaceDistanceContainer.cs new file mode 100644 index 0000000..451aa42 --- /dev/null +++ b/Shared/Models/Stateless/Methods/FaceDistanceContainer.cs @@ -0,0 +1,6 @@ +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +internal abstract class FaceDistanceContainer +{ + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IAge.cs b/Shared/Models/Stateless/Methods/IAge.cs index c74d6c2..4892486 100644 --- a/Shared/Models/Stateless/Methods/IAge.cs +++ b/Shared/Models/Stateless/Methods/IAge.cs @@ -3,6 +3,12 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface IAge { // ... + (int, TimeSpan) TestStatic_GetAge(long minuendTicks, long subtrahendTicks); + static (int, TimeSpan) GetAge(long minuendTicks, long subtrahendTicks) => Age.GetAge(minuendTicks, subtrahendTicks); + + (int, TimeSpan) TestStatic_GetAge(long minuendTicks, DateTime subtrahend); + static (int, TimeSpan) GetAge(long minuendTicks, DateTime subtrahend) => Age.GetAge(minuendTicks, subtrahend); + (int, TimeSpan) TestStatic_GetAge(DateTime minuend, DateTime subtrahend); static (int, TimeSpan) GetAge(DateTime minuend, DateTime subtrahend) => Age.GetAge(minuend, subtrahend); diff --git a/Shared/Models/Stateless/Methods/IClosest.cs b/Shared/Models/Stateless/Methods/IClosest.cs deleted file mode 100644 index 027faad..0000000 --- a/Shared/Models/Stateless/Methods/IClosest.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace View_by_Distance.Shared.Models.Stateless.Methods; - -public interface IClosest -{ // ... - - Models.Closest[] TestStatic_Get(List faceDistances); - - static Models.Closest[] GetCollection(Models.Face face, DateTime minimumDateTime, List faceDistances) => Closest.GetCollection(face, minimumDateTime, faceDistances); - -} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IFace.cs b/Shared/Models/Stateless/Methods/IFace.cs index 554c7d9..777bda5 100644 --- a/Shared/Models/Stateless/Methods/IFace.cs +++ b/Shared/Models/Stateless/Methods/IFace.cs @@ -18,9 +18,6 @@ public interface IFace Models.Face[] TestStatic_Getα(Dictionary faceParts); static double? Getα(Dictionary faceParts) => Face.Getα(faceParts); - List TestStatic_GetSortingCollection(Models.Face face); - static List GetSortingCollection(Models.Face face) => Face.GetSortingCollection(face); - int?[] TestStatic_GetInts(List faces); static int?[] GetInts(List faces) => (from l in faces where l.FaceEncoding is not null && l.Location?.NormalizedPixelPercentage is not null select l.Location?.NormalizedPixelPercentage).ToArray(); diff --git a/Shared/Models/Stateless/Methods/IFaceDistance.cs b/Shared/Models/Stateless/Methods/IFaceDistance.cs new file mode 100644 index 0000000..ded8664 --- /dev/null +++ b/Shared/Models/Stateless/Methods/IFaceDistance.cs @@ -0,0 +1,10 @@ +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +public interface IFaceDistance +{ // ... + + double TestStatic_GetDefaultValue(); + + static double GetDefaultValue() => FaceDistance.GetDefaultValue(); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IFaceDistanceContainer.cs b/Shared/Models/Stateless/Methods/IFaceDistanceContainer.cs new file mode 100644 index 0000000..2e5c2c4 --- /dev/null +++ b/Shared/Models/Stateless/Methods/IFaceDistanceContainer.cs @@ -0,0 +1,6 @@ +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +public interface IFaceDistanceContainer +{ // ... + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IFaceFileSystem.cs b/Shared/Models/Stateless/Methods/IFaceFileSystem.cs index 2af43dc..9029043 100644 --- a/Shared/Models/Stateless/Methods/IFaceFileSystem.cs +++ b/Shared/Models/Stateless/Methods/IFaceFileSystem.cs @@ -3,10 +3,14 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface IFaceFileSystem : IFileSystem { // ... - void TestStatic_SearchForMissingFile(string fullFileName, FileInfo fileInfo, string dataFullFileName); - Models.FaceFileSystem[][] TestStatic_GetFaceFileSystemCollection(string requestPath, string rootResultsDirectory, (string RootResultsDirectoryAbsoluteUri, string C_ResizeContentDirectory, string D_FacesContentDirectory, string E_DistanceCollectionDirectory) tuple, string selectedFileFullName); + void TestStatic_SearchForMissingFile(string fullFileName, FileInfo fileInfo, string dataFullFileName) => + SearchForMissingFile(fullFileName, fileInfo, dataFullFileName); + static void SearchForMissingFile(string fullFileName, FileInfo fileInfo, string dataFullFileName) => + FaceFileSystem.SearchForMissingFile(fullFileName, fileInfo, dataFullFileName); - static void SearchForMissingFile(string fullFileName, FileInfo fileInfo, string dataFullFileName) => FaceFileSystem.SearchForMissingFile(fullFileName, fileInfo, dataFullFileName); - static Models.FaceFileSystem[] GetFaceFileSystemCollection(string requestPath, (string RootResultsDirectoryAbsoluteUri, string C_ResizeContentDirectory, string D_FacesContentDirectory, string E_DistanceCollectionDirectory) tuple, string selectedFileFullName) => FaceFileSystem.GetFaceFileSystemCollection(requestPath, tuple, selectedFileFullName); + Models.FaceFileSystem[] TestStatic_GetFaceFileSystemCollection(string requestPath, string rootResultsDirectory, (string RootResultsDirectoryAbsoluteUri, string C_ResizeContentDirectory, string D_FacesContentDirectory, string E_DistanceCollectionDirectory) tuple, string selectedFileFullName) => + GetFaceFileSystemCollection(requestPath, tuple, selectedFileFullName); + static Models.FaceFileSystem[] GetFaceFileSystemCollection(string requestPath, (string RootResultsDirectoryAbsoluteUri, string C_ResizeContentDirectory, string D_FacesContentDirectory, string E_DistanceCollectionDirectory) tuple, string selectedFileFullName) => + FaceFileSystem.GetFaceFileSystemCollection(requestPath, tuple, selectedFileFullName); } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IIMapping.cs b/Shared/Models/Stateless/Methods/IIMapping.cs deleted file mode 100644 index 2ee2df5..0000000 --- a/Shared/Models/Stateless/Methods/IIMapping.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace View_by_Distance.Shared.Models.Stateless.Methods; - -public interface IMapping -{ // ... - - double? TestStatic_GetReversedDeterministicHashCodeKey(string file); - static double? GetReversedDeterministicHashCodeKey(bool keyValuePairsAny, Dictionary> keyValuePairs, string file) => - Mapping.GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); - - double TestStatic_GetDeterministicHashCodeKey(Models.Item item, Models.Face face); - static double GetDeterministicHashCodeKey(Models.Item item, Models.Face face) => - Mapping.GetDeterministicHashCodeKey(item, face); - - double TestStatic_GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest); - static double GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest) => - Mapping.GetDeterministicHashCodeKey(item, closest); - -} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/ILocation.cs b/Shared/Models/Stateless/Methods/ILocation.cs index 1ffca1a..dbbf844 100644 --- a/Shared/Models/Stateless/Methods/ILocation.cs +++ b/Shared/Models/Stateless/Methods/ILocation.cs @@ -3,15 +3,24 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface ILocation { // ... - Models.Location? TestStatic_GetLocation(Models.Location? location, int height, int width, int zCount); + Models.Location? TestStatic_GetLocation(Models.Location? location, int height, int width, int zCount) => + GetLocation(location, height, width, zCount); static Models.Location? GetLocation(Models.Location? location, int height, int width, int zCount) => location is null ? null : new(location.Confidence, height, location, width, zCount); - Models.Location? TestStatic_GetLocation(int factor, Models.Location? location, int height, int width, int zCount); + Models.Location? TestStatic_GetLocation(int factor, Models.Location? location, int height, int width, int zCount) => + GetLocation(location, height, width, zCount); static Models.Location? GetLocation(int factor, Models.Location? location, int height, int width, int zCount) => location is null ? null : new(location.Confidence, factor, height, location, width, zCount); - int?[] TestStatic_GetInts(List locations); - static int?[] GetInts(List locations) => (from l in locations where l.NormalizedPixelPercentage is not null select l.NormalizedPixelPercentage).ToArray(); + int?[] TestStatic_GetInts(List locations) => + GetInts(locations); + static int?[] GetInts(List locations) => + (from l in locations where l.NormalizedPixelPercentage is not null select l.NormalizedPixelPercentage).ToArray(); + + int TestStatic_GetNormalizedPixelPercentage(int bottom, int height, int left, int right, int top, int width, int zCount) => + GetNormalizedPixelPercentage(bottom, height, left, right, top, width, zCount); + static int GetNormalizedPixelPercentage(int bottom, int height, int left, int right, int top, int width, int zCount) => + Location.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, zCount); } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IMapping.cs b/Shared/Models/Stateless/Methods/IMapping.cs new file mode 100644 index 0000000..04ca8f7 --- /dev/null +++ b/Shared/Models/Stateless/Methods/IMapping.cs @@ -0,0 +1,17 @@ +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +public interface IMapping +{ // ... + + static string GetDeterministicHashCodeKeyDisplay(int id, int normalizedPixelPercentage) + => $"{id}.{normalizedPixelPercentage}"; + + static double GetDeterministicHashCodeKeyValue(int id, int normalizedPixelPercentage) + => Math.Round(double.Parse($"{id}.{normalizedPixelPercentage}"), Stateless.ILocation.Digits); + + (int?, int?) TestStatic_GetReversedDeterministicHashCodeKey(bool keyValuePairsAny, Dictionary> keyValuePairs, string file) => + GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); + static (int?, int?) GetReversedDeterministicHashCodeKey(bool keyValuePairsAny, Dictionary> keyValuePairs, string file) => + Mapping.GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IPersonBirthday.cs b/Shared/Models/Stateless/Methods/IPersonBirthday.cs index f161edb..23bc2e9 100644 --- a/Shared/Models/Stateless/Methods/IPersonBirthday.cs +++ b/Shared/Models/Stateless/Methods/IPersonBirthday.cs @@ -11,7 +11,8 @@ public interface IPersonBirthday // ... - double? TestStatic_GetAge(Models.PersonBirthday birthday); + double? TestStatic_GetAge(Models.PersonBirthday birthday) => + GetAge(birthday); static double? GetAge(Models.PersonBirthday birthday) => PersonBirthday.GetAge(birthday); @@ -30,10 +31,16 @@ public interface IPersonBirthday static string GetFileName(Models.PersonBirthday personBirthday) => PersonBirthday.GetFileName(personBirthday); - (int, TimeSpan) TestStatic_GetAge(DateTime dateTime, Models.PersonBirthday birthday); + (int, TimeSpan) TestStatic_GetAge(DateTime dateTime, Models.PersonBirthday birthday) => + GetAge(dateTime, birthday); static (int, TimeSpan) GetAge(DateTime dateTime, Models.PersonBirthday birthday) => PersonBirthday.GetAge(dateTime, birthday); + (int, TimeSpan) TestStatic_GetAge(long dateTimeTicks, Models.PersonBirthday birthday) => + GetAge(dateTimeTicks, birthday); + static (int, TimeSpan) GetAge(long dateTimeTicks, Models.PersonBirthday birthday) => + PersonBirthday.GetAge(dateTimeTicks, birthday); + string TestStatic_GetFormatted(Models.PersonBirthday personBirthday) => PersonBirthday.GetFormatted(personBirthday); static string GetFormatted(Models.PersonBirthday personBirthday) => @@ -44,16 +51,25 @@ public interface IPersonBirthday static Models.PersonBirthday? GetPersonBirthday(string personKey) => PersonBirthday.GetPersonBirthday(personKey); + bool TestStatic_IsCounterPersonBirthday(Models.PersonBirthday personBirthday); + static bool IsCounterPersonBirthday(Models.PersonBirthday personBirthday) => + PersonBirthday.IsCounterPersonBirthday(personBirthday); + Models.PersonBirthday TestStatic_GetNextBirthDate(Properties.IStorage storage) => PersonBirthday.GetNextBirthDate(storage); static Models.PersonBirthday GetNextBirthDate(Properties.IStorage storage) => PersonBirthday.GetNextBirthDate(storage); - TimeSpan? TestStatic_Get(DateTime now, Models.PersonBirthday personBirthday) => - PersonBirthday.GetTimeSpan(now, isWrongYear: false, personBirthday); + TimeSpan? TestStatic_Get(DateTime minimumDateTime, Models.PersonBirthday personBirthday) => + PersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear: false, personBirthday); static TimeSpan? GetTimeSpan(DateTime minimumDateTime, Models.PersonBirthday personBirthday) => PersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear: false, personBirthday); + TimeSpan? TestStatic_Get(long minimumDateTimeTicks, bool? isWrongYear, Models.PersonBirthday personBirthday) => + PersonBirthday.GetTimeSpan(minimumDateTimeTicks, isWrongYear, personBirthday); + static TimeSpan? GetTimeSpan(long minimumDateTimeTicks, bool? isWrongYear, Models.PersonBirthday personBirthday) => + PersonBirthday.GetTimeSpan(minimumDateTimeTicks, isWrongYear, personBirthday); + string TestStatic_GetFileFullName(Properties.IStorage storage, Models.PersonBirthday personBirthday) => PersonBirthday.GetFileFullName(storage, personBirthday); static string GetFileFullName(Properties.IStorage storage, Models.PersonBirthday personBirthday) => @@ -69,4 +85,8 @@ public interface IPersonBirthday static TimeSpan? GetTimeSpan(DateTime minimumDateTime, bool? isWrongYear, Models.PersonBirthday personBirthday) => PersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, personBirthday); + bool TestStatic_IsWrongYearFilterOrCounterPersonBirthday(bool? isWrongYear, Models.PersonBirthday personBirthday); + static bool IsWrongYearFilterOrCounterPersonBirthday(bool? isWrongYear, Models.PersonBirthday personBirthday) => + PersonBirthday.IsWrongYearFilterOrCounterPersonBirthday(isWrongYear, personBirthday); + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/ISorting.cs b/Shared/Models/Stateless/Methods/ISorting.cs index cdbc192..4697872 100644 --- a/Shared/Models/Stateless/Methods/ISorting.cs +++ b/Shared/Models/Stateless/Methods/ISorting.cs @@ -3,10 +3,14 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface ISorting { // ... - Models.Sorting TestStatic_GetSorting(int[] numbers); - static Models.Sorting GetSorting(int[] numbers) => Sorting.Get(numbers); + Models.Sorting[] TestStatic_Sort(List collection) => + Sort(collection); + static Models.Sorting[] Sort(List collection) => + (from l in collection orderby l.WithinRange, l.DistancePermyriad, l.DaysDelta select l).ToArray(); - List TestStatic_GetFaceNumbers(List collection); - static List GetFaceNumbers(List collection) => Sorting.GetFaceNumbers(collection); + Models.Sorting TestStatic_Get(Models.FaceDistance faceDistanceEncoding, Models.FaceDistance faceDistanceLength, bool anyLowerThanTolerance, List<(long lcl, long minimum, long maximum, long ucl)> personKeysRangesCollection) => + Get(faceDistanceEncoding, faceDistanceLength, anyLowerThanTolerance, personKeysRangesCollection); + static Models.Sorting Get(Models.FaceDistance faceDistanceEncoding, Models.FaceDistance faceDistanceLength, bool anyLowerThanTolerance, List<(long lcl, long minimum, long maximum, long ucl)> personKeysRangesCollection) => + Sorting.Get(faceDistanceEncoding, faceDistanceLength, anyLowerThanTolerance, personKeysRangesCollection); } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/ISortingContainer.cs b/Shared/Models/Stateless/Methods/ISortingContainer.cs new file mode 100644 index 0000000..ed1ea2c --- /dev/null +++ b/Shared/Models/Stateless/Methods/ISortingContainer.cs @@ -0,0 +1,11 @@ +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +public interface ISortingContainer +{ // ... + + Models.SortingContainer[] TestStatic_Sort(List collection) => + Sort(collection); + static Models.SortingContainer[] Sort(List collection) => + (from l in collection orderby l.Sorting.WithinRange, l.Sorting.DistancePermyriad, l.Sorting.DaysDelta select l).ToArray(); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Location.cs b/Shared/Models/Stateless/Methods/Location.cs index 67a1a7e..4815400 100644 --- a/Shared/Models/Stateless/Methods/Location.cs +++ b/Shared/Models/Stateless/Methods/Location.cs @@ -3,4 +3,67 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Location { + internal static int GetNormalizedPixelPercentage(int bottom, int height, int left, int right, int top, int width, int zCount) + { + int result; + double value; + double total = width * height; + Check(bottom, left, right, top, zCount); + double xCenter = left + ((right - left) / 2); + double yCenter = top + ((bottom - top) / 2); + double at = ((yCenter - 1) * width) + xCenter; + value = at / total; + if (value < 0) + value = 3; + result = (int)(Math.Round(value, Stateless.ILocation.Digits) * Stateless.ILocation.Factor); + return result; + } + + internal static void Check(int bottom, int left, int right, int top, int zCount) + { + if (left < 0) + throw new Exception(); + if (right < 0) + throw new Exception(); + if (right < left) + throw new Exception(); + if (top < 0) + throw new Exception(); + if (bottom < 0) + throw new Exception(); + if (bottom < top) + throw new Exception(); + if (zCount < 0) + throw new Exception(); + } + + internal static void Check(int bottom, int height, int left, int right, int top, int width, int zCount) + { + if (bottom > height) + throw new Exception(); + if (left > width) + throw new Exception(); + if (right > width) + throw new Exception(); + if (top > height) + throw new Exception(); + if (zCount < 0) + throw new Exception(); + } + + internal static void Check(int bottom, int left, int? normalizedPixelPercentage, int right, int top, int zCount) + { + Check(bottom, left, right, top, zCount); + if (normalizedPixelPercentage.HasValue && normalizedPixelPercentage.Value < 0) + throw new Exception(); + } + + internal static void Check(int bottom, int height, int left, int? normalizedPixelPercentage, int right, int top, int width, int zCount) + { + Check(bottom, left, right, top, zCount); + Check(bottom, height, left, right, top, width, zCount); + if (normalizedPixelPercentage.HasValue && normalizedPixelPercentage.Value < 0) + throw new Exception(); + } + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Mapping.cs b/Shared/Models/Stateless/Methods/Mapping.cs index 906ce5f..c5d993b 100644 --- a/Shared/Models/Stateless/Methods/Mapping.cs +++ b/Shared/Models/Stateless/Methods/Mapping.cs @@ -5,24 +5,11 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Mapping { - private static double GetDeterministicHashCodeKey(int id, int normalizedPixelPercentage) - => Math.Round(double.Parse($"{id}.{normalizedPixelPercentage}"), Stateless.ILocation.Digits); - - internal static double GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest) - { - double result; - if (item.Property?.Id is null || item.ImageFileHolder is null) - throw new NullReferenceException(); - result = GetDeterministicHashCodeKey(item.Property.Id.Value, closest.NormalizedPixelPercentage); - return result; - } - internal static double GetDeterministicHashCodeKey(Models.Item item, Models.Face face) { - double result; if (item.Property?.Id is null || item.ImageFileHolder is null || face.Location?.NormalizedPixelPercentage is null) throw new NullReferenceException(); - result = GetDeterministicHashCodeKey(item.Property.Id.Value, face.Location.NormalizedPixelPercentage.Value); + double result = IMapping.GetDeterministicHashCodeKeyValue(item.Property.Id.Value, face.Location.NormalizedPixelPercentage.Value); return result; } @@ -37,7 +24,7 @@ internal abstract class Mapping { if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) continue; - if (normalizedPixelPercentageValue != face.Location.NormalizedPixelPercentage.Value && deterministicHashCodeKey != GetDeterministicHashCodeKey(id, face.Location.NormalizedPixelPercentage.Value)) + if (normalizedPixelPercentageValue != face.Location.NormalizedPixelPercentage.Value && deterministicHashCodeKey != IMapping.GetDeterministicHashCodeKeyValue(id, face.Location.NormalizedPixelPercentage.Value)) continue; collection.Add(face); } @@ -56,36 +43,46 @@ internal abstract class Mapping } } - private static double? GetReversedDeterministicHashCodeFromSegments(bool keyValuePairsAny, Dictionary> keyValuePairs, string file, string[] segments) + private static (int?, int?, double?) GetReversedDeterministicHashCodeKeysFromSegments(bool keyValuePairsAny, Dictionary> keyValuePairs, string file, string[] segments) { double? result; if (segments.Length != 3) throw new Exception(); string id = segments[0]; + int normalizedPixelPercentageValue; string normalizedPixelPercentage = segments[1]; - if (!int.TryParse(id, out int idValue) || !int.TryParse(normalizedPixelPercentage, out int normalizedPixelPercentageValue)) + if (!int.TryParse(id, out int idValue) | !int.TryParse(normalizedPixelPercentage, out normalizedPixelPercentageValue)) result = null; else { - result = GetDeterministicHashCodeKey(idValue, normalizedPixelPercentageValue); + result = IMapping.GetDeterministicHashCodeKeyValue(idValue, normalizedPixelPercentageValue); if (keyValuePairsAny && keyValuePairs.ContainsKey(idValue)) UseKeyValuePairsSaveFaceEncoding(keyValuePairs, file, idValue, normalizedPixelPercentageValue, result.Value, $".{segments[2]}"); } - return result; + return new(idValue, normalizedPixelPercentageValue, result); } - internal static double? GetReversedDeterministicHashCodeKey(bool keyValuePairsAny, Dictionary> keyValuePairs, string file) + internal static (int?, int?) GetReversedDeterministicHashCodeKey(bool keyValuePairsAny, Dictionary> keyValuePairs, string file) { - double? result; + int? id; + int? normalizedPixelPercentage; string fileName = Path.GetFileName(file); if (fileName.Length < 2 || fileName[1..].Contains('-')) - result = null; + { + id = null; + normalizedPixelPercentage = null; + } else { string[] segments = fileName.Split('.'); - result = GetReversedDeterministicHashCodeFromSegments(keyValuePairsAny, keyValuePairs, file, segments); + (id, normalizedPixelPercentage, double? result) = GetReversedDeterministicHashCodeKeysFromSegments(keyValuePairsAny, keyValuePairs, file, segments); + if (result is null) + { + id = null; + normalizedPixelPercentage = null; + } } - return result; + return new(id, normalizedPixelPercentage); } } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/PersonBirthday.cs b/Shared/Models/Stateless/Methods/PersonBirthday.cs index 8abae10..649a77a 100644 --- a/Shared/Models/Stateless/Methods/PersonBirthday.cs +++ b/Shared/Models/Stateless/Methods/PersonBirthday.cs @@ -26,16 +26,53 @@ internal abstract class PersonBirthday return result; } - internal static TimeSpan? GetTimeSpan(DateTime minimumDateTime, bool? isWrongYear, Models.PersonBirthday personBirthday) + internal static bool IsCounterPersonBirthday(Models.PersonBirthday personBirthday) + { + bool result; + if (personBirthday.Value.Year < 1900) + result = true; + else + result = false; + return result; + } + + internal static bool IsWrongYearFilterOrCounterPersonBirthday(bool? isWrongYear, Models.PersonBirthday personBirthday) + { + bool result; + if (isWrongYear is null || isWrongYear.Value || IsCounterPersonBirthday(personBirthday)) + result = true; + else + result = false; + return result; + } + + internal static TimeSpan? GetTimeSpan(long minimumDateTimeTicks, bool? isWrongYear, Models.PersonBirthday personBirthday) { TimeSpan? timeSpan; - if (isWrongYear is null || isWrongYear.Value || personBirthday.Value.Year < 1900) + bool isWrongYearFilterOrCounterPersonBirthday = IsWrongYearFilterOrCounterPersonBirthday(isWrongYear, personBirthday); + if (isWrongYearFilterOrCounterPersonBirthday) timeSpan = null; else - timeSpan = new(minimumDateTime.Ticks - personBirthday.Value.Ticks); + timeSpan = new(minimumDateTimeTicks - personBirthday.Value.Ticks); return timeSpan; } + internal static TimeSpan? GetTimeSpan(DateTime minimumDateTime, bool? isWrongYear, Models.PersonBirthday personBirthday) + { + TimeSpan? timeSpan = GetTimeSpan(minimumDateTime.Ticks, isWrongYear, personBirthday); + return timeSpan; + } + + internal static (int, TimeSpan) GetAge(long dateTimeTicks, Models.PersonBirthday birthday) + { + TimeSpan result; + int years; + if (birthday?.Value is null) + throw new NullReferenceException(nameof(birthday.Value)); + (years, result) = Age.GetAge(dateTimeTicks, birthday.Value); + return (years, result); + } + internal static (int, TimeSpan) GetAge(DateTime dateTime, Models.PersonBirthday birthday) { TimeSpan result; diff --git a/Shared/Models/Stateless/Methods/Sorting.cs b/Shared/Models/Stateless/Methods/Sorting.cs index a30fdc4..e9065ea 100644 --- a/Shared/Models/Stateless/Methods/Sorting.cs +++ b/Shared/Models/Stateless/Methods/Sorting.cs @@ -3,48 +3,47 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Sorting { - internal static Models.Sorting Get(int[] collection) + internal static Models.Sorting Get(Models.FaceDistance faceDistanceEncoding, Models.FaceDistance faceDistanceLength, bool anyLowerThanTolerance, List<(long lcl, long minimum, long maximum, long ucl)> personKeysRangesCollection) { - if (collection.Length != 5) - throw new Exception(); Models.Sorting result; - int id = collection[3]; - int distance = collection[1]; - int confidence = collection[2]; - int faceEncoding = collection[0]; - int normalizedPixelPercentage = collection[4]; - result = new(confidence, distance, faceEncoding, id, normalizedPixelPercentage); + if (faceDistanceLength.Length is null) + throw new NotSupportedException(); + if (faceDistanceLength.NormalizedPixelPercentage is null) + throw new NotSupportedException(); + bool older; + int daysDelta; + int withinRange; + int distancePermyriad; + if (faceDistanceLength.Length.Value == 0 || (anyLowerThanTolerance && faceDistanceLength.Length.Value >= Stateless.IFaceDistance.Tolerance)) + { + older = false; + daysDelta = 0; + withinRange = 0; + distancePermyriad = 0; + } + else + { + if (faceDistanceEncoding.MinimumDateTime is null || faceDistanceLength.MinimumDateTime is null) + throw new NotSupportedException(); + List withinRanges = new(); + long ticks = faceDistanceEncoding.MinimumDateTime.Value.Ticks; + TimeSpan timeSpan = new(faceDistanceLength.MinimumDateTime.Value.Ticks - ticks); + older = timeSpan.TotalMilliseconds < 0; + daysDelta = (int)Math.Round(Math.Abs(timeSpan.TotalDays), 0); + distancePermyriad = (int)(faceDistanceLength.Length.Value / Stateless.IFaceDistance.Tolerance * Stateless.IFaceDistance.Permyriad); + foreach ((long lcl, long minimum, long maximum, long ucl) in personKeysRangesCollection) + { + if (ticks > minimum && ticks < maximum) + withinRanges.Add(0); + else if (ticks > lcl && ticks < ucl) + withinRanges.Add(1); + else + withinRanges.Add(2); + } + withinRange = withinRanges.Max(); + } + result = new(daysDelta, distancePermyriad, faceDistanceLength.Id, faceDistanceLength.NormalizedPixelPercentage.Value, older, withinRange); return result; } - internal static List GetFaceNumbers(List collection) - { - List results = new(); - List faceNumbers; - collection = (from l in collection orderby l.FaceEncoding is not null, l.Distance, l.Confidence descending, l.Id, l.NormalizedPixelPercentage select l).ToList(); - foreach (Models.Sorting sorting in collection) - { - faceNumbers = new(); - if (sorting.FaceEncoding is null) - faceNumbers.Add(Stateless.ILocation.Factor); - else - faceNumbers.Add(Stateless.ILocation.Factor * 2); - if (sorting.Distance is null) - faceNumbers.Add(Stateless.ILocation.Factor); - else - faceNumbers.Add((int)(Math.Round(sorting.Distance.Value, Stateless.ILocation.Digits) * Stateless.ILocation.Factor)); - if (sorting.Confidence is null) - faceNumbers.Add(Stateless.ILocation.Factor); - else - faceNumbers.Add((int)(Math.Round(sorting.Confidence.Value, Stateless.ILocation.Digits) * Stateless.ILocation.Factor)); - faceNumbers.Add(sorting.Id); - if (sorting.NormalizedPixelPercentage is null) - faceNumbers.Add(Stateless.ILocation.Factor); - else - faceNumbers.Add(sorting.NormalizedPixelPercentage.Value); - results.Add(faceNumbers.ToArray()); - } - return results; - } - } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/SortingContainer.cs b/Shared/Models/Stateless/Methods/SortingContainer.cs new file mode 100644 index 0000000..a2fbd41 --- /dev/null +++ b/Shared/Models/Stateless/Methods/SortingContainer.cs @@ -0,0 +1,6 @@ +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +internal abstract class SortingContainer +{ + +} \ No newline at end of file diff --git a/Tests/Models/Binder/Configuration.cs b/Tests/Models/Binder/Configuration.cs index af47e75..f3bffeb 100644 --- a/Tests/Models/Binder/Configuration.cs +++ b/Tests/Models/Binder/Configuration.cs @@ -19,7 +19,6 @@ public class Configuration [Display(Name = "Ignore Extensions"), Required] public string[] IgnoreExtensions { get; set; } [Display(Name = "Ignore Relative Paths"), Required] public string[] IgnoreRelativePaths { get; set; } [Display(Name = "Julie Phares Copy Birthdays"), Required] public string[] JuliePhares { get; set; } - [Display(Name = "Load Or Create Then Save Directory Distance Results"), Required] public string[] LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions { get; set; } [Display(Name = "Load Or Create Then Save Distance Results"), Required] public string[] LoadOrCreateThenSaveDistanceResultsForOutputResolutions { get; set; } [Display(Name = "Load Or Create Then Save Image Faces Results"), Required] public string[] LoadOrCreateThenSaveImageFacesResultsForOutputResolutions { get; set; } [Display(Name = "Load Or Create Then Save Index"), Required] public bool? LoadOrCreateThenSaveIndex { get; set; } @@ -129,8 +128,6 @@ public class Configuration throw new NullReferenceException(nameof(configuration.TestDistanceResults)); if (configuration.ValidResolutions is null) throw new NullReferenceException(nameof(configuration.ValidResolutions)); - if (configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions is null) - configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions = Array.Empty(); if (configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions is null) configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions = Array.Empty(); if (configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions is null) @@ -149,7 +146,6 @@ public class Configuration configuration.IgnoreExtensions, configuration.IgnoreRelativePaths, configuration.JuliePhares, - configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions, configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions, configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions, configuration.LoadOrCreateThenSaveIndex.Value, diff --git a/Tests/Models/Configuration.cs b/Tests/Models/Configuration.cs index a5cb689..617469e 100644 --- a/Tests/Models/Configuration.cs +++ b/Tests/Models/Configuration.cs @@ -18,7 +18,6 @@ public class Configuration public string[] IgnoreExtensions { init; get; } public string[] IgnoreRelativePaths { init; get; } public string[] JuliePhares { init; get; } - public string[] LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions { init; get; } public string[] LoadOrCreateThenSaveDistanceResultsForOutputResolutions { init; get; } public string[] LoadOrCreateThenSaveImageFacesResultsForOutputResolutions { init; get; } public bool LoadOrCreateThenSaveIndex { init; get; } @@ -53,7 +52,7 @@ public class Configuration public string[] ValidResolutions { init; get; } [JsonConstructor] - public Configuration(Property.Models.Configuration propertyConfiguration, bool checkJsonForDistanceResults, int crossDirectoryMaxItemsInDistanceCollection, int distanceFactor, bool forceFaceLastWriteTimeToCreationTime, bool forceMetadataLastWriteTimeToCreationTime, bool forceResizeLastWriteTimeToCreationTime, string[] ignoreExtensions, string[] ignoreRelativePaths, string[] juliePhares, string[] loadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions, string[] loadOrCreateThenSaveDistanceResultsForOutputResolutions, string[] loadOrCreateThenSaveImageFacesResultsForOutputResolutions, bool loadOrCreateThenSaveIndex, int locationConfidenceFactor, int? mappedMaxIndex, int maxItemsInDistanceCollection, string[] mixedYearRelativePaths, string modelDirectory, string modelName, int numberOfJitters, int numberOfTimesToUpsample, string outputExtension, int outputQuality, string[] outputResolutions, bool overrideForFaceImages, bool overrideForFaceLandmarkImages, bool overrideForResizeImages, int paddingLoops, string predictorModelName, bool propertiesChangedForDistance, bool propertiesChangedForFaces, bool propertiesChangedForIndex, bool propertiesChangedForMetadata, bool propertiesChangedForResize, bool reverse, string[] saveFaceLandmarkForOutputResolutions, bool saveFullYearOfRandomFiles, bool saveResizedSubfiles, string[] saveShortcutsForOutputResolutions, bool skipSearch, bool testDistanceResults, string[] validResolutions) + public Configuration(Property.Models.Configuration propertyConfiguration, bool checkJsonForDistanceResults, int crossDirectoryMaxItemsInDistanceCollection, int distanceFactor, bool forceFaceLastWriteTimeToCreationTime, bool forceMetadataLastWriteTimeToCreationTime, bool forceResizeLastWriteTimeToCreationTime, string[] ignoreExtensions, string[] ignoreRelativePaths, string[] juliePhares, string[] loadOrCreateThenSaveDistanceResultsForOutputResolutions, string[] loadOrCreateThenSaveImageFacesResultsForOutputResolutions, bool loadOrCreateThenSaveIndex, int locationConfidenceFactor, int? mappedMaxIndex, int maxItemsInDistanceCollection, string[] mixedYearRelativePaths, string modelDirectory, string modelName, int numberOfJitters, int numberOfTimesToUpsample, string outputExtension, int outputQuality, string[] outputResolutions, bool overrideForFaceImages, bool overrideForFaceLandmarkImages, bool overrideForResizeImages, int paddingLoops, string predictorModelName, bool propertiesChangedForDistance, bool propertiesChangedForFaces, bool propertiesChangedForIndex, bool propertiesChangedForMetadata, bool propertiesChangedForResize, bool reverse, string[] saveFaceLandmarkForOutputResolutions, bool saveFullYearOfRandomFiles, bool saveResizedSubfiles, string[] saveShortcutsForOutputResolutions, bool skipSearch, bool testDistanceResults, string[] validResolutions) { _PropertyConfiguration = propertyConfiguration; CheckJsonForDistanceResults = checkJsonForDistanceResults; @@ -65,7 +64,6 @@ public class Configuration IgnoreExtensions = ignoreExtensions; IgnoreRelativePaths = ignoreRelativePaths; JuliePhares = juliePhares; - LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions = loadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions; LoadOrCreateThenSaveDistanceResultsForOutputResolutions = loadOrCreateThenSaveDistanceResultsForOutputResolutions; LoadOrCreateThenSaveImageFacesResultsForOutputResolutions = loadOrCreateThenSaveImageFacesResultsForOutputResolutions; LoadOrCreateThenSaveIndex = loadOrCreateThenSaveIndex; diff --git a/Tests/UnitTestCalculations.cs b/Tests/UnitTestCalculations.cs index ab91055..c6b119e 100644 --- a/Tests/UnitTestCalculations.cs +++ b/Tests/UnitTestCalculations.cs @@ -68,6 +68,9 @@ public class UnitTestCalculations Assert.IsFalse(_PropertyConfiguration is null); } + [TestMethod] + public void TestMethodGetSort() => Assert.IsTrue(new bool[] { true, false }.OrderByDescending(l => l).First()); + [TestMethod] public void TestMethodGetAge() { @@ -77,4 +80,39 @@ public class UnitTestCalculations Assert.IsTrue(age.Value > 42.6092); } + [TestMethod] + public void TestMethodRound() + { + Assert.IsTrue(Shared.Models.Stateless.ILocation.Digits == 6); + Assert.IsTrue(Shared.Models.Stateless.ILocation.Factor == 1000000); + double valueA = 0.00001d; + int checkA = (int)(Math.Round(valueA, Shared.Models.Stateless.ILocation.Digits) * Shared.Models.Stateless.ILocation.Factor); + Assert.IsTrue(checkA == 10); + double valueB = 0.01d; + int checkB = (int)(Math.Round(valueB, Shared.Models.Stateless.ILocation.Digits) * Shared.Models.Stateless.ILocation.Factor); + Assert.IsTrue(checkB == 10000); + Assert.IsTrue(checkB > checkA); + } + + [TestMethod] + public void TestMethodRoundB() + { + const int DistanceDigits = 3; + const int DistanceFactor = 1000; + const int ToleranceAfterFactor = 600; + Assert.IsTrue(DistanceDigits == 3); + Assert.IsTrue(DistanceFactor == 1000); + Assert.IsTrue(Shared.Models.Stateless.IFaceDistance.Tolerance == 0.6d); + Assert.IsTrue(ToleranceAfterFactor == 600); + double valueA = 0.00001d; + int checkA = (int)(Math.Round(valueA, Shared.Models.Stateless.ILocation.Digits) * Shared.Models.Stateless.ILocation.Factor); + Assert.IsTrue(checkA == 10); + double valueB = 0.01d; + int checkB = (int)(Math.Round(valueB, Shared.Models.Stateless.ILocation.Digits) * Shared.Models.Stateless.ILocation.Factor); + Assert.IsTrue(checkB == 10000); + Assert.IsTrue(checkB > checkA); + int checkC = (int)(Shared.Models.Stateless.IFaceDistance.Tolerance * DistanceFactor); + Assert.IsTrue(checkC == ToleranceAfterFactor); + } + } \ No newline at end of file diff --git a/Tests/UnitTestResize.cs b/Tests/UnitTestResize.cs index 1ded7fb..3e7673d 100644 --- a/Tests/UnitTestResize.cs +++ b/Tests/UnitTestResize.cs @@ -100,7 +100,7 @@ public class UnitTestResize bottom = 10; width = 100; height = 100; - normalizedPixelPercentage = Location.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 1); + normalizedPixelPercentage = Shared.Models.Stateless.Methods.ILocation.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 1); Assert.IsTrue(normalizedPixelPercentage == 40500); left = 50; top = 50; @@ -108,7 +108,7 @@ public class UnitTestResize bottom = 60; width = 100; height = 100; - normalizedPixelPercentage = Location.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 1); + normalizedPixelPercentage = Shared.Models.Stateless.Methods.ILocation.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 1); Assert.IsTrue(normalizedPixelPercentage == 545500); } @@ -123,7 +123,7 @@ public class UnitTestResize bottom = 260; width = 500; height = 500; - normalizedPixelPercentage = Location.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 1); + normalizedPixelPercentage = Shared.Models.Stateless.Methods.ILocation.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 1); Assert.IsTrue(normalizedPixelPercentage == 499000); left = 490; top = 490; @@ -131,7 +131,7 @@ public class UnitTestResize bottom = 510; width = 1000; height = 1000; - normalizedPixelPercentage = Location.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 1); + normalizedPixelPercentage = Shared.Models.Stateless.Methods.ILocation.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 1); Assert.IsTrue(normalizedPixelPercentage == 499500); } @@ -146,7 +146,7 @@ public class UnitTestResize bottom = 80; width = 100; height = 100; - normalizedPixelPercentage = Location.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 1); + normalizedPixelPercentage = Shared.Models.Stateless.Methods.ILocation.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 1); Assert.IsTrue(normalizedPixelPercentage == 594000); left = 20; top = 40; @@ -154,7 +154,7 @@ public class UnitTestResize bottom = 80; width = 100; height = 100; - normalizedPixelPercentage = Location.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 10); + normalizedPixelPercentage = Shared.Models.Stateless.Methods.ILocation.GetNormalizedPixelPercentage(bottom, height, left, right, top, width, 10); Assert.IsTrue(normalizedPixelPercentage == 594000); } diff --git a/TestsWithFaceRecognitionDotNet/Models/Binder/Configuration.cs b/TestsWithFaceRecognitionDotNet/Models/Binder/Configuration.cs index af47e75..f3bffeb 100644 --- a/TestsWithFaceRecognitionDotNet/Models/Binder/Configuration.cs +++ b/TestsWithFaceRecognitionDotNet/Models/Binder/Configuration.cs @@ -19,7 +19,6 @@ public class Configuration [Display(Name = "Ignore Extensions"), Required] public string[] IgnoreExtensions { get; set; } [Display(Name = "Ignore Relative Paths"), Required] public string[] IgnoreRelativePaths { get; set; } [Display(Name = "Julie Phares Copy Birthdays"), Required] public string[] JuliePhares { get; set; } - [Display(Name = "Load Or Create Then Save Directory Distance Results"), Required] public string[] LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions { get; set; } [Display(Name = "Load Or Create Then Save Distance Results"), Required] public string[] LoadOrCreateThenSaveDistanceResultsForOutputResolutions { get; set; } [Display(Name = "Load Or Create Then Save Image Faces Results"), Required] public string[] LoadOrCreateThenSaveImageFacesResultsForOutputResolutions { get; set; } [Display(Name = "Load Or Create Then Save Index"), Required] public bool? LoadOrCreateThenSaveIndex { get; set; } @@ -129,8 +128,6 @@ public class Configuration throw new NullReferenceException(nameof(configuration.TestDistanceResults)); if (configuration.ValidResolutions is null) throw new NullReferenceException(nameof(configuration.ValidResolutions)); - if (configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions is null) - configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions = Array.Empty(); if (configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions is null) configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions = Array.Empty(); if (configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions is null) @@ -149,7 +146,6 @@ public class Configuration configuration.IgnoreExtensions, configuration.IgnoreRelativePaths, configuration.JuliePhares, - configuration.LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions, configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions, configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions, configuration.LoadOrCreateThenSaveIndex.Value, diff --git a/TestsWithFaceRecognitionDotNet/Models/Configuration.cs b/TestsWithFaceRecognitionDotNet/Models/Configuration.cs index a5cb689..617469e 100644 --- a/TestsWithFaceRecognitionDotNet/Models/Configuration.cs +++ b/TestsWithFaceRecognitionDotNet/Models/Configuration.cs @@ -18,7 +18,6 @@ public class Configuration public string[] IgnoreExtensions { init; get; } public string[] IgnoreRelativePaths { init; get; } public string[] JuliePhares { init; get; } - public string[] LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions { init; get; } public string[] LoadOrCreateThenSaveDistanceResultsForOutputResolutions { init; get; } public string[] LoadOrCreateThenSaveImageFacesResultsForOutputResolutions { init; get; } public bool LoadOrCreateThenSaveIndex { init; get; } @@ -53,7 +52,7 @@ public class Configuration public string[] ValidResolutions { init; get; } [JsonConstructor] - public Configuration(Property.Models.Configuration propertyConfiguration, bool checkJsonForDistanceResults, int crossDirectoryMaxItemsInDistanceCollection, int distanceFactor, bool forceFaceLastWriteTimeToCreationTime, bool forceMetadataLastWriteTimeToCreationTime, bool forceResizeLastWriteTimeToCreationTime, string[] ignoreExtensions, string[] ignoreRelativePaths, string[] juliePhares, string[] loadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions, string[] loadOrCreateThenSaveDistanceResultsForOutputResolutions, string[] loadOrCreateThenSaveImageFacesResultsForOutputResolutions, bool loadOrCreateThenSaveIndex, int locationConfidenceFactor, int? mappedMaxIndex, int maxItemsInDistanceCollection, string[] mixedYearRelativePaths, string modelDirectory, string modelName, int numberOfJitters, int numberOfTimesToUpsample, string outputExtension, int outputQuality, string[] outputResolutions, bool overrideForFaceImages, bool overrideForFaceLandmarkImages, bool overrideForResizeImages, int paddingLoops, string predictorModelName, bool propertiesChangedForDistance, bool propertiesChangedForFaces, bool propertiesChangedForIndex, bool propertiesChangedForMetadata, bool propertiesChangedForResize, bool reverse, string[] saveFaceLandmarkForOutputResolutions, bool saveFullYearOfRandomFiles, bool saveResizedSubfiles, string[] saveShortcutsForOutputResolutions, bool skipSearch, bool testDistanceResults, string[] validResolutions) + public Configuration(Property.Models.Configuration propertyConfiguration, bool checkJsonForDistanceResults, int crossDirectoryMaxItemsInDistanceCollection, int distanceFactor, bool forceFaceLastWriteTimeToCreationTime, bool forceMetadataLastWriteTimeToCreationTime, bool forceResizeLastWriteTimeToCreationTime, string[] ignoreExtensions, string[] ignoreRelativePaths, string[] juliePhares, string[] loadOrCreateThenSaveDistanceResultsForOutputResolutions, string[] loadOrCreateThenSaveImageFacesResultsForOutputResolutions, bool loadOrCreateThenSaveIndex, int locationConfidenceFactor, int? mappedMaxIndex, int maxItemsInDistanceCollection, string[] mixedYearRelativePaths, string modelDirectory, string modelName, int numberOfJitters, int numberOfTimesToUpsample, string outputExtension, int outputQuality, string[] outputResolutions, bool overrideForFaceImages, bool overrideForFaceLandmarkImages, bool overrideForResizeImages, int paddingLoops, string predictorModelName, bool propertiesChangedForDistance, bool propertiesChangedForFaces, bool propertiesChangedForIndex, bool propertiesChangedForMetadata, bool propertiesChangedForResize, bool reverse, string[] saveFaceLandmarkForOutputResolutions, bool saveFullYearOfRandomFiles, bool saveResizedSubfiles, string[] saveShortcutsForOutputResolutions, bool skipSearch, bool testDistanceResults, string[] validResolutions) { _PropertyConfiguration = propertyConfiguration; CheckJsonForDistanceResults = checkJsonForDistanceResults; @@ -65,7 +64,6 @@ public class Configuration IgnoreExtensions = ignoreExtensions; IgnoreRelativePaths = ignoreRelativePaths; JuliePhares = juliePhares; - LoadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions = loadOrCreateThenSaveDirectoryDistanceResultsForOutputResolutions; LoadOrCreateThenSaveDistanceResultsForOutputResolutions = loadOrCreateThenSaveDistanceResultsForOutputResolutions; LoadOrCreateThenSaveImageFacesResultsForOutputResolutions = loadOrCreateThenSaveImageFacesResultsForOutputResolutions; LoadOrCreateThenSaveIndex = loadOrCreateThenSaveIndex; diff --git a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs index 0d16b6b..795fd77 100644 --- a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs +++ b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs @@ -214,9 +214,9 @@ public class UnitTestFace List<(int, Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary? FaceParts)> collection; collection = faceRecognition.GetCollection(image, includeFaceEncoding: true, includeFaceParts: true, sortByNormalizedPixelPercentage: true); Assert.IsTrue(collection.Count == 2); - FaceRecognitionDotNet.FaceEncoding[] faceEncodings = (from l in collection where l.FaceEncoding is not null select l.FaceEncoding).ToArray(); - List faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncodings[0]); - Assert.IsTrue(faceDistances.Count == 2); + List faceDistanceEncodings = (from l in collection where l.FaceEncoding is not null select new FaceDistance(l.FaceEncoding)).ToList(); + List faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncodings[0]); + Assert.IsTrue(faceDistanceLengths.Count == 2); Assert.IsNotNull(sourceFileName); } diff --git a/package.json b/package.json index 9cd8736..cf8fc06 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "c": "When looping through all files and file not in collection path&name or date is different from collection value", "d": "Get id in normal fashion", "e": "If id is in collection update collection to new path/name", - "f": "If not save and add to collection" + "f": "If not save and add to collection", + "g": "Nicéphore Niépce in 1826 or 1827" } } \ No newline at end of file