diff --git a/.vscode/settings.json b/.vscode/settings.json index 49e567b..f46ff2b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,6 +21,7 @@ "nosj", "paramref", "Phares", + "RDHC", "Rects", "resnet", "Serilog", diff --git a/FaceRecognitionDotNet/FaceRecognition.cs b/FaceRecognitionDotNet/FaceRecognition.cs index 6f141c8..41a8b0e 100644 --- a/FaceRecognitionDotNet/FaceRecognition.cs +++ b/FaceRecognitionDotNet/FaceRecognition.cs @@ -222,9 +222,9 @@ public class FaceRecognition : DisposableObject return results; } - public List<(Location, FaceEncoding?, Dictionary?)> GetCollection(Image image, bool includeFaceEncoding, bool includeFaceParts, bool sortByNormalizedPixelPercentage) + public List<(int, Location, FaceEncoding?, Dictionary?)> GetCollection(Image image, bool includeFaceEncoding, bool includeFaceParts, bool sortByNormalizedPixelPercentage) { - List<(Location, FaceEncoding?, Dictionary?)> results = new(); + List<(int, Location, FaceEncoding?, Dictionary?)> results = new(); if (image is null) throw new NullReferenceException(nameof(image)); image.ThrowIfDisposed(); @@ -235,9 +235,9 @@ public class FaceRecognition : DisposableObject List fullObjectDetections = GetFullObjectDetections(image, locations); if (fullObjectDetections.Count != locations.Count) throw new Exception(); - List<(Location Location, List FaceEncodings, List> FaceParts)> collection = new(); - foreach (Location location in locations) - collection.Add(new(location, new(), new())); + List<(int LocationIndex, Location Location, List FaceEncodings, List> FaceParts)> collection = new(); + for (int i = 0; i < locations.Count; i++) + collection.Add(new(i, locations[i], new(), new())); if (locations.Count != collection.Count) throw new Exception(); if (!includeFaceEncoding) @@ -274,18 +274,18 @@ public class FaceRecognition : DisposableObject fullObjectDetection.Dispose(); const int indexZero = 0; Dictionary keyValuePairs; - foreach ((Location location, List faceEncodings, List> faceParts) in collection) + foreach ((int locationIndex, Location location, List faceEncodings, List> faceParts) in collection) { if (faceEncodings.Count != 1 || faceParts.Count != 1) continue; if (!faceParts[indexZero].Any()) - results.Add(new(location, faceEncodings[indexZero], null)); + results.Add(new(locationIndex, location, faceEncodings[indexZero], null)); else { keyValuePairs = new(); foreach ((FacePart facePart, FacePoint[] facePoints) in faceParts[indexZero]) keyValuePairs.Add(facePart, facePoints); - results.Add(new(location, faceEncodings[indexZero], keyValuePairs)); + results.Add(new(locationIndex, location, faceEncodings[indexZero], keyValuePairs)); } } return results; diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index b2f1c3f..770424b 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -664,13 +664,12 @@ public class DlibDotNet foreach (string outputResolution in _Configuration.OutputResolutions) { (aResultsFullGroupDirectory, bResultsFullGroupDirectory, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, eResultsFullGroupDirectory, zResultsFullGroupDirectory) = GetResultsFullGroupDirectories(configuration, model, predictorModel, outputResolution); - if (_ArgZeroIsConfigurationRootDirectory && _Exceptions.Count == 0 && outputResolution == _Configuration.OutputResolutions[0] && (mapLogic.NamedFaceInfoDeterministicHashCodeKeyValuePairs.Any() || mapLogic.NamedDeterministicHashCodeKeyValuePairs.Any())) + if (_ArgZeroIsConfigurationRootDirectory && _Exceptions.Count == 0 && outputResolution == _Configuration.OutputResolutions[0]) { - if (!string.IsNullOrEmpty(mapLogic.DeterministicHashCodeRootDirectory) && !mapLogic.IncorrectDeterministicHashCodeKeyValuePairs.Any()) - mapLogic.UpdateKeyValuePairs(containers); + mapLogic.UseKeyValuePairsSaveFaceEncoding(containers); foreach (Container container in containers) { - Map.Models.MapLogic.AddToNamed(mapLogic, container.Items); + mapLogic.AddToNamed(container.Items); if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) D_Face.SaveShortcuts(_Configuration.JuliePhares, dResultsFullGroupDirectory, ticks, peopleCollection, mapLogic, container.Items); } diff --git a/Instance/Models/_D_Face.cs b/Instance/Models/_D_Face.cs index 883b5c6..213fa51 100644 --- a/Instance/Models/_D_Face.cs +++ b/Instance/Models/_D_Face.cs @@ -182,20 +182,19 @@ public class D_Face results.Add(new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, item.RelativePath, i: null, location: null)); else { - List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary? FaceParts)> collection; + List<(int, Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary? FaceParts)> collection; FaceRecognition faceRecognition = new(_Configuration.NumberOfTimesToUpsample, _Configuration.NumberOfJitters, _PredictorModel, _Model, _ModelParameter); collection = faceRecognition.GetCollection(unknownImage, includeFaceEncoding: true, includeFaceParts: true, sortByNormalizedPixelPercentage: true); if (!collection.Any()) results.Add(new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, item.RelativePath, i: null, location: null)); else { - int i = 0; Face face; double[] rawEncoding; Shared.Models.FaceEncoding convertedFaceEncoding; - foreach ((Location location, FaceRecognitionDotNet.FaceEncoding? faceEncoding, Dictionary? faceParts) in collection) + foreach ((int locationIndex, Location location, FaceRecognitionDotNet.FaceEncoding? faceEncoding, Dictionary? faceParts) in collection) { - face = new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, item.RelativePath, i, location); + face = new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, item.RelativePath, locationIndex, location); if (faceEncoding is not null) { rawEncoding = faceEncoding.GetRawEncoding(); @@ -205,7 +204,6 @@ public class D_Face if (faceParts is not null) face.SetFaceParts(faceParts); results.Add(face); - i += 1; } } unknownImage.Dispose(); @@ -379,7 +377,7 @@ public class D_Face WindowsShortcut windowsShortcut; const string pattern = @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]"; string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, $"({ticks})"); - List<(Item, (string, Face?, (string, string, string, string))[])> collections = Map.Models.MapLogic.GetCollection(mapLogic, items, dFacesContentDirectory); + List<(Item, (string, Face?, (string, string, string, string))[])> collections = mapLogic.GetCollection(items, dFacesContentDirectory); foreach ((Item item, (string personKey, Face? _, (string, string, string, string))[] collection) in collections) { if (collection.Length != 1) diff --git a/Instance/Models/_E_Distance.cs b/Instance/Models/_E_Distance.cs index dd930bb..6dfb9fb 100644 --- a/Instance/Models/_E_Distance.cs +++ b/Instance/Models/_E_Distance.cs @@ -541,7 +541,7 @@ internal class E_Distance { List faceDistances = FaceRecognition.FaceDistances(tuple.FaceEncodings, faceEncoding); result = new(face.Location?.NormalizedPixelPercentage, tuple.MinimumDateTime, tuple.IsWrongYear, tuple.PersonBirthday, faceDistances); - if (result.Minimum > Shared.Models.Stateless.Methods.IClosest.MaximumMinimum) + if (result.Minimum > Shared.Models.Stateless.IClosest.MaximumMinimum) result = null; } return result; @@ -565,7 +565,7 @@ internal class E_Distance continue; faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncoding); closest = new(face.Location?.NormalizedPixelPercentage, minimumDateTime, isWrongYear, personBirthday, faceDistances); - if (closest.Minimum > Shared.Models.Stateless.Methods.IClosest.MaximumMinimum) + if (closest.Minimum > Shared.Models.Stateless.IClosest.MaximumMinimum) continue; closestCollection.Add(closest); } @@ -613,7 +613,7 @@ internal class E_Distance if (itemMinimumDateTime is null) continue; (itemIsWrongYear, _) = Map.Models.MapLogic.IsWrongYear(item); - if (Shared.Models.Stateless.Methods.IClosest.SkipIsWrongYear && itemIsWrongYear.HasValue && itemIsWrongYear.Value) + if (Shared.Models.Stateless.IClosest.SkipIsWrongYear && itemIsWrongYear.HasValue && itemIsWrongYear.Value) continue; item.Closest.Clear(); for (int i = 0; i < item.Faces.Count; i++) @@ -631,12 +631,12 @@ internal class E_Distance if (closest.PersonBirthday is null) continue; personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(closest.PersonBirthday); - if (mapLogic.IncorrectDeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey) && mapLogic.IncorrectDeterministicHashCodeKeyValuePairs[deterministicHashCodeKey].Contains(personKey)) + if (mapLogic.IsIncorrect(deterministicHashCodeKey, personKey)) continue; key = Map.Models.MapLogic.GetKey(closest.MinimumDateTime, closest.IsWrongYear, closest.PersonBirthday); if (!results.ContainsKey(key)) results.Add(key, 0); - else if (results[key] > Shared.Models.Stateless.Methods.IClosest.MaximumPer) + else if (results[key] > Shared.Models.Stateless.IClosest.MaximumPer) continue; results[key] += 1; item.Closest[0] = closest; @@ -732,7 +732,7 @@ internal class E_Distance FileInfo landmarkFileInfo; string landmarksDirectory; double deterministicHashCodeKey; - const string facePopulatedKey = "Closest"; + const string facePopulatedKey = nameof(Closest); const string pattern = @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]"; foreach (Container container in containers) { diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index b389d25..dadf32a 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -12,16 +12,12 @@ public class MapLogic protected readonly Dictionary _IndicesFromNew; protected readonly string _DeterministicHashCodeRootDirectory; protected readonly Dictionary _SixCharacterNamedFaceInfo; - protected readonly Dictionary _NamedFaceInfoDeterministicHashCodeKeyValuePairs; - protected readonly Dictionary _NamedDeterministicHashCodeKeyValuePairs; + protected readonly Dictionary _DeterministicHashCodeUnknownFaceKeyValuePairs; + protected readonly Dictionary _DeterministicHashCodeKeyValuePairs; protected readonly Dictionary _IncorrectDeterministicHashCodeKeyValuePairs; public Dictionary KeyValuePairs => _KeyValuePairs; public Dictionary IndicesFromNew => _IndicesFromNew; - public string DeterministicHashCodeRootDirectory => _DeterministicHashCodeRootDirectory; - public Dictionary NamedDeterministicHashCodeKeyValuePairs => _NamedDeterministicHashCodeKeyValuePairs; - public Dictionary IncorrectDeterministicHashCodeKeyValuePairs => _IncorrectDeterministicHashCodeKeyValuePairs; - public Dictionary NamedFaceInfoDeterministicHashCodeKeyValuePairs => _NamedFaceInfoDeterministicHashCodeKeyValuePairs; private readonly Serilog.ILogger? _Log; private readonly Configuration _Configuration; @@ -31,7 +27,7 @@ public class MapLogic _AllCollection = new(); _Configuration = configuration; _Log = Serilog.Log.ForContext(); - Dictionary? namedFaceInfoDeterministicHashCode; + Dictionary? deterministicHashCodeUnknownFaceKeyValuePairs; if (configuration.VerifyToSeason is null || !configuration.VerifyToSeason.Any()) throw new Exception(); string json; @@ -42,20 +38,20 @@ public class MapLogic List>? collection; Dictionary indicesFromNew = new(); Dictionary? sixCharacterNamedFaceInfo; - Dictionary namedDeterministicHashCode = new(); - Dictionary incorrectDeterministicHashCode = new(); + Dictionary deterministicHashCodeKeyValuePairs = new(); + Dictionary incorrectDeterministicHashCodeKeyValuePairs = new(); string? rootDirectoryParent = Path.GetDirectoryName(configuration.RootDirectory); if (string.IsNullOrEmpty(rootDirectoryParent)) throw new NullReferenceException(nameof(rootDirectoryParent)); files = Directory.GetFiles(rootDirectoryParent, "*DeterministicHashCode*.json", SearchOption.TopDirectoryOnly); if (files.Length != 1) - namedFaceInfoDeterministicHashCode = new(); + deterministicHashCodeUnknownFaceKeyValuePairs = new(); else { json = File.ReadAllText(files[0]); - namedFaceInfoDeterministicHashCode = JsonSerializer.Deserialize>(json); - if (namedFaceInfoDeterministicHashCode is null) - throw new NullReferenceException(nameof(namedFaceInfoDeterministicHashCode)); + deterministicHashCodeUnknownFaceKeyValuePairs = JsonSerializer.Deserialize>(json); + if (deterministicHashCodeUnknownFaceKeyValuePairs is null) + throw new NullReferenceException(nameof(deterministicHashCodeUnknownFaceKeyValuePairs)); } string[] directories = Directory.GetDirectories(rootDirectoryParent, "*DeterministicHashCode*", SearchOption.TopDirectoryOnly); if (!directories.Any()) @@ -64,9 +60,9 @@ public class MapLogic { Dictionary> faces = new(); deterministicHashCodeRootDirectory = directories[0]; - SetKeyValuePairs(deterministicHashCodeRootDirectory, namedDeterministicHashCode, incorrectDeterministicHashCode, faces); + SetKeyValuePairs(deterministicHashCodeRootDirectory, deterministicHashCodeKeyValuePairs, incorrectDeterministicHashCodeKeyValuePairs, faces); } - if (!namedFaceInfoDeterministicHashCode.Any()) + if (!deterministicHashCodeUnknownFaceKeyValuePairs.Any()) sixCharacterNamedFaceInfo = new(); else { @@ -112,78 +108,79 @@ public class MapLogic _KeyValuePairs = keyValuePairs; _IndicesFromNew = indicesFromNew; _SixCharacterNamedFaceInfo = sixCharacterNamedFaceInfo; - _NamedDeterministicHashCodeKeyValuePairs = namedDeterministicHashCode; _DeterministicHashCodeRootDirectory = deterministicHashCodeRootDirectory; - _IncorrectDeterministicHashCodeKeyValuePairs = incorrectDeterministicHashCode; - _NamedFaceInfoDeterministicHashCodeKeyValuePairs = namedFaceInfoDeterministicHashCode; + _DeterministicHashCodeKeyValuePairs = deterministicHashCodeKeyValuePairs; + _IncorrectDeterministicHashCodeKeyValuePairs = incorrectDeterministicHashCodeKeyValuePairs; + _DeterministicHashCodeUnknownFaceKeyValuePairs = deterministicHashCodeUnknownFaceKeyValuePairs; } - private static void SetKeyValuePairs(string deterministicHashCodeRootDirectory, List<(string, double)> named, List<(string, double)> incorrect, Dictionary> keyValuePairs) + public bool IsIncorrect(double deterministicHashCodeKey, string personKey) => _DeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey) && _IncorrectDeterministicHashCodeKeyValuePairs[deterministicHashCodeKey].Contains(personKey); + + private void SetKeyValuePairs(string deterministicHashCodeRootDirectory, List<(string, double)> named, List<(string, double)> incorrect, Dictionary> keyValuePairs) { string[] files; - string fileName; string personKey; - string? checkFile; string[] yearDirectories; + string ticksDirectoryName; string[] personKeyDirectories; string[] personNameDirectories; - double? idAndNormalizedPixelPercentage; + string[] personNameLinkDirectories; + double? reversedDeterministicHashCodeKey; + bool keyValuePairsAny = keyValuePairs.Any(); string[] ticksDirectories = Directory.GetDirectories(deterministicHashCodeRootDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string ticksDirectory in ticksDirectories) { - if (!ticksDirectory.EndsWith(')')) + ticksDirectoryName = Path.GetFileName(ticksDirectory); + if (ticksDirectoryName.Length < 3 || ticksDirectoryName[0] != '(' || ticksDirectoryName[^1] != ')') continue; personKeyDirectories = Directory.GetDirectories(ticksDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string personKeyDirectory in personKeyDirectories) { personKey = Path.GetFileName(personKeyDirectory); + if (personKey == nameof(Shared.Models.Closest)) + throw new Exception($"Move personKey directories up one from {nameof(Shared.Models.Closest)} and delete {nameof(Shared.Models.Closest)} directory!"); yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string yearDirectory in yearDirectories) { files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly); personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string file in files) - { - if (file.EndsWith(".lnk")) - continue; - fileName = Path.GetFileName(file); - idAndNormalizedPixelPercentage = Shared.Models.Stateless.Methods.INamed.GetReversedDeterministicHashCode(fileName); - if (idAndNormalizedPixelPercentage is null) - { - (checkFile, idAndNormalizedPixelPercentage) = Shared.Models.Stateless.Methods.INamed.GetReversedDeterministicHashCode(keyValuePairs, file); - if (idAndNormalizedPixelPercentage is null) - break; - if (!string.IsNullOrEmpty(checkFile)) - File.Move(file, checkFile); - } - incorrect.Add(new(personKey, idAndNormalizedPixelPercentage.Value)); - } + File.Delete(file); foreach (string personNameDirectory in personNameDirectories) { files = Directory.GetFiles(personNameDirectory, "*", SearchOption.TopDirectoryOnly); + personNameLinkDirectories = Directory.GetDirectories(personNameDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string file in files) { - if (file.EndsWith(".lnk")) + if (file.EndsWith(".lnk") || file.EndsWith(".json")) continue; - fileName = Path.GetFileName(file); - idAndNormalizedPixelPercentage = Shared.Models.Stateless.Methods.INamed.GetReversedDeterministicHashCode(fileName); - if (idAndNormalizedPixelPercentage is null) - { - (checkFile, idAndNormalizedPixelPercentage) = Shared.Models.Stateless.Methods.INamed.GetReversedDeterministicHashCode(keyValuePairs, file); - if (idAndNormalizedPixelPercentage is null) - break; - if (!string.IsNullOrEmpty(checkFile)) - File.Move(file, checkFile); - } - named.Add(new(personKey, idAndNormalizedPixelPercentage.Value)); + reversedDeterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); + if (reversedDeterministicHashCodeKey is null) + continue; + named.Add(new(personKey, reversedDeterministicHashCodeKey.Value)); } + foreach (string personNameLinkDirectory in personNameLinkDirectories) + { + files = Directory.GetFiles(personNameLinkDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string file in files) + { + if (!file.EndsWith(".lnk")) + continue; + File.Delete(file); + } + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(personNameLinkDirectory); + } + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(personNameDirectory); } + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(yearDirectory); } + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(personKeyDirectory); } + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(ticksDirectory); } } - private static void SetKeyValuePairs(string deterministicHashCodeRootDirectory, Dictionary namedDeterministicHashCode, Dictionary incorrectDeterministicHashCode, Dictionary> keyValuePairs) + private void SetKeyValuePairs(string deterministicHashCodeRootDirectory, Dictionary deterministicHashCodeKeyValuePairs, Dictionary incorrectDeterministicHashCodeKeyValuePairs, Dictionary> keyValuePairs) { Dictionary> namedKeyValuePairs = new(); Dictionary> incorrectKeyValuePairs = new(); @@ -205,36 +202,35 @@ public class MapLogic incorrectKeyValuePairs[idAndNormalizedPixelPercentage].Add(personKey); } foreach (KeyValuePair> keyValuePair in namedKeyValuePairs) - namedDeterministicHashCode.Add(keyValuePair.Key, keyValuePair.Value.Distinct().ToArray()); + deterministicHashCodeKeyValuePairs.Add(keyValuePair.Key, keyValuePair.Value.Distinct().ToArray()); foreach (KeyValuePair> keyValuePair in incorrectKeyValuePairs) - incorrectDeterministicHashCode.Add(keyValuePair.Key, keyValuePair.Value.Distinct().ToArray()); + incorrectDeterministicHashCodeKeyValuePairs.Add(keyValuePair.Key, keyValuePair.Value.Distinct().ToArray()); } - public void UpdateKeyValuePairs(List containers) + public void UseKeyValuePairsSaveFaceEncoding(List containers) { - Dictionary> keyValuePairs = new(); - Dictionary namedDeterministicHashCode = new(); - Dictionary incorrectDeterministicHashCode = new(); - foreach (Shared.Models.Container container in containers) + if (!string.IsNullOrEmpty(_DeterministicHashCodeRootDirectory)) { - foreach (Shared.Models.Item item in container.Items) + Dictionary> keyValuePairs = new(); + List<(string PersonKey, double IdAndNormalizedPixelPercentage)> named = new(); + List<(string PersonKey, double IdAndNormalizedPixelPercentage)> incorrect = new(); + foreach (Shared.Models.Container container in containers) { - if (item.ImageFileHolder is null || item.Property?.Id is null || !item.Faces.Any()) - continue; - if (keyValuePairs.ContainsKey(item.Property.Id.Value)) + foreach (Shared.Models.Item item in container.Items) { - if (keyValuePairs[item.Property.Id.Value].Count != item.Faces.Count) - throw new Exception(); - continue; + if (item.ImageFileHolder is null || item.Property?.Id is null || !item.Faces.Any()) + continue; + if (keyValuePairs.ContainsKey(item.Property.Id.Value)) + { + if (keyValuePairs[item.Property.Id.Value].Count != item.Faces.Count) + throw new Exception(); + continue; + } + keyValuePairs.Add(item.Property.Id.Value, item.Faces); } - keyValuePairs.Add(item.Property.Id.Value, item.Faces); } + SetKeyValuePairs(_DeterministicHashCodeRootDirectory, named, incorrect, keyValuePairs); } - SetKeyValuePairs(_DeterministicHashCodeRootDirectory, namedDeterministicHashCode, incorrectDeterministicHashCode, keyValuePairs); - foreach (KeyValuePair keyValuePair in namedDeterministicHashCode) - _NamedDeterministicHashCodeKeyValuePairs.Add(keyValuePair.Key, keyValuePair.Value); - foreach (KeyValuePair keyValuePair in incorrectDeterministicHashCode) - _IncorrectDeterministicHashCodeKeyValuePairs.Add(keyValuePair.Key, keyValuePair.Value); } public override string ToString() @@ -305,7 +301,7 @@ public class MapLogic } } - public static void AddToNamed(MapLogic mapLogic, List items) + public void AddToNamed(List items) { bool? isWrongYear; DateTime minimumDateTime; @@ -324,10 +320,10 @@ public class MapLogic if (face.LocationIndex is null) continue; deterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetDeterministicHashCodeKey(item, face); - if (!mapLogic.NamedDeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey)) + if (!_DeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey)) continue; minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - personKeys.AddRange(mapLogic.NamedDeterministicHashCodeKeyValuePairs[deterministicHashCodeKey]); + personKeys.AddRange(_DeterministicHashCodeKeyValuePairs[deterministicHashCodeKey]); (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); for (int i = 0; i < personKeys.Count; i++) { @@ -341,10 +337,10 @@ public class MapLogic } if (!personKeys.Any()) { - if (!mapLogic.NamedFaceInfoDeterministicHashCodeKeyValuePairs.ContainsKey(item.Property.Id.Value)) + if (!_DeterministicHashCodeUnknownFaceKeyValuePairs.ContainsKey(item.Property.Id.Value)) continue; minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - personKeys.AddRange(mapLogic.NamedFaceInfoDeterministicHashCodeKeyValuePairs[item.Property.Id.Value]); + personKeys.AddRange(_DeterministicHashCodeUnknownFaceKeyValuePairs[item.Property.Id.Value]); (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); for (int i = 0; i < personKeys.Count; i++) { @@ -357,7 +353,7 @@ public class MapLogic } } - public static List<(Shared.Models.Item, (string, Shared.Models.Face?, (string, string, string, string))[])> GetCollection(MapLogic mapLogic, List items, string dFacesContentDirectory) + public List<(Shared.Models.Item, (string, Shared.Models.Face?, (string, string, string, string))[])> GetCollection(List items, string dFacesContentDirectory) { List<(Shared.Models.Item, (string, Shared.Models.Face?, (string, string, string, string))[])> results = new(); string[] keys; @@ -393,7 +389,7 @@ public class MapLogic if (item.Property?.Id is null || item.ResizedFileHolder is null) continue; collection = new(); - if (!mapLogic.NamedFaceInfoDeterministicHashCodeKeyValuePairs.ContainsKey(item.Property.Id.Value)) + if (!_DeterministicHashCodeUnknownFaceKeyValuePairs.ContainsKey(item.Property.Id.Value)) { faceCollection = new(); personKey = string.Empty; @@ -402,7 +398,7 @@ public class MapLogic else { faceCollection = item.Faces; - keys = mapLogic.NamedFaceInfoDeterministicHashCodeKeyValuePairs[item.Property.Id.Value]; + keys = _DeterministicHashCodeUnknownFaceKeyValuePairs[item.Property.Id.Value]; minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); if (minimumDateTime is null) continue; diff --git a/Shared/Models/Item.cs b/Shared/Models/Item.cs index 1ad42b7..7fedaf7 100644 --- a/Shared/Models/Item.cs +++ b/Shared/Models/Item.cs @@ -85,7 +85,7 @@ public class Item : Properties.IItem else if (fileHolder.ExtensionLowered.Length > 3 && fileHolder.ExtensionLowered[3] == 'e' && fileHolder.ExtensionLowered.Remove(3, 1) == filenameExtension) _ResizedFileHolder = fileHolder; else - throw new NotImplementedException(); + _ResizedFileHolder = fileHolder; } public bool Any() => (_Abandoned.HasValue && _Abandoned.Value) || (_Changed.HasValue && _Changed.Value) || (_Moved.HasValue && _Moved.Value) || (_NoJson.HasValue && _NoJson.Value); diff --git a/Shared/Models/Location.cs b/Shared/Models/Location.cs index 5cdafc6..02e779f 100644 --- a/Shared/Models/Location.cs +++ b/Shared/Models/Location.cs @@ -108,8 +108,6 @@ public class Location : Properties.ILocation, IEquatable { int result; double value; - int decimals = 6; - int factor = 1000000; double total = width * height; Check(bottom, left, right, top, zCount); double xCenter = left + ((right - left) / 2); @@ -118,7 +116,7 @@ public class Location : Properties.ILocation, IEquatable value = at / total; if (value < 0) value = 3; - result = (int)(Math.Round(value, decimals) * factor); + result = (int)(Math.Round(value, Stateless.ILocation.Decimals) * Stateless.ILocation.Factor); return result; } diff --git a/Shared/Models/Stateless/IClosest.cs b/Shared/Models/Stateless/IClosest.cs new file mode 100644 index 0000000..70bd684 --- /dev/null +++ b/Shared/Models/Stateless/IClosest.cs @@ -0,0 +1,11 @@ +namespace View_by_Distance.Shared.Models.Stateless; + +public interface IClosest +{ + + const int MaximumPer = 50; + const float MaximumMinimum = 0.50f; + const bool SkipIsWrongYear = true; + const float MinimumMinimum = 0.05f; + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/ILocation.cs b/Shared/Models/Stateless/ILocation.cs new file mode 100644 index 0000000..089a958 --- /dev/null +++ b/Shared/Models/Stateless/ILocation.cs @@ -0,0 +1,9 @@ +namespace View_by_Distance.Shared.Models.Stateless; + +public interface ILocation +{ + + const int Decimals = 6; + const int Factor = 1000000; + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/IPerson.cs b/Shared/Models/Stateless/IPerson.cs new file mode 100644 index 0000000..629759c --- /dev/null +++ b/Shared/Models/Stateless/IPerson.cs @@ -0,0 +1,7 @@ +namespace View_by_Distance.Shared.Models.Stateless; + +public interface IPerson +{ + const string KeyFormat = "yyyy-MM-dd_HH"; + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Closest.cs b/Shared/Models/Stateless/Methods/Closest.cs index e918679..6e9a496 100644 --- a/Shared/Models/Stateless/Methods/Closest.cs +++ b/Shared/Models/Stateless/Methods/Closest.cs @@ -3,6 +3,6 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Closest { - internal static Models.Closest[] Get(List collection) => (from l in collection orderby l.Minimum < IClosest.MinimumMinimum, l.Average select l).ToArray(); + internal static Models.Closest[] Get(List collection) => (from l in collection orderby l.Minimum < Stateless.IClosest.MinimumMinimum, l.Average select l).ToArray(); } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IClosest.cs b/Shared/Models/Stateless/Methods/IClosest.cs index 439400f..12c8722 100644 --- a/Shared/Models/Stateless/Methods/IClosest.cs +++ b/Shared/Models/Stateless/Methods/IClosest.cs @@ -3,11 +3,6 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface IClosest { // ... - const int MaximumPer = 50; - const float MaximumMinimum = 0.50f; - const bool SkipIsWrongYear = true; - const float MinimumMinimum = 0.05f; - Models.Closest[] TestStatic_Get(List collection); static Models.Closest[] Get(List collection) => Closest.Get(collection); diff --git a/Shared/Models/Stateless/Methods/INamed.cs b/Shared/Models/Stateless/Methods/INamed.cs index 5aa4652..abbd564 100644 --- a/Shared/Models/Stateless/Methods/INamed.cs +++ b/Shared/Models/Stateless/Methods/INamed.cs @@ -3,9 +3,9 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface INamed { // ... - double? TestStatic_GetReversedDeterministicHashCode(string fileName); - static double? GetReversedDeterministicHashCode(string fileName) => - Named.GetReversedDeterministicHashCode(fileName); + double? TestStatic_GetReversedDeterministicHashCodeKey(string file); + static double? GetReversedDeterministicHashCodeKey(bool keyValuePairsAny, Dictionary> keyValuePairs, string file) => + Named.GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); double TestStatic_GetDeterministicHashCodeKey(Models.Item item, Models.Face face); static double GetDeterministicHashCodeKey(Models.Item item, Models.Face face) => @@ -15,8 +15,4 @@ public interface INamed static double GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest) => Named.GetDeterministicHashCodeKey(item, closest); - (string?, double?) TestStatic_GetReversedDeterministicHashCode(Dictionary> keyValuePairs, string file); - static (string?, double?) GetReversedDeterministicHashCode(Dictionary> keyValuePairs, string file) => - Named.GetReversedDeterministicHashCode(keyValuePairs, file); - } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Named.cs b/Shared/Models/Stateless/Methods/Named.cs index 83295b3..7c482c5 100644 --- a/Shared/Models/Stateless/Methods/Named.cs +++ b/Shared/Models/Stateless/Methods/Named.cs @@ -1,17 +1,19 @@ +using System.Text.Json; + namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Named { - private static double GetDeterministicHashCodeFileName(int id, int normalizedPixelPercentage) - => double.Parse($"{id}.{normalizedPixelPercentage}"); + private static double GetDeterministicHashCodeKey(int id, int normalizedPixelPercentage) + => Math.Round(double.Parse($"{id}.{normalizedPixelPercentage}"), Stateless.ILocation.Decimals); internal static double GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest) { double result; if (item.Property?.Id is null || item.ImageFileHolder is null || closest.NormalizedPixelPercentage is null) throw new NullReferenceException(); - result = GetDeterministicHashCodeFileName(item.Property.Id.Value, closest.NormalizedPixelPercentage.Value); + result = GetDeterministicHashCodeKey(item.Property.Id.Value, closest.NormalizedPixelPercentage.Value); return result; } @@ -20,127 +22,70 @@ internal abstract class Named double result; if (item.Property?.Id is null || item.ImageFileHolder is null || face.Location?.NormalizedPixelPercentage is null) throw new NullReferenceException(); - result = GetDeterministicHashCodeFileName(item.Property.Id.Value, face.Location.NormalizedPixelPercentage.Value); + result = GetDeterministicHashCodeKey(item.Property.Id.Value, face.Location.NormalizedPixelPercentage.Value); return result; } - private static double? GetReversedDeterministicHashCodeB(string fileName) + private static void UseKeyValuePairsSaveFaceEncoding(Dictionary> keyValuePairs, string file, int id, int normalizedPixelPercentageValue, double deterministicHashCodeKey, string extensionLowered) + { + string json; + string checkFile; + string? directoryName; + List collection = new(); + List faces = keyValuePairs[id]; + foreach (Models.Face face in faces) + { + if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) + continue; + if (normalizedPixelPercentageValue != face.Location.NormalizedPixelPercentage.Value && deterministicHashCodeKey != GetDeterministicHashCodeKey(id, face.Location.NormalizedPixelPercentage.Value)) + continue; + collection.Add(face); + } + if (collection.Count != 1) + throw new Exception(); + foreach (Models.Face face in collection) + { + directoryName = Path.GetDirectoryName(file); + if (string.IsNullOrEmpty(directoryName)) + continue; + checkFile = Path.Combine(directoryName, $"{deterministicHashCodeKey}{extensionLowered}.json"); + if (File.Exists(checkFile)) + continue; + json = JsonSerializer.Serialize(face.FaceEncoding); + _ = IPath.WriteAllText(checkFile, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + } + } + + private static double? GetReversedDeterministicHashCodeFromSegments(bool keyValuePairsAny, Dictionary> keyValuePairs, string file, string[] segments) { double? result; - string[] segments = fileName.Split('.'); - if (segments.Length < 2) + if (segments.Length != 3) throw new Exception(); string id = segments[0]; string normalizedPixelPercentage = segments[1]; - if (!double.TryParse(string.Concat(id, '.', normalizedPixelPercentage), out double resultValue)) + if (!int.TryParse(id, out int idValue) || !int.TryParse(normalizedPixelPercentage, out int normalizedPixelPercentageValue)) result = null; else - result = resultValue; + { + result = GetDeterministicHashCodeKey(idValue, normalizedPixelPercentageValue); + if (keyValuePairsAny && keyValuePairs.ContainsKey(idValue)) + UseKeyValuePairsSaveFaceEncoding(keyValuePairs, file, idValue, normalizedPixelPercentageValue, result.Value, $".{segments[2]}"); + } return result; } - internal static double? GetReversedDeterministicHashCode(string fileName) + internal static double? GetReversedDeterministicHashCodeKey(bool keyValuePairsAny, Dictionary> keyValuePairs, string file) { double? result; + string fileName = Path.GetFileName(file); if (fileName.Length < 2 || fileName[1..].Contains('-')) result = null; else - result = GetReversedDeterministicHashCodeB(fileName); + { + string[] segments = fileName.Split('.'); + result = GetReversedDeterministicHashCodeFromSegments(keyValuePairsAny, keyValuePairs, file, segments); + } return result; } - private static (string? check, int? normalizedPixelPercentage) GetReversedDeterministicHashCodeFromFace(string file, int idValue, int locationIndexValue, List faces) - { - string? check; - int? normalizedPixelPercentage; - Models.Face face = faces[locationIndexValue]; - if (face.Location?.NormalizedPixelPercentage is null) - { - check = null; - normalizedPixelPercentage = null; - } - else - { - string extensionLowered = Path.GetExtension(file).ToLower(); - normalizedPixelPercentage = face.Location.NormalizedPixelPercentage.Value; - double deterministicHashCodeKey = GetDeterministicHashCodeFileName(idValue, normalizedPixelPercentage.Value); - check = Path.Combine(string.Concat(Path.GetDirectoryName(file)), $"{deterministicHashCodeKey}{extensionLowered}"); - } - return (check, normalizedPixelPercentage); - } - - private static (string? check, int? normalizedPixelPercentage) GetReversedDeterministicHashCodeAfterParse(Dictionary> keyValuePairs, string file, int idValue, int locationIndexValue) - { - string? check; - int? normalizedPixelPercentage; - List faces = keyValuePairs[idValue]; - if (faces.Count > locationIndexValue) - (check, normalizedPixelPercentage) = GetReversedDeterministicHashCodeFromFace(file, idValue, locationIndexValue, faces); - else - { - check = null; - normalizedPixelPercentage = null; - } - return (check, normalizedPixelPercentage); - } - - private static (string? check, string id, int? normalizedPixelPercentage) GetReversedDeterministicHashCodeFromCollection(Dictionary> keyValuePairs, string file, string fileName) - { - string id; - string? check; - int? normalizedPixelPercentage; - string[] segments = fileName.Split(' '); - if (segments.Length < 3) - throw new Exception(); - id = segments[2].Split('.')[0]; - string locationIdex = segments[0]; - if (int.TryParse(id, out int idValue) && int.TryParse(locationIdex, out int locationIndexValue) && keyValuePairs.ContainsKey(idValue)) - (check, normalizedPixelPercentage) = GetReversedDeterministicHashCodeAfterParse(keyValuePairs, file, idValue, locationIndexValue); - else - { - check = null; - id = string.Empty; - normalizedPixelPercentage = null; - } - if (normalizedPixelPercentage is null) - id = string.Empty; - return new(check, id, normalizedPixelPercentage); - } - - private static (string?, double?) GetReversedDeterministicHashCode(Dictionary> keyValuePairs, string file, string fileName) - { - double? result; - string? check; - string id; - int? normalizedPixelPercentage; - if (keyValuePairs.Any()) - (check, id, normalizedPixelPercentage) = GetReversedDeterministicHashCodeFromCollection(keyValuePairs, file, fileName); - else - { - check = null; - id = string.Empty; - normalizedPixelPercentage = null; - } - if (normalizedPixelPercentage is null || !double.TryParse(string.Concat(id, '.', normalizedPixelPercentage.Value), out double resultValue)) - result = null; - else - result = resultValue; - return new(check, result); - } - - internal static (string?, double?) GetReversedDeterministicHashCode(Dictionary> keyValuePairs, string file) - { - double? result; - string? check; - string fileName = Path.GetFileName(file); - if (fileName.Contains('-')) - (check, result) = GetReversedDeterministicHashCode(keyValuePairs, file, fileName); - else - { - check = null; - result = null; - } - return new(check, result); - } - } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Person.cs b/Shared/Models/Stateless/Methods/Person.cs index 4525465..a535f1a 100644 --- a/Shared/Models/Stateless/Methods/Person.cs +++ b/Shared/Models/Stateless/Methods/Person.cs @@ -65,7 +65,6 @@ internal abstract class Person Dictionary results = new(); string[] segments; DateTime personKey; - const string KeyFormat = "yyyy-MM-dd_HH"; DateTime incrementDate = new(1500, 1, 1); string[] lines = File.ReadAllLines(knownPeopleFile); _ = incrementDate.AddDays(lines.Length); @@ -77,8 +76,8 @@ internal abstract class Person segments = line.Replace(" //", "\t//").Split('\t'); if (segments.Length < 1) continue; - SetSegments(ref segments, KeyFormat, ref incrementDate); - personKey = DateTime.ParseExact(segments[0], KeyFormat, cultureInfo); + SetSegments(ref segments, Stateless.IPerson.KeyFormat, ref incrementDate); + personKey = DateTime.ParseExact(segments[0], Stateless.IPerson.KeyFormat, cultureInfo); if (results.ContainsKey(personKey)) continue; results.Add(personKey, segments); @@ -88,7 +87,7 @@ internal abstract class Person for (int i = 1; i < (1000 - countBefore); i++) { personKey = minimumDateTime.AddDays(i * -1); - results.Add(personKey, new string[] { personKey.ToString(KeyFormat) }); + results.Add(personKey, new string[] { personKey.ToString(Stateless.IPerson.KeyFormat) }); } return results.OrderBy(l => l.Key).ToDictionary(l => l.Key, l => l.Value); } diff --git a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs index cc4149f..0fb5375 100644 --- a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs +++ b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs @@ -214,7 +214,7 @@ public class UnitTestFace Image image = FaceRecognition.LoadImageFile(item.ResizedFileHolder.FullName); Assert.IsNotNull(image); FaceRecognition faceRecognition = new(_Configuration.NumberOfTimesToUpsample, _Configuration.NumberOfJitters, predictorModel, model, modelParameter); - List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary? FaceParts)> collection; + 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();