From 05fb8685d9f0a8e5db2db876ab747c1909d676a3 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Sun, 6 Jul 2025 10:45:59 -0700 Subject: [PATCH] Removed Rectangle from LocationContainer mapped-ids-then-whole-percentages-to-location-container save-extracted-face save-extracted-java-script-object-notation --- Compare/Compare.cs | 135 +++++++++++++++-- Compare/Models/CompareSettings.cs | 4 +- .../Models/Stateless/FaceEncodingLogic.cs | 139 ++++++++++++++++-- Distance/Models/Stateless/FilterLogicA.cs | 8 +- Distance/Models/Stateless/FilterLogicB.cs | 73 ++------- Distance/Models/Stateless/FilterLogicC.cs | 14 +- Distance/Models/Stateless/IDistance.cs | 38 ++--- Distance/Models/Stateless/MappedLogicA.cs | 26 ---- Metadata/Models/Stateless/Exif.cs | 1 - Shared/Models/ExifDirectory.cs | 20 --- Shared/Models/LocationContainer.cs | 40 ++++- Shared/Models/Stateless/ILocation.cs | 5 - Shared/Models/Stateless/Location.cs | 32 +--- 13 files changed, 327 insertions(+), 208 deletions(-) diff --git a/Compare/Compare.cs b/Compare/Compare.cs index 32129e1..2b0f31c 100644 --- a/Compare/Compare.cs +++ b/Compare/Compare.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using ShellProgressBar; using System.Collections.ObjectModel; +using System.Drawing; using View_by_Distance.Compare.Models; using View_by_Distance.Distance.Models.Stateless; using View_by_Distance.Face.Models.Stateless; @@ -62,22 +63,25 @@ public partial class Compare : ICompare, IDisposable ReadOnlyDictionary onlyOne; bool runToDoCollectionFirst = GetRunToDoCollectionFirst(appSettings, compare); ReadOnlyCollections readOnlyCollections = GetReadOnlyCollections(appSettings); - ReadOnlyCollection mappedExifDirectoryWithEncoding = GetMappedExifDirectoryWithEncoding(appSettings, compare, readOnlyCollections); - ReadOnlyDictionary> keyValuePairs = IDistance.Extract(appSettings.CompareSettings, mappedExifDirectoryWithEncoding); + ReadOnlyDictionary> mappedIdsThenWholePercentagesToLocationContainer = GetMappedIdsThenWholePercentagesToLocationContainer(appSettings, compare, readOnlyCollections); + if (appSettings.CompareSettings.SaveExtractedFaces || appSettings.CompareSettings.SaveExtractedJavaScriptObjectNotation) + SaveExtracted(appSettings, mappedIdsThenWholePercentagesToLocationContainer); foreach (string outputResolution in appSettings.CompareSettings.OutputResolutions) { if (runToDoCollectionFirst || outputResolution.Any(char.IsNumber)) continue; logger?.LogInformation("{outputResolution}", outputResolution); exifDirectories = IFace.GetExifDirectories(appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, outputResolution); - preFiltered = IDistance.GetPreFilterLocationContainer(appSettings.DistanceSettings, appSettings.CompareSettings, compare, readOnlyCollections, keyValuePairs, exifDirectories); + preFiltered = IDistance.GetPreFilterLocationContainer(appSettings.DistanceSettings, appSettings.CompareSettings, compare, readOnlyCollections, mappedIdsThenWholePercentagesToLocationContainer, exifDirectories); if (preFiltered.Count == 0) continue; + if (appSettings.CompareSettings.SaveExtractedFaces || appSettings.CompareSettings.SaveExtractedJavaScriptObjectNotation) + SaveExtracted(appSettings, preFiltered); distanceLimits = new(appSettings.DistanceSettings); postFiltered = IDistance.GetPostFilterLocationContainer(preFiltered, distanceLimits); if (postFiltered.Count == 0) continue; - matrix = IDistance.GetMatrixLocationContainers(appSettings.DistanceSettings, appSettings.CompareSettings, compare, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered); + matrix = IDistance.GetMatrixLocationContainers(appSettings.DistanceSettings, compare, mappedIdsThenWholePercentagesToLocationContainer, distanceLimits, postFiltered); if (matrix.Count == 0) continue; onlyOne = IDistance.GetOnlyOne(appSettings.DistanceSettings, matrix); @@ -155,16 +159,127 @@ public partial class Compare : ICompare, IDisposable return result; } - private static ReadOnlyCollection GetMappedExifDirectoryWithEncoding(AppSettings appSettings, ICompare compare, ReadOnlyCollections readOnlyCollections) + private static ReadOnlyDictionary> GetMappedIdsThenWholePercentagesToLocationContainer(AppSettings appSettings, ICompare compare, ReadOnlyCollections readOnlyCollections) { - ReadOnlyCollection results; - ReadOnlyCollection exifDirectories = IDistance.GetMapped(appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.PeopleSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, readOnlyCollections); - if (exifDirectories.Count == 0 && !appSettings.DistanceSettings.SaveSortingWithoutPerson) - throw new NotSupportedException($"Switch {nameof(appSettings.DistanceSettings.SaveSortingWithoutPerson)}!"); - results = IDistance.GetMappedExifDirectoryWithEncoding(compare, exifDirectories); + + ReadOnlyDictionary> results; + results = IDistance.GetMappedIdsThenWholePercentagesToLocationContainer(appSettings.ResultSettings, + appSettings.MetadataSettings, + appSettings.PeopleSettings, + appSettings.DistanceSettings, + appSettings.CompareSettings, + compare, + readOnlyCollections); if (results.Count == 0 && !appSettings.DistanceSettings.SaveSortingWithoutPerson) throw new NotSupportedException($"Switch {nameof(appSettings.DistanceSettings.SaveSortingWithoutPerson)}!"); return results; } + private void SaveExtracted(AppSettings appSettings, ReadOnlyDictionary> mappedIdsThenWholePercentagesToLocationContainer) + { + ReadOnlyCollection? paths; + ReadOnlyDictionary> rootDirectoryFileNameToPaths = GetRootDirectoryFileNameToPaths(appSettings.ResultSettings, appSettings.CompareSettings); + foreach (KeyValuePair> keyValuePair in mappedIdsThenWholePercentagesToLocationContainer) + { + foreach (KeyValuePair keyValue in keyValuePair.Value) + { + if (keyValue.Value.ExifDirectory is null || keyValue.Value.FaceFile?.Location is null) + continue; + if (rootDirectoryFileNameToPaths.TryGetValue(keyValue.Value.FilePath.FileNameFirstSegment, out paths)) + { + if (appSettings.CompareSettings.SaveExtractedJavaScriptObjectNotation && keyValue.Value.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is not null) + SaveExtractedJavaScriptObjectNotation(keyValue.Key, keyValue.Value, paths); + if (appSettings.CompareSettings.SaveExtractedFaces) + SaveExtractedFace(keyValuePair.Key, keyValue.Key, keyValue.Value.ExifDirectory, keyValue.Value.FaceFile.Location, keyValue.Value.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, paths); + } + } + } + } + + private static void SaveExtracted(AppSettings appSettings, ReadOnlyCollection preFiltered) + { + ReadOnlyCollection? paths; + ReadOnlyDictionary> rootDirectoryFileNameToPaths = GetRootDirectoryFileNameToPaths(appSettings.ResultSettings, appSettings.CompareSettings); + foreach (LocationContainer locationContainer in preFiltered) + { + if (locationContainer?.FilePath?.Id is null || locationContainer?.WholePercentages is null || locationContainer?.ExifDirectory is null || locationContainer?.FaceFile?.Location is null) + continue; + if (rootDirectoryFileNameToPaths.TryGetValue(locationContainer.FilePath.FileNameFirstSegment, out paths)) + { + if (appSettings.CompareSettings.SaveExtractedJavaScriptObjectNotation && locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is not null) + SaveExtractedJavaScriptObjectNotation(locationContainer.WholePercentages.Value, locationContainer, paths); + if (appSettings.CompareSettings.SaveExtractedFaces) + SaveExtractedFace(locationContainer.FilePath.Id.Value, locationContainer.WholePercentages.Value, locationContainer.ExifDirectory, locationContainer.FaceFile.Location, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, paths); + } + } + } + + private static ReadOnlyDictionary> GetRootDirectoryFileNameToPaths(ResultSettings resultSettings, CompareSettings compareSettings) + { + Dictionary> results = []; + string key; + string extension; + List? collection; + Dictionary> keyValuePairs = []; + string[] files = !compareSettings.SaveExtractedFaces && !compareSettings.SaveExtractedJavaScriptObjectNotation ? [] : Directory.GetFiles(resultSettings.RootDirectory, "*", SearchOption.AllDirectories); + foreach (string file in files) + { + extension = Path.GetExtension(file); + if (resultSettings.IgnoreExtensions.Contains(extension)) + continue; + key = Path.GetFileNameWithoutExtension(file); + if (!keyValuePairs.TryGetValue(key, out collection)) + { + keyValuePairs.Add(key, []); + if (!keyValuePairs.TryGetValue(key, out collection)) + throw new Exception(); + } + collection.Add(file); + } + foreach (KeyValuePair> keyValuePair in keyValuePairs) + results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly()); + return results.AsReadOnly(); + } + + private static void SaveExtractedJavaScriptObjectNotation(int wholePercentages, LocationContainer locationContainer, ReadOnlyCollection paths) + { + string file; + string json; + foreach (string path in paths) + { + json = locationContainer.GetWithoutEncoding(); + file = $"{path}-{wholePercentages}.json"; + _ = IPath.WriteAllText(file, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + } + } + + private static void SaveExtractedFace(int id, int wholePercentages, ExifDirectory _, Location location, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName, ReadOnlyCollection paths) + { + int width; + int height; + string person; + foreach (string path in paths) + { + width = location.Right - location.Left; + height = location.Bottom - location.Top; + person = personKeyFormattedAndKeyTicksAndDisplayDirectoryName is null ? string.Empty : $"-{personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName}"; + ExtractFace(file: path, + width: width, + height: height, + left: location.Left, + top: location.Top, + suffix: $"-{id}-{wholePercentages}{person}-face.jpg"); + } + } + + private static void ExtractFace(string file, float width, float height, double left, double top, string suffix) + { + RectangleF rectangle = new((float)left, (float)top, width, height); + using Bitmap source = new(file); + using Bitmap bitmap = new((int)width, (int)height); + using (Graphics graphics = Graphics.FromImage(bitmap)) + graphics.DrawImage(source, new RectangleF(0, 0, width, height), rectangle, GraphicsUnit.Pixel); + bitmap.Save($"{file}{suffix}"); + } + } \ No newline at end of file diff --git a/Compare/Models/CompareSettings.cs b/Compare/Models/CompareSettings.cs index 98214cd..299a46c 100644 --- a/Compare/Models/CompareSettings.cs +++ b/Compare/Models/CompareSettings.cs @@ -9,7 +9,9 @@ public record CompareSettings(string Company, string FacesHiddenFileNameExtension, string FacesPartsFileNameExtension, int MaxDegreeOfParallelism, - string[] OutputResolutions) : Shared.Models.Properties.ICompareSettings + string[] OutputResolutions, + bool SaveExtractedFaces, + bool SaveExtractedJavaScriptObjectNotation) : Shared.Models.Properties.ICompareSettings { public override string ToString() diff --git a/Distance/Models/Stateless/FaceEncodingLogic.cs b/Distance/Models/Stateless/FaceEncodingLogic.cs index 696faf0..2d12908 100644 --- a/Distance/Models/Stateless/FaceEncodingLogic.cs +++ b/Distance/Models/Stateless/FaceEncodingLogic.cs @@ -1,6 +1,7 @@ using System.Collections.ObjectModel; using System.Text.Json; using View_by_Distance.Shared.Models; +using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless; namespace View_by_Distance.Distance.Models.Stateless; @@ -8,27 +9,135 @@ namespace View_by_Distance.Distance.Models.Stateless; internal static class FaceEncodingLogic { - internal static ReadOnlyCollection GetMappedExifDirectoryWithEncoding(ICompare compare, ReadOnlyCollection exifDirectories) + internal static void MoveUnableToMatch(FilePath filePath) { - List results = []; - string? json; - FaceEncoding? faceEncoding; - ExifDirectory exifDirectory; - FaceRecognitionDotNet.Models.FaceEncoding? encoding; + string checkFile = $"{filePath.FullName}.unk"; + if (File.Exists(filePath.FullName) && !File.Exists(checkFile)) + File.Move(filePath.FullName, checkFile); + } + + internal static ReadOnlyDictionary> GetMappedIdsThenWholePercentagesToLocationContainer(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections) + { + ReadOnlyDictionary> results; + List locationContainers = []; + int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism; + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; + ReadOnlyCollection exifDirectories = MappedLogicA.GetMapped(resultSettings, + metadataSettings, + peopleSettings, + distanceSettings, + compareSettings, + compare, + readOnlyCollections); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds); string message = $") Building Mapped with Encoding Face Files Collection - {totalSeconds} total second(s)"; compare.ConstructProgressBar(exifDirectories.Count, message); - foreach (ExifDirectory e in exifDirectories) + _ = Parallel.For(0, exifDirectories.Count, parallelOptions, (i, state) => + LocationContainersParallelFor(distanceSettings, compareSettings, compare, exifDirectories, i, locationContainers)); + results = GetMappedIdsThenWholePercentagesToLocationContainer(locationContainers); + return results; + } + + private static void LocationContainersParallelFor(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollection exifDirectories, int i, List results) + { + compare.Tick(); + ExifDirectory exifDirectory = exifDirectories[i]; + LocationContainer? locationContainer = GetLocationContainer(distanceSettings, compareSettings, exifDirectory); + results.Add(locationContainer); + } + + internal static LocationContainer? GetLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ExifDirectory exifDirectory) + { + LocationContainer? result; + string? json; + DateOnly dateOnly; + FaceFile? faceFile; + int? wholePercentages; + FaceEncoding? faceEncoding; + FaceRecognitionDotNet.Models.FaceEncoding? encoding; + wholePercentages = IMapping.GetWholePercentages(compareSettings, exifDirectory.FilePath); + if (wholePercentages is null) { - compare.Tick(); - json = Metadata.Models.Stateless.IMetadata.GetFaceEncoding(e); - faceEncoding = json is null ? null : JsonSerializer.Deserialize(json, FaceEncodingGenerationContext.Default.FaceEncoding); - if (faceEncoding is null) - continue; - encoding = FaceRecognitionDotNet.Models.FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding); - exifDirectory = ExifDirectory.Get(encoding, e); - results.Add(exifDirectory); + if (distanceSettings.DistanceMoveUnableToMatch) + MoveUnableToMatch(exifDirectory.FilePath); + result = null; } + else + { + faceFile = GetFaceFile(distanceSettings, exifDirectory); + if (faceFile is null) + result = null; + else + { + dateOnly = DateOnly.FromDateTime(new DateTime(exifDirectory.FilePath.CreationTicks)); + json = Metadata.Models.Stateless.IMetadata.GetFaceEncoding(exifDirectory); + faceEncoding = json is null ? null : JsonSerializer.Deserialize(json, FaceEncodingGenerationContext.Default.FaceEncoding); + if (faceEncoding is null) + result = null; + else + { + encoding = FaceRecognitionDotNet.Models.FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding); + result = new(CreationDateOnly: dateOnly, + ExifDirectory: exifDirectory, + Encoding: encoding, + FaceFile: faceFile, + FilePath: exifDirectory.FilePath, + LengthPermyriad: null, + LengthSource: null, + PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName: exifDirectory.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, + WholePercentages: wholePercentages); + } + } + } + return result; + } + + private static FaceFile? GetFaceFile(DistanceSettings distanceSettings, ExifDirectory exifDirectory) + { + FaceFile? result; + string? json = Metadata.Models.Stateless.IMetadata.GetOutputResolution(exifDirectory); + if (json is null || !json.Contains(nameof(DateTime))) + { + if (distanceSettings.DistanceMoveUnableToMatch) + MoveUnableToMatch(exifDirectory.FilePath); + result = null; + } + else + { + result = JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile); + if (result is null || result.Location is null) + { + if (distanceSettings.DistanceMoveUnableToMatch) + MoveUnableToMatch(exifDirectory.FilePath); + result = null; + } + } + return result; + } + + private static ReadOnlyDictionary> GetMappedIdsThenWholePercentagesToLocationContainer(List locationContainers) + { + Dictionary> results = []; + int id; + int wholePercentages; + Dictionary? keyValue; + Dictionary> keyValuePairs = []; + foreach (LocationContainer? locationContainer in locationContainers) + { + if (locationContainer?.FilePath.Id is null || locationContainer.WholePercentages is null) + continue; + id = locationContainer.FilePath.Id.Value; + wholePercentages = locationContainer.WholePercentages.Value; + if (!keyValuePairs.TryGetValue(id, out keyValue)) + { + keyValuePairs.Add(id, []); + if (!keyValuePairs.TryGetValue(id, out keyValue)) + throw new Exception(); + } + keyValue.Add(wholePercentages, locationContainer); + } + foreach (KeyValuePair> keyValuePair in keyValuePairs) + results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly()); return results.AsReadOnly(); } diff --git a/Distance/Models/Stateless/FilterLogicA.cs b/Distance/Models/Stateless/FilterLogicA.cs index c74c9ac..0b4e4b2 100644 --- a/Distance/Models/Stateless/FilterLogicA.cs +++ b/Distance/Models/Stateless/FilterLogicA.cs @@ -32,7 +32,7 @@ internal static class FilterLogicA return result; } - internal static ReadOnlyCollection GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary> keyValuePairs, ReadOnlyCollection exifDirectories) + internal static ReadOnlyCollection GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary> mappedIdsThenWholePercentagesToLocationContainer, ReadOnlyCollection exifDirectories) { List results = []; string? json; @@ -41,8 +41,8 @@ internal static class FilterLogicA bool? isFocusPerson; bool? inSkipCollection; FaceEncoding? faceEncoding; - ReadOnlyDictionary? keyValues; FaceRecognitionDotNet.Models.FaceEncoding? encoding; + ReadOnlyDictionary? keyValues; List? wholePercentagesCollection; ReadOnlyCollection locationContainers = FilterLogicB.GetLocationContainers(distanceSettings, compareSettings, compare, exifDirectories, nameof(FilterLogicA)); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds); @@ -53,7 +53,7 @@ internal static class FilterLogicA compare.Tick(); if (locationContainer.FilePath.Id is null || locationContainer.WholePercentages is null) continue; - if (keyValuePairs.TryGetValue(locationContainer.FilePath.Id.Value, out keyValues)) + if (mappedIdsThenWholePercentagesToLocationContainer.TryGetValue(locationContainer.FilePath.Id.Value, out keyValues)) { if (keyValues.ContainsKey(locationContainer.WholePercentages.Value)) continue; @@ -85,7 +85,7 @@ internal static class FilterLogicA if (faceEncoding is null) continue; encoding = FaceRecognitionDotNet.Models.FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding); - results.Add(LocationContainer.Get(locationContainer, encoding, keepExifDirectory: false)); + results.Add(LocationContainer.Get(locationContainer, encoding)); } return results.AsReadOnly(); } diff --git a/Distance/Models/Stateless/FilterLogicB.cs b/Distance/Models/Stateless/FilterLogicB.cs index f36d0a5..52cf2dc 100644 --- a/Distance/Models/Stateless/FilterLogicB.cs +++ b/Distance/Models/Stateless/FilterLogicB.cs @@ -1,6 +1,4 @@ using System.Collections.ObjectModel; -using System.Drawing; -using System.Text.Json; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless; @@ -10,71 +8,30 @@ namespace View_by_Distance.Distance.Models.Stateless; internal static class FilterLogicB { - private static void MoveUnableToMatch(FilePath filePath) - { - string checkFile = $"{filePath.FullName}.unk"; - if (File.Exists(filePath.FullName) && !File.Exists(checkFile)) - File.Move(filePath.FullName, checkFile); - } - - private static void LocationContainersParallelFor(DistanceSettings distanceSettings, ICompareSettings compareSettings, List locationContainers, ExifDirectory exifDirectory) - { - string? json; - if (exifDirectory.FilePath.Id is null) - return; - DateOnly dateOnly = DateOnly.FromDateTime(new DateTime(exifDirectory.FilePath.CreationTicks)); - int? wholePercentages = IMapping.GetWholePercentages(compareSettings, exifDirectory.FilePath); - if (wholePercentages is null) - { - if (distanceSettings.DistanceMoveUnableToMatch) - MoveUnableToMatch(exifDirectory.FilePath); - return; - } - json = Metadata.Models.Stateless.IMetadata.GetOutputResolution(exifDirectory); - if (json is null || !json.Contains(nameof(DateTime))) - { - if (distanceSettings.DistanceMoveUnableToMatch) - MoveUnableToMatch(exifDirectory.FilePath); - return; - } - FaceFile? faceFile = JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile); - if (faceFile is null || faceFile.Location is null) - { - if (distanceSettings.DistanceMoveUnableToMatch) - MoveUnableToMatch(exifDirectory.FilePath); - return; - } - RectangleF? rectangle = Shared.Models.Stateless.ILocation.GetPercentagesRectangle(distanceSettings, wholePercentages.Value); - if (rectangle is null) - return; - LocationContainer locationContainer = new(CreationDateOnly: dateOnly, - ExifDirectory: exifDirectory, - Encoding: exifDirectory.Encoding, - FaceFile: faceFile, - FilePath: exifDirectory.FilePath, - LengthPermyriad: null, - LengthSource: null, - PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName: exifDirectory.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, - Rectangle: rectangle, - WholePercentages: wholePercentages); - lock (locationContainers) - locationContainers.Add(locationContainer); - } - internal static ReadOnlyCollection GetLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollection exifDirectories, string sourceClass) { List results = []; int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism; - int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds); string message = $") Building LocationContainers Face Files Collection {sourceClass} - {totalSeconds} total second(s)"; compare.ConstructProgressBar(exifDirectories.Count, message); _ = Parallel.For(0, exifDirectories.Count, parallelOptions, (i, state) => - { - compare.Tick(); - LocationContainersParallelFor(distanceSettings, compareSettings, results, exifDirectories[i]); - }); + LocationContainersParallelFor(distanceSettings, compareSettings, compare, exifDirectories, i, results)); return results.AsReadOnly(); } + private static void LocationContainersParallelFor(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollection exifDirectories, int i, List results) + { + compare.Tick(); + ExifDirectory exifDirectory = exifDirectories[i]; + if (exifDirectory.FilePath.Id is null) + return; + LocationContainer? locationContainer = FaceEncodingLogic.GetLocationContainer(distanceSettings, compareSettings, exifDirectory); + if (locationContainer is null) + return; + lock (results) + results.Add(locationContainer); + } + } \ No newline at end of file diff --git a/Distance/Models/Stateless/FilterLogicC.cs b/Distance/Models/Stateless/FilterLogicC.cs index ea1c4f8..6c6259c 100644 --- a/Distance/Models/Stateless/FilterLogicC.cs +++ b/Distance/Models/Stateless/FilterLogicC.cs @@ -24,22 +24,24 @@ internal static class FilterLogicC return results.AsReadOnly(); } - private static ReadOnlyCollection GetCombined(DistanceSettings distanceSettings, Shared.Models.Properties.ICompareSettings compareSettings, ICompare compare, ReadOnlyCollection exifDirectories, ReadOnlyCollection postFiltered) + private static ReadOnlyCollection GetCombined(ReadOnlyDictionary> mappedIdsThenWholePercentagesToLocationContainer, ReadOnlyCollection postFiltered) { List results = []; foreach (LocationContainer locationContainer in postFiltered) results.Add(locationContainer); - ReadOnlyCollection locationContainers = FilterLogicB.GetLocationContainers(distanceSettings, compareSettings, compare, exifDirectories, nameof(FilterLogicC)); - foreach (LocationContainer locationContainer in locationContainers) - results.Add(locationContainer); + foreach (KeyValuePair> keyValuePair in mappedIdsThenWholePercentagesToLocationContainer) + { + foreach (KeyValuePair keyValue in keyValuePair.Value) + results.Add(keyValue.Value); + } return results.AsReadOnly(); } - internal static ReadOnlyCollection GetMatrixLocationContainers(DistanceSettings distanceSettings, Shared.Models.Properties.ICompareSettings compareSettings, ICompare compare, ReadOnlyCollection mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection postFiltered) + internal static ReadOnlyCollection GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompare compare, ReadOnlyDictionary> mappedIdsThenWholePercentagesToLocationContainer, DistanceLimits distanceLimits, ReadOnlyCollection postFiltered) { List results = []; ReadOnlyCollection collection; - ReadOnlyCollection locationContainers = GetCombined(distanceSettings, compareSettings, compare, mappedExifDirectoryWithEncoding, postFiltered); + ReadOnlyCollection locationContainers = GetCombined(mappedIdsThenWholePercentagesToLocationContainer, postFiltered); string message = $") Building Matrix - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds)} total second(s)"; compare.ConstructProgressBar(postFiltered.Count, message); foreach (LocationContainer locationContainer in postFiltered) diff --git a/Distance/Models/Stateless/IDistance.cs b/Distance/Models/Stateless/IDistance.cs index bc50388..bc3fcc9 100644 --- a/Distance/Models/Stateless/IDistance.cs +++ b/Distance/Models/Stateless/IDistance.cs @@ -9,7 +9,7 @@ namespace View_by_Distance.Distance.Models.Stateless; public interface IDistance { - static string Get(bool saveIndividually, string forceSingleImageHumanized, int by, bool isDefaultName) => + public static string Get(bool saveIndividually, string forceSingleImageHumanized, int by, bool isDefaultName) => $"{by switch { IMapLogic.Mapping => nameof(IMapLogic.Mapping), @@ -23,55 +23,43 @@ public interface IDistance public static ReadOnlyDictionary GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection matrix) => FilterLogicC.GetOnlyOne(distanceSettings, matrix); - public static ReadOnlyCollection GetMappedExifDirectoryWithEncoding(ICompare compare, ReadOnlyCollection exifDirectories) => - FaceEncodingLogic.GetMappedExifDirectoryWithEncoding(compare, exifDirectories); - public static ReadOnlyCollection GetPostFilterLocationContainer(ReadOnlyCollection preFiltered, DistanceLimits distanceLimits) => FilterLogicC.GetPostFilterLocationContainer(preFiltered, distanceLimits); - public static ReadOnlyDictionary> Extract(ICompareSettings compareSettings, ReadOnlyCollection exifDirectories) => - MappedLogicA.Extract(compareSettings, exifDirectories); - public static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, int? updated, ReadOnlyCollection saveContainers) => FilterLogicD.SaveContainers(distanceSettings, compareSettings, compare, updated, saveContainers); public static ReadOnlyCollection GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, string outputResolution, ReadOnlyDictionary onlyOne) => FilterLogicD.GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, outputResolution, onlyOne); - public static ReadOnlyCollection GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections) => - MappedLogicA.GetMapped(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, readOnlyCollections); + public static ReadOnlyCollection GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompare compare, ReadOnlyDictionary> mappedIdsThenWholePercentagesToLocationContainer, DistanceLimits distanceLimits, ReadOnlyCollection postFiltered) => + FilterLogicC.GetMatrixLocationContainers(distanceSettings, compare, mappedIdsThenWholePercentagesToLocationContainer, distanceLimits, postFiltered); - public static ReadOnlyCollection GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollection mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection postFiltered) => - FilterLogicC.GetMatrixLocationContainers(distanceSettings, compareSettings, compare, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered); + public static ReadOnlyDictionary> GetMappedIdsThenWholePercentagesToLocationContainer(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections) => + FaceEncodingLogic.GetMappedIdsThenWholePercentagesToLocationContainer(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, readOnlyCollections); - public static ReadOnlyCollection GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary> keyValuePairs, ReadOnlyCollection exifDirectories) => - FilterLogicA.GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, readOnlyCollections, keyValuePairs, exifDirectories); + public static ReadOnlyCollection GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary> mappedIdsThenWholePercentagesToLocationContainer, ReadOnlyCollection exifDirectories) => + FilterLogicA.GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, readOnlyCollections, mappedIdsThenWholePercentagesToLocationContainer, exifDirectories); internal static ReadOnlyDictionary TestStatic_GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection matrix) => GetOnlyOne(distanceSettings, matrix); - internal static ReadOnlyCollection TestStatic_GetMappedExifDirectoryWithEncoding(ICompare compare, ReadOnlyCollection exifDirectories) => - GetMappedExifDirectoryWithEncoding(compare, exifDirectories); - internal static ReadOnlyCollection TestStatic_GetPostFilterLocationContainer(ReadOnlyCollection preFiltered, DistanceLimits distanceLimits) => GetPostFilterLocationContainer(preFiltered, distanceLimits); - internal static ReadOnlyDictionary> TestStatic_Extract(ICompareSettings compareSettings, ReadOnlyCollection exifDirectories) => - Extract(compareSettings, exifDirectories); - internal static void TestStatic_SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, int? updated, ReadOnlyCollection saveContainers) => SaveContainers(distanceSettings, compareSettings, compare, updated, saveContainers); internal static ReadOnlyCollection TestStatic_GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, string outputResolution, ReadOnlyDictionary onlyOne) => GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, outputResolution, onlyOne); - internal static ReadOnlyCollection TestStatic_GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections) => - GetMapped(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, readOnlyCollections); + internal static ReadOnlyCollection TestStatic_GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompare compare, ReadOnlyDictionary> mappedIdsThenWholePercentagesToLocationContainer, DistanceLimits distanceLimits, ReadOnlyCollection postFiltered) => + GetMatrixLocationContainers(distanceSettings, compare, mappedIdsThenWholePercentagesToLocationContainer, distanceLimits, postFiltered); - internal static ReadOnlyCollection TestStatic_GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollection mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection postFiltered) => - GetMatrixLocationContainers(distanceSettings, compareSettings, compare, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered); + internal static ReadOnlyDictionary> TestStatic_GetMappedIdsThenWholePercentagesToLocationContainer(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections) => + GetMappedIdsThenWholePercentagesToLocationContainer(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, readOnlyCollections); - internal static ReadOnlyCollection TestStatic_GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary> keyValuePairs, ReadOnlyCollection exifDirectories) => - GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, readOnlyCollections, keyValuePairs, exifDirectories); + internal static ReadOnlyCollection TestStatic_GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary> mappedIdsThenWholePercentagesToLocationContainer, ReadOnlyCollection exifDirectories) => + GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, readOnlyCollections, mappedIdsThenWholePercentagesToLocationContainer, exifDirectories); } \ No newline at end of file diff --git a/Distance/Models/Stateless/MappedLogicA.cs b/Distance/Models/Stateless/MappedLogicA.cs index 16d8669..201959c 100644 --- a/Distance/Models/Stateless/MappedLogicA.cs +++ b/Distance/Models/Stateless/MappedLogicA.cs @@ -14,32 +14,6 @@ internal static class MappedLogicA string? PersonDisplayDirectoryName, FilePath FilePath); - internal static ReadOnlyDictionary> Extract(ICompareSettings compareSettings, ReadOnlyCollection exifDirectories) - { - Dictionary> results = []; - int? wholePercentages; - Dictionary? keyValues; - Dictionary> keyValuePairs = []; - foreach (ExifDirectory exifDirectory in exifDirectories) - { - if (exifDirectory.FilePath.Id is null) - continue; - if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues)) - { - keyValuePairs.Add(exifDirectory.FilePath.Id.Value, []); - if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues)) - throw new Exception(); - } - wholePercentages = IMapping.GetWholePercentages(compareSettings, exifDirectory.FilePath); - if (wholePercentages is null) - continue; - keyValues.Add(wholePercentages.Value, exifDirectory.FilePath); - } - foreach (KeyValuePair> keyValuePair in keyValuePairs) - results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly()); - return results.AsReadOnly(); - } - internal static ReadOnlyCollection GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections) { List results = []; diff --git a/Metadata/Models/Stateless/Exif.cs b/Metadata/Models/Stateless/Exif.cs index d91ce64..3583933 100644 --- a/Metadata/Models/Stateless/Exif.cs +++ b/Metadata/Models/Stateless/Exif.cs @@ -529,7 +529,6 @@ internal abstract class Exif Shared.Models.QuickTimeMovieHeaderDirectory[] quickTimeMovieHeaderDirectories = GetQuickTimeMovieHeaderDirectoryDirectories(directories); Shared.Models.QuickTimeTrackHeaderDirectory[] quickTimeTrackHeaderDirectories = GetQuickTimeTrackHeaderDirectoryDirectories(directories); result = new(aviDirectories, - null, exifBaseDirectories, fileMetadataDirectories, filePath, diff --git a/Shared/Models/ExifDirectory.cs b/Shared/Models/ExifDirectory.cs index 7953c92..5bff7d2 100644 --- a/Shared/Models/ExifDirectory.cs +++ b/Shared/Models/ExifDirectory.cs @@ -4,7 +4,6 @@ using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; public record ExifDirectory(AviDirectory[] AviDirectories, - object? Encoding, ExifDirectoryBase[] ExifBaseDirectories, FileMetadataDirectory[] FileMetadataDirectories, FilePath FilePath, @@ -28,25 +27,6 @@ public record ExifDirectory(AviDirectory[] AviDirectories, return result; } - public static ExifDirectory Get(object encoding, ExifDirectory e) => - new(e.AviDirectories, - encoding, - e.ExifBaseDirectories, - e.FileMetadataDirectories, - e.FilePath, - e.GifHeaderDirectories, - e.GpsDirectories, - e.Height, - e.JpegDirectories, - e.MakernoteDirectories, - e.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, - e.PhotoshopDirectories, - e.PngDirectories, - e.QuickTimeMovieHeaderDirectories, - e.QuickTimeTrackHeaderDirectories, - e.WebPDirectories, - e.Width); - } [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] diff --git a/Shared/Models/LocationContainer.cs b/Shared/Models/LocationContainer.cs index c474a24..0f8231c 100644 --- a/Shared/Models/LocationContainer.cs +++ b/Shared/Models/LocationContainer.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using System.Text.Json; +using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; @@ -10,22 +11,35 @@ public record LocationContainer(DateOnly? CreationDateOnly, int? LengthPermyriad, FilePath? LengthSource, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, - RectangleF? Rectangle, int? WholePercentages) { - public static LocationContainer Get(LocationContainer locationContainer, object? encoding, bool keepExifDirectory) + public string GetWithoutEncoding() + { + string result; + WithoutEncoding withoutEncoding = new(CreationDateOnly: CreationDateOnly, + ExifDirectory: ExifDirectory, + FaceFile: FaceFile, + FilePath: FilePath, + LengthPermyriad: LengthPermyriad, + LengthSource: LengthSource, + PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName: PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, + WholePercentages: WholePercentages); + result = JsonSerializer.Serialize(withoutEncoding, WithoutEncodingSourceGenerationContext.Default.WithoutEncoding); + return result; + } + + public static LocationContainer Get(LocationContainer locationContainer, object? encoding) { LocationContainer result; result = new(CreationDateOnly: locationContainer.CreationDateOnly, - ExifDirectory: keepExifDirectory ? locationContainer.ExifDirectory : null, + ExifDirectory: locationContainer.ExifDirectory, Encoding: encoding, FaceFile: locationContainer.FaceFile, FilePath: locationContainer.FilePath, LengthPermyriad: locationContainer.LengthPermyriad, LengthSource: locationContainer.LengthSource, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName: locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, - Rectangle: locationContainer.Rectangle, WholePercentages: locationContainer.WholePercentages); return result; } @@ -41,9 +55,23 @@ public record LocationContainer(DateOnly? CreationDateOnly, LengthPermyriad: lengthPermyriad, LengthSource: source.FilePath, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName: locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, - Rectangle: locationContainer.Rectangle, WholePercentages: locationContainer.WholePercentages); return result; } +} + +internal record WithoutEncoding(DateOnly? CreationDateOnly, + ExifDirectory? ExifDirectory, + FaceFile? FaceFile, + FilePath FilePath, + int? LengthPermyriad, + FilePath? LengthSource, + PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, + int? WholePercentages); + +[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] +[JsonSerializable(typeof(WithoutEncoding))] +internal partial class WithoutEncodingSourceGenerationContext : JsonSerializerContext +{ } \ No newline at end of file diff --git a/Shared/Models/Stateless/ILocation.cs b/Shared/Models/Stateless/ILocation.cs index b494be8..8514431 100644 --- a/Shared/Models/Stateless/ILocation.cs +++ b/Shared/Models/Stateless/ILocation.cs @@ -5,11 +5,6 @@ namespace View_by_Distance.Shared.Models.Stateless; public interface ILocation { - RectangleF? TestStatic_GetPercentagesRectangle(DistanceSettings distanceSettings, int wholePercentages) => - GetPercentagesRectangle(distanceSettings, wholePercentages); - static RectangleF? GetPercentagesRectangle(DistanceSettings distanceSettings, int wholePercentages) => - Location.GetPercentagesRectangle(distanceSettings, wholePercentages); - Models.Location TestStatic_GetTrimBound(double detectionConfidence, Rectangle rectangle, int width, int height, int facesCount) => TrimBound(detectionConfidence, rectangle, width, height, facesCount); static Models.Location TrimBound(double detectionConfidence, Rectangle rectangle, int width, int height, int facesCount) => diff --git a/Shared/Models/Stateless/Location.cs b/Shared/Models/Stateless/Location.cs index cef980e..e04ebb0 100644 --- a/Shared/Models/Stateless/Location.cs +++ b/Shared/Models/Stateless/Location.cs @@ -1,6 +1,4 @@ -using System.Drawing; - -namespace View_by_Distance.Shared.Models.Stateless; +namespace View_by_Distance.Shared.Models.Stateless; internal abstract class Location { @@ -47,32 +45,4 @@ internal abstract class Location return result; } - internal static RectangleF? GetPercentagesRectangle(DistanceSettings distanceSettings, int wholePercentages) - { - RectangleF? result; - string wp = wholePercentages.ToString(); - int length = (distanceSettings.LocationDigits - 1) / 4; - string[] segments = - [ - wp[..1], - wp.Substring(1, length), - wp.Substring(3, length), - wp.Substring(5, length), - wp.Substring(7, length) - ]; - if (string.Join(string.Empty, segments) != wp) - result = null; - else - { - if (!int.TryParse(segments[1], out int xWholePercent) || !int.TryParse(segments[2], out int yWholePercent) || !int.TryParse(segments[3], out int wWholePercent) || !int.TryParse(segments[4], out int hWholePercent)) - result = null; - else - { - float factor = 100; - result = new(xWholePercent / factor, yWholePercent / factor, wWholePercent / factor, hWholePercent / factor); - } - } - return result; - } - } \ No newline at end of file