From 26edd826d5c7e9410cd806895f8ac6a9e5ba3324 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Wed, 28 Dec 2022 23:52:42 -0700 Subject: [PATCH] LocationContainer --- Distance/Models/_E_Distance.cs | 181 ++++++------------- Face/Models/_D_Face.cs | 69 ++++++- Instance/DlibDotNet.cs | 69 ++++--- Map/Models/MapLogic.cs | 2 +- Map/Models/Stateless/MapLogic.cs | 2 +- Metadata/Models/Stateless/IMetadata.cs | 8 +- Metadata/Models/Stateless/Metadata.cs | 2 +- Property/Models/A_Property.cs | 28 ++- Shared/Models/LocationContainer.cs | 6 + Shared/Models/Stateless/Methods/ILocation.cs | 29 ++- Shared/Models/Stateless/Methods/IProperty.cs | 5 - Shared/Models/Stateless/Methods/Location.cs | 109 +++++++++-- Shared/Models/Stateless/Methods/Property.cs | 26 --- Shared/View-by-Distance.Shared.csproj | 1 - 14 files changed, 317 insertions(+), 220 deletions(-) create mode 100644 Shared/Models/LocationContainer.cs diff --git a/Distance/Models/_E_Distance.cs b/Distance/Models/_E_Distance.cs index ce3fcbf..8aa0875 100644 --- a/Distance/Models/_E_Distance.cs +++ b/Distance/Models/_E_Distance.cs @@ -38,53 +38,14 @@ public partial class E_Distance _DistanceMoveUnableToMatch = distanceMoveUnableToMatch; } - private void MoveUnableToMatch(string eDistanceContentDirectory, string file, string fileName) + private static void MoveUnableToMatch(string file) { - bool check; - string? directoryName = Path.GetDirectoryName(file); - if (fileName is null || directoryName is null) - check = false; - else - { - if (string.IsNullOrEmpty(directoryName) || string.IsNullOrEmpty(directoryName) || !directoryName.Contains(eDistanceContentDirectory)) - check = false; - else - { - List directoryNames = new(); - string? checkDirectoryName = directoryName; - for (int i = 0; i < int.MaxValue; i++) - { - if (string.IsNullOrEmpty(checkDirectoryName)) - continue; - directoryNames.Add(Path.GetFileName(checkDirectoryName)); - checkDirectoryName = Path.GetDirectoryName(checkDirectoryName); - if (string.IsNullOrEmpty(checkDirectoryName)) - continue; - if (checkDirectoryName == eDistanceContentDirectory) - break; - } - if (string.IsNullOrEmpty(checkDirectoryName) || !directoryNames.Any() || !long.TryParse(directoryNames[^1][1..^1], out long directoryTicks)) - { - check = false; - File.Delete(file); - } - else - { - checkDirectoryName = Path.Combine(checkDirectoryName, $"({directoryTicks}_{string.Join('-', _RangeDistanceTolerance)})"); - for (int i = directoryNames.Count - 1 - 1; i > -1; i--) - checkDirectoryName = Path.Combine(checkDirectoryName, directoryNames[i]); - if (!Directory.Exists(checkDirectoryName)) - _ = Directory.CreateDirectory(checkDirectoryName); - File.Move(file, Path.Combine(checkDirectoryName, fileName)); - check = true; - } - } - } - if (check) - _Moved.Add(file); + string checkFile = string.Concat(file, ".unk"); + if (File.Exists(file) && !File.Exists(checkFile)) + File.Move(file, checkFile); } - private FaceDistanceContainer[] GetFaceDistanceContainers(MappingFromItem mappingFromItem, List filteredFaces) + private FaceDistanceContainer[] GetFaceDistanceContainers(MappingFromItem mappingFromItem, List intersectFaces) { FaceDistanceContainer[] results; int confidencePercent; @@ -92,7 +53,7 @@ public partial class E_Distance FaceDistance faceDistance; FaceDistanceContainer faceDistanceContainer; List collection = new(); - foreach (Face face in filteredFaces) + foreach (Face face in intersectFaces) { if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) throw new NotSupportedException(); @@ -104,7 +65,7 @@ public partial class E_Distance { faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); faceDistance = new(confidencePercent, faceEncoding, mappingFromItem.Id, mappingFromItem.IsWrongYear, mappingFromItem.MinimumDateTime, normalizedRectangle); - lock (filteredFaces) + lock (intersectFaces) face.SetFaceDistance(faceDistance); } faceDistanceContainer = new(face, faceDistance); @@ -126,42 +87,38 @@ public partial class E_Distance return faceDistanceEncodings; } - private List<(Face Face, double? Length)> GetValues(MappingFromItem mappingFromItem, Face[] faces, Shared.Models.FaceEncoding modelsFaceEncoding, int normalizedRectangle) + private List<(Face Face, double? Length)> GetValues(MappingFromItem mappingFromItem, List intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding) { List<(Face Face, double? Length)> results = new(); Face face; FaceDistance faceDistanceLength; - List filteredFaces = FilterByIntersect(faces, normalizedRectangle); - if (filteredFaces.Any()) + FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding = FaceRecognition.LoadFaceEncoding(modelsFaceEncoding.RawEncoding); + FaceDistance faceDistanceEncoding = new(faceRecognitionDotNetFaceEncoding); + FaceDistanceContainer[] faceDistanceContainers = GetFaceDistanceContainers(mappingFromItem, intersectFaces); + int faceDistanceContainersLength = faceDistanceContainers.Length; + if (faceDistanceContainersLength != intersectFaces.Count) + throw new NotSupportedException(); + List faceDistanceEncodings = GetFaceDistanceEncodings(faceDistanceContainers); + if (faceDistanceEncodings.Count != intersectFaces.Count) + throw new NotSupportedException(); + List faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding); + if (faceDistanceLengths.Count != faceDistanceContainersLength) + throw new NotSupportedException(); + for (int i = 0; i < intersectFaces.Count; i++) { - FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding = FaceRecognition.LoadFaceEncoding(modelsFaceEncoding.RawEncoding); - FaceDistance faceDistanceEncoding = new(faceRecognitionDotNetFaceEncoding); - FaceDistanceContainer[] faceDistanceContainers = GetFaceDistanceContainers(mappingFromItem, filteredFaces); - int faceDistanceContainersLength = faceDistanceContainers.Length; - if (faceDistanceContainersLength != filteredFaces.Count) + face = intersectFaces[i]; + faceDistanceLength = faceDistanceLengths[i]; + if (faceDistanceLength.Length is null) throw new NotSupportedException(); - List faceDistanceEncodings = GetFaceDistanceEncodings(faceDistanceContainers); - if (faceDistanceEncodings.Count != filteredFaces.Count) - throw new NotSupportedException(); - List faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding); - if (faceDistanceLengths.Count != faceDistanceContainersLength) - throw new NotSupportedException(); - for (int i = 0; i < filteredFaces.Count; i++) - { - face = filteredFaces[i]; - faceDistanceLength = faceDistanceLengths[i]; - if (faceDistanceLength.Length is null) - throw new NotSupportedException(); - results.Add(new(face, faceDistanceLength.Length.Value)); - } + results.Add(new(face, faceDistanceLength.Length.Value)); } return results; } - private (Face, double?)[] GetClosestFaceByDistanceIgnoringTolerance(MappingFromItem mappingFromItem, int normalizedRectangle, Face[] filteredFaces, Shared.Models.FaceEncoding modelsFaceEncoding) + private (Face, double?)[] GetClosestFaceByDistanceIgnoringTolerance(MappingFromItem mappingFromItem, List intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding) { (Face, double?)[] results; - List<(Face Face, double? Length)> collection = GetValues(mappingFromItem, filteredFaces, modelsFaceEncoding, normalizedRectangle); + List<(Face Face, double? Length)> collection = GetValues(mappingFromItem, intersectFaces, modelsFaceEncoding); results = (from l in collection orderby l.Length select l).Take(1).ToArray(); if (results.Any()) { @@ -240,70 +197,41 @@ public partial class E_Distance } } - private static List FilterByIntersect(Face[] faces, int normalizedRectangle) - { - List results = new(); - bool useOldWay; - double? percent; - System.Drawing.Rectangle checkRectangle; - System.Drawing.Rectangle sourceRectangle; - System.Drawing.Rectangle intersectRectangle; - foreach (Face face in faces) - { - if (face.Location is null || face.OutputResolution is null) - continue; - checkRectangle = new(face.Location.Left, face.Location.Top, face.Location.Right - face.Location.Left, face.Location.Bottom - face.Location.Top); - for (int i = 1; i < 3; i++) - { - useOldWay = i == 1; - sourceRectangle = Shared.Models.Stateless.Methods.ILocation.GetRectangle(checkRectangle, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, normalizedRectangle, face.OutputResolution, useOldWay); - intersectRectangle = System.Drawing.Rectangle.Intersect(checkRectangle, sourceRectangle); - if (intersectRectangle.Width == 0 || intersectRectangle.Height == 0) - continue; - percent = (double)intersectRectangle.Width * intersectRectangle.Height / (checkRectangle.Width * checkRectangle.Height); - if (percent < 0.000001) - continue; - results.Add(face); - } - } - return results; - } - - public void LookForMatchFacesAndPossiblyRename(string facesFileNameExtension, string eDistanceContentDirectory, MappingFromItem mappingFromItem, List faces, List<(bool c, string File, int NormalizedRectangle, IReadOnlyList? Directories)> collection) + public void LookForMatchFacesAndPossiblyRename(string facesFileNameExtension, string eDistanceContentDirectory, MappingFromItem mappingFromItem, List faces, List> collection) { string? json; string fileName; string[] matches; FileInfo? fileInfo; + List intersectFaces; List<(Face, double?)> checkFaces = new(); Shared.Models.FaceEncoding? modelsFaceEncoding; Face[] filteredFaces = (from l in faces where l.FaceEncoding is not null && l.Location is not null && l.OutputResolution is not null select l).ToArray(); if (filteredFaces.Length != faces.Count) checkFaces.Clear(); - foreach ((bool fromDistanceContent, string file, int normalizedRectangle, IReadOnlyList? directories) in collection) + foreach (LocationContainer? locationContainer in collection) { - if (!filteredFaces.Any()) - break; - if (_Renamed.Contains(file)) + if (_Renamed.Contains(locationContainer.File)) continue; - fileName = Path.GetFileName(file); - if (fromDistanceContent && _DuplicateMappedFaceFiles.Contains(fileName)) + fileName = Path.GetFileName(locationContainer.File); + if (locationContainer.FromDistanceContent && _DuplicateMappedFaceFiles.Contains(fileName)) continue; checkFaces.Clear(); - if (directories is null) + if (!locationContainer.Directories.Any()) { - if (fromDistanceContent) - throw new NullReferenceException(nameof(directories)); + if (locationContainer.FromDistanceContent) + throw new NullReferenceException(nameof(locationContainer.Directories)); continue; } - json = Metadata.Models.Stateless.IMetadata.GetFaceEncoding(directories); + json = Metadata.Models.Stateless.IMetadata.GetFaceEncoding(locationContainer.Directories); if (json is null) { if (_DistanceMoveUnableToMatch) - MoveUnableToMatch(eDistanceContentDirectory, file, fileName); + MoveUnableToMatch(locationContainer.File); continue; } - checkFaces.AddRange(GetMatchingFacesByFaceEncoding(filteredFaces, json)); + if (filteredFaces.Any()) + checkFaces.AddRange(GetMatchingFacesByFaceEncoding(filteredFaces, json)); if (checkFaces.Count == 1) _Debug.Add(0); if (checkFaces.Count != 1 && !string.IsNullOrEmpty(json)) @@ -312,44 +240,49 @@ public partial class E_Distance modelsFaceEncoding = JsonSerializer.Deserialize(json); if (modelsFaceEncoding is null) throw new NotSupportedException(); - checkFaces.AddRange(GetClosestFaceByDistanceIgnoringTolerance(mappingFromItem, normalizedRectangle, filteredFaces, modelsFaceEncoding)); + if (filteredFaces.Any()) + { + intersectFaces = Shared.Models.Stateless.Methods.ILocation.FilterByIntersect(filteredFaces, locationContainer.NormalizedRectangle); + if (intersectFaces.Any()) + checkFaces.AddRange(GetClosestFaceByDistanceIgnoringTolerance(mappingFromItem, intersectFaces, modelsFaceEncoding)); + } } if (!checkFaces.Any()) { if (_DistanceMoveUnableToMatch) - MoveUnableToMatch(eDistanceContentDirectory, file, fileName); + MoveUnableToMatch(locationContainer.File); continue; } if (checkFaces.Count != 1) { if (_DistanceMoveUnableToMatch) - MoveUnableToMatch(eDistanceContentDirectory, file, fileName); + MoveUnableToMatch(locationContainer.File); continue; } - fileInfo = CheckFileThenGetFileInfo(facesFileNameExtension, mappingFromItem, file, checkFaces); + fileInfo = CheckFileThenGetFileInfo(facesFileNameExtension, mappingFromItem, locationContainer.File, checkFaces); if (fileInfo is not null) { if (_DistanceRenameToMatch && fileInfo is not null) { if (fileInfo.Exists) - File.Delete(file); + File.Delete(locationContainer.File); else - File.Move(file, fileInfo.FullName); - _Renamed.Add(file); + File.Move(locationContainer.File, fileInfo.FullName); + _Renamed.Add(locationContainer.File); } continue; } if (_AllMappedFaceFileNames.Contains(fileName)) { lock (_AllMappedFaceFiles) - matches = (from l in _AllMappedFaceFiles where l != file && Path.GetFileName(l) == fileName select l).ToArray(); - if (fromDistanceContent && matches.Any()) - AppendMatchingDuplicates(file, matches); + matches = (from l in _AllMappedFaceFiles where l != locationContainer.File && Path.GetFileName(l) == fileName select l).ToArray(); + if (locationContainer.FromDistanceContent && matches.Any()) + AppendMatchingDuplicates(locationContainer.File, matches); } - if (!fromDistanceContent) + if (!locationContainer.FromDistanceContent) continue; lock (_AllMappedFaceFiles) - _AllMappedFaceFiles.Add(file); + _AllMappedFaceFiles.Add(locationContainer.File); lock (_AllMappedFaceFileNames) _AllMappedFaceFileNames.Add(fileName); } diff --git a/Face/Models/_D_Face.cs b/Face/Models/_D_Face.cs index fe511aa..3e2b7dd 100644 --- a/Face/Models/_D_Face.cs +++ b/Face/Models/_D_Face.cs @@ -140,6 +140,12 @@ public class D_Face #pragma warning disable CA1416 + private static (int width, int height) Get(string file) + { + using Bitmap source = new(file); + return new(source.Width, source.Height); + } + private PropertyItem GetPropertyItem(int id, string value) { PropertyItem result = (PropertyItem)_ConstructorInfo.Invoke(null); @@ -291,12 +297,60 @@ public class D_Face #pragma warning restore CA1416 - public List GetFaces(string dResultsFullGroupDirectory, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, List? mappingFromPhotoPrismCollection) + private static List> GetCollection(int outputResolutionWidth, int outputResolutionHeight, List> collection, List faces) + { + List> results = new(); + string? json; + int width, height; + Location? location; + Rectangle? rectangle; + List skip = new(); + OutputResolution? outputResolution = null; + foreach (Shared.Models.Face face in faces) + { + if (face.Location is null || face.OutputResolution is null) + continue; + skip.Add(Shared.Models.Stateless.Methods.ILocation.GetNormalizedRectangle(face.Location, ILocation.Digits, face.OutputResolution)); + } + foreach (LocationContainer locationContainer in collection) + { + if (locationContainer.Directories is null) + continue; + if (skip.Contains(locationContainer.NormalizedRectangle)) + continue; + foreach (Shared.Models.Face face in faces) + { + if (face.Location is not null && face.OutputResolution is not null) + continue; + json = Metadata.Models.Stateless.IMetadata.GetOutputResolution(locationContainer.Directories); + if (json is not null) + { + outputResolution = JsonSerializer.Deserialize(json); + if (outputResolution is not null && (outputResolution.Width != outputResolutionWidth || outputResolution.Height != outputResolutionHeight)) + continue; + } + (width, height) = Get(locationContainer.File); + rectangle = Shared.Models.Stateless.Methods.ILocation.GetRectangle(height, ILocation.Digits, ILocation.Factor, locationContainer.NormalizedRectangle, outputResolutionHeight, outputResolutionWidth, width); + if (rectangle is null) + continue; + location = Shared.Models.Stateless.Methods.ILocation.GetLocation(outputResolutionHeight, rectangle.Value, outputResolutionWidth); + if (location is null) + continue; + if (!results.Any(l => l.NormalizedRectangle == locationContainer.NormalizedRectangle)) + results.Add(new(locationContainer.FromDistanceContent, locationContainer.File, locationContainer.NormalizedRectangle, locationContainer.Directories, rectangle.Value, location)); + } + } + if (results.Any()) + outputResolution = null; + return results; + } + + public List GetFaces(string dResultsFullGroupDirectory, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, List>? collection, List? mappingFromPhotoPrismCollection) { List? results; if (string.IsNullOrEmpty(dResultsFullGroupDirectory)) throw new NullReferenceException(nameof(dResultsFullGroupDirectory)); - string json; + string? json; List? locations; string[] changesFrom = new string[] { nameof(A_Property), nameof(B_Metadata), nameof(C_Resize) }; List dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList(); @@ -334,10 +388,15 @@ public class D_Face parseExceptions.Add(nameof(D_Face)); } } - if (mappingFromPhotoPrismCollection is null || results is null) - locations = null; + List> containers; + if (results is null || collection is null) + containers = new(); else - locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(mappingFromPhotoPrismCollection, results); + containers = GetCollection(outputResolutionWidth, outputResolutionHeight, collection, results); + if (mappingFromPhotoPrismCollection is null || results is null) + locations = (from l in containers where l is not null select l.Location).ToList(); + else + locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(mappingFromPhotoPrismCollection, results, containers); if (results is null || (locations is not null && locations.Any())) { results = GetFaces(property, mappingFromItem, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, locations); diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index 2d7acc2..cae65f5 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -283,7 +283,7 @@ public partial class DlibDotNet return result; } - private void FullParallelForWork(A_Property propertyLogic, Dictionary?)>> idToMappedFaceFilesWithCollection, string outputResolution, string bResultsFullGroupDirectory, string cResultsFullGroupDirectory, string dResultsDateGroupDirectory, string dResultsFullGroupDirectory, string eDistanceContentDirectory, List> sourceDirectoryChanges, List propertyFileHolderCollection, List propertyCollection, List>> metadataCollections, List> resizeKeyValuePairs, List> imageFaceCollections, Container container, int index, Item item, DateTime[] containerDateTimes) + private void FullParallelForWork(A_Property propertyLogic, Dictionary>> idToMappedFaceFilesWithCollection, string outputResolution, string bResultsFullGroupDirectory, string cResultsFullGroupDirectory, string dResultsDateGroupDirectory, string dResultsFullGroupDirectory, string eDistanceContentDirectory, List> sourceDirectoryChanges, List propertyFileHolderCollection, List propertyCollection, List>> metadataCollections, List> resizeKeyValuePairs, List> imageFaceCollections, Container container, int index, Item item, DateTime[] containerDateTimes) { if (_Log is null) throw new NullReferenceException(nameof(_Log)); @@ -369,10 +369,14 @@ public partial class DlibDotNet int outputResolutionHeight = outputResolutionCollection[1]; int outputResolutionOrientation = outputResolutionCollection[2]; List? mappingFromPhotoPrismCollection; - List<(bool, string, int, IReadOnlyList?)>? collection; + List>? collection; + if (item.Property?.Id is null) + collection = null; + else + _ = idToMappedFaceFilesWithCollection.TryGetValue(item.Property.Id.Value, out collection); if (!_FileNameToCollection.TryGetValue(mappingFromItem.RelativePath[1..], out mappingFromPhotoPrismCollection)) mappingFromPhotoPrismCollection = null; - faces = _Faces.GetFaces(dResultsFullGroupDirectory, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, mappingFromPhotoPrismCollection); + faces = _Faces.GetFaces(dResultsFullGroupDirectory, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, collection, mappingFromPhotoPrismCollection); if (_AppSettings.MaxDegreeOfParallelism < 2) ticks = LogDelta(ticks, nameof(D_Face.GetFaces)); bool anyFacesSaved = _Faces.SaveFaces(dResultsFullGroupDirectory, subFileTuples, parseExceptions, mappingFromItem, facesDirectory, faces); @@ -380,8 +384,7 @@ public partial class DlibDotNet ticks = LogDelta(ticks, nameof(D_Face.SaveFaces)); if ((_Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch) && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution) - && !anyFacesSaved && item.Property?.Id is not null - && idToMappedFaceFilesWithCollection.TryGetValue(item.Property.Id.Value, out collection)) + && !anyFacesSaved && collection is not null) _Distance.LookForMatchFacesAndPossiblyRename(_Faces.FileNameExtension, eDistanceContentDirectory, mappingFromItem, faces, collection); if (_Configuration.SaveFaceLandmarkForOutputResolutions.Contains(outputResolution)) { @@ -405,7 +408,7 @@ public partial class DlibDotNet private int FullParallelWork(int maxDegreeOfParallelism, A_Property propertyLogic, - Dictionary?)>> idToMappedFaceFilesWithCollection, + Dictionary>> idToMappedFaceFilesWithCollection, string outputResolution, string bResultsFullGroupDirectory, string cResultsFullGroupDirectory, @@ -658,7 +661,7 @@ public partial class DlibDotNet int maxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism; List nullablePropertyCollection = new(); List>> metadataCollection = new(); - Dictionary?)>> idToMappedFaceFilesWithCollection = GetDictionary(ticks, a2PeopleContentDirectory, eDistanceContentDirectory); + Dictionary>> idToMappedFaceFilesWithCollection = GetDictionary(ticks, a2PeopleContentDirectory, eDistanceContentDirectory); string dResultsDateGroupDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(D_Face)); foreach (string outputResolution in _Configuration.OutputResolutions) { @@ -874,7 +877,7 @@ public partial class DlibDotNet List filteredItems = GetItems(argZero, containers); mapLogic.SaveShortcutsForOutputResolutions(filteredItems, mappingCollection, personKeyToCount); } - if (_Configuration.PersonCharactersToCopyTo.Length == 1) + if (_Configuration.PersonCharactersToCopyTo.Length == 1 && _Configuration.PersonCharacters.ToArray().Contains(_Configuration.PersonCharactersToCopyTo[0])) mapLogic.CopyAtLeastOneMappedFiles(_Configuration.PersonCharactersToCopyTo[0], dFacesContentDirectory, a2PeopleSingletonDirectory, mappingCollection); Dictionary> idToNormalizedRectangleToMapping = MapLogicSupport.GetIdToNormalizedRectangleToFace(mappingCollection); mapLogic.CopyManualFiles(dFacesContentDirectory, idToNormalizedRectangleToMapping); @@ -1008,11 +1011,11 @@ public partial class DlibDotNet return results; } - private void ParallelFor(string eDistanceContentDirectory, List<(bool, string, int, int, IReadOnlyList?)> collection, string file) + private void ParallelFor(string eDistanceContentDirectory, List<(bool, string, int, int, IReadOnlyList)> collection, string file) { const string lnk = ".lnk"; int? id, normalizedRectangle; - IReadOnlyList? directories; + IReadOnlyList directories; bool fromDistanceContent = !file.EndsWith(lnk) && file.Contains(eDistanceContentDirectory); if (!file.EndsWith(lnk)) (id, normalizedRectangle) = Shared.Models.Stateless.Methods.IMapping.GetConverted(_MapConfiguration.FacesFileNameExtension, file); @@ -1021,52 +1024,58 @@ public partial class DlibDotNet if (id is null || normalizedRectangle is null) return; if (file.EndsWith(lnk) || (!_Configuration.DistanceMoveUnableToMatch && !_Configuration.DistanceRenameToMatch)) - directories = null; + directories = new List(); else directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(file); lock (collection) collection.Add(new(fromDistanceContent, file, id.Value, normalizedRectangle.Value, directories)); } - private List<(bool, string, int, int, IReadOnlyList?)> GetCollection(long ticks, string? a2PeopleContentDirectory, string eDistanceContentDirectory) + private List<(bool, string, int, int, IReadOnlyList)> GetCollection(long ticks, string? a2PeopleContentDirectory, string eDistanceContentDirectory) { + string file; List files = new(); - List<(bool, string, int, int, IReadOnlyList?)> results = new(); + List<(bool, string, int, int, IReadOnlyList)> results = new(); files.AddRange(Map.Models.Stateless.Methods.IMapLogic.GetDisplayDirectoryAllFiles(_PersonContainers)); files.AddRange(Map.Models.Stateless.Methods.IMapLogic.DeleteEmptyDirectoriesAndGetMappedFaceFiles(_MapConfiguration, _PersonContainers, ticks, a2PeopleContentDirectory, eDistanceContentDirectory)); - // foreach (string file in files) - // { - // if (!file.EndsWith(".dup")) - // continue; - // if (!File.Exists(file)) - // continue; - // File.Move(file, file[..^4]); - // } - if (files.Any() && (_Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch)) + for (int i = 0; i < files.Count; i++) + { + file = files[i]; + if (!file.EndsWith(".dup") && !file.EndsWith(".unk")) + continue; + if (!File.Exists(file)) + continue; + File.Move(file, file[..^4]); + files[i] = file[..^4]; + } + string[] distictFiles = files.Distinct().ToArray(); + if (distictFiles.Any() && (_Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch)) { int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)"; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism }; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - using ProgressBar progressBar = new(files.Count, message, options); - _ = Parallel.For(0, files.Count, parallelOptions, (i, state) => + using ProgressBar progressBar = new(distictFiles.Length, message, options); + _ = Parallel.For(0, distictFiles.Length, parallelOptions, (i, state) => { progressBar.Tick(); - ParallelFor(eDistanceContentDirectory, results, files[i]); + ParallelFor(eDistanceContentDirectory, results, distictFiles[i]); }); } return results; } - private Dictionary?)>> GetDictionary(long ticks, string? a2PeopleContentDirectory, string eDistanceContentDirectory) + private Dictionary>> GetDictionary(long ticks, string? a2PeopleContentDirectory, string eDistanceContentDirectory) { - Dictionary?)>> results = new(); - List<(bool, string, int, int, IReadOnlyList?)> collection = GetCollection(ticks, a2PeopleContentDirectory, eDistanceContentDirectory); - foreach ((bool fromDistanceContent, string file, int id, int normalizedRectangle, IReadOnlyList? directories) in collection) + Dictionary>> results = new(); + LocationContainer noob; + List<(bool, string, int, int, IReadOnlyList)> collection = GetCollection(ticks, a2PeopleContentDirectory, eDistanceContentDirectory); + foreach ((bool fromDistanceContent, string file, int id, int normalizedRectangle, IReadOnlyList directories) in collection) { if (!results.ContainsKey(id)) results.Add(id, new()); - results[id].Add(new(fromDistanceContent, file, normalizedRectangle, directories)); + noob = new LocationContainer(fromDistanceContent, file, normalizedRectangle, directories, null, null); + results[id].Add(noob); } return results; } diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index 25d1fcc..f8a03c5 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -915,7 +915,7 @@ public class MapLogic : Shared.Models.Methods.IMapLogic collection.Clear(); windowsShortcut = WindowsShortcut.Load(file); fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); - if (windowsShortcut.Path is null) + if (string.IsNullOrEmpty(windowsShortcut.Path)) continue; if (!Directory.Exists(windowsShortcut.Path)) { diff --git a/Map/Models/Stateless/MapLogic.cs b/Map/Models/Stateless/MapLogic.cs index d336562..5d363ac 100644 --- a/Map/Models/Stateless/MapLogic.cs +++ b/Map/Models/Stateless/MapLogic.cs @@ -233,7 +233,7 @@ internal abstract class MapLogic if (!string.IsNullOrEmpty(a2PeopleContentDirectory)) File.WriteAllLines(Path.Combine(a2PeopleContentDirectory, "People - C.tsv"), from l in lines select l.Line); List<(string, string[], string)> collection = DeleteEmptyDirectoriesAndGetCollection(configuration, personKeyFormattedCollection, ticksDirectories, message); - foreach ((string personKeyFormatted, string[] personDisplayDirectoryNames, string mappedFaceFile) in collection) + foreach ((_, _, string mappedFaceFile) in collection) results.Add(mappedFaceFile); return results; } diff --git a/Metadata/Models/Stateless/IMetadata.cs b/Metadata/Models/Stateless/IMetadata.cs index 128b4d4..a54d17e 100644 --- a/Metadata/Models/Stateless/IMetadata.cs +++ b/Metadata/Models/Stateless/IMetadata.cs @@ -8,9 +8,9 @@ public interface IMetadata static string? GetFaceEncoding(IReadOnlyList directories) => Metadata.GetFaceEncoding(directories); - string? TestStatic_GetFaceX(IReadOnlyList directories) => - GetFaceX(directories); - static string? GetFaceX(IReadOnlyList directories) => - Metadata.GetFaceX(directories); + string? TestStatic_GetOutputResolution(IReadOnlyList directories) => + GetOutputResolution(directories); + static string? GetOutputResolution(IReadOnlyList directories) => + Metadata.GetOutputResolution(directories); } \ No newline at end of file diff --git a/Metadata/Models/Stateless/Metadata.cs b/Metadata/Models/Stateless/Metadata.cs index c20b1a4..996305c 100644 --- a/Metadata/Models/Stateless/Metadata.cs +++ b/Metadata/Models/Stateless/Metadata.cs @@ -25,7 +25,7 @@ internal class Metadata return result; } - internal static string? GetFaceX(IReadOnlyList directories) + internal static string? GetOutputResolution(IReadOnlyList directories) { string? result; List results = new(); diff --git a/Property/Models/A_Property.cs b/Property/Models/A_Property.cs index f938b5c..d8cfa1a 100644 --- a/Property/Models/A_Property.cs +++ b/Property/Models/A_Property.cs @@ -64,6 +64,32 @@ public class A_Property #pragma warning disable CA1416 + private static List GetMetadataDateTimesByPattern(string dateTimeFormat, FileHolder fileHolder) + { + List results = new(); + try + { + DateTime checkDateTime; + DateTime kristy = new(1976, 3, 8); + IReadOnlyList directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(fileHolder.FullName); + foreach (MetadataExtractor.Directory directory in directories) + { + foreach (MetadataExtractor.Tag tag in directory.Tags) + { + if (string.IsNullOrEmpty(tag.Description) || tag.Description.Length != dateTimeFormat.Length) + continue; + if (!DateTime.TryParseExact(tag.Description, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) + continue; + if (checkDateTime < kristy) + continue; + results.Add(checkDateTime); + } + } + } + catch (Exception) { } + return results; + } + private Shared.Models.Property GetImageProperty(FileHolder fileHolder, Shared.Models.Property? property, bool populateId, bool isIgnoreExtension, bool isValidImageFormatExtension, bool isValidMetadataExtensions, int? id) { Shared.Models.Property result; @@ -90,7 +116,7 @@ public class A_Property if (!isValidImageFormatExtension && isValidMetadataExtensions && fileHolder.Exists) { dateTimeFormat = "ddd MMM dd HH:mm:ss yyyy"; - List dateTimes = Shared.Models.Stateless.Methods.IProperty.GetMetadataDateTimesByPattern(dateTimeFormat, fileHolder); + List dateTimes = GetMetadataDateTimesByPattern(dateTimeFormat, fileHolder); if (dateTimes.Any()) dateTimeOriginal = dateTimes.Min(); } diff --git a/Shared/Models/LocationContainer.cs b/Shared/Models/LocationContainer.cs new file mode 100644 index 0000000..4f9fb45 --- /dev/null +++ b/Shared/Models/LocationContainer.cs @@ -0,0 +1,6 @@ +using System.Drawing; + +namespace View_by_Distance.Shared.Models; + +public record LocationContainer(bool FromDistanceContent, string File, int NormalizedRectangle, IReadOnlyList Directories, Rectangle? Rectangle, Location? Location) +{ } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/ILocation.cs b/Shared/Models/Stateless/Methods/ILocation.cs index 958a1ad..f8ea238 100644 --- a/Shared/Models/Stateless/Methods/ILocation.cs +++ b/Shared/Models/Stateless/Methods/ILocation.cs @@ -5,6 +5,16 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface ILocation { // ... + Models.Location? TestStatic_GetLocation(int height, Rectangle rectangle, int width) => + GetLocation(height, rectangle, width); + static Models.Location? GetLocation(int height, Rectangle rectangle, int width) => + Location.GetLocation(height, rectangle, width); + + List TestStatic_FilterByIntersect(Models.Face[] faces, int normalizedRectangle) => + FilterByIntersect(faces, normalizedRectangle); + static List FilterByIntersect(Models.Face[] faces, int normalizedRectangle) => + Location.FilterByIntersect(faces, normalizedRectangle); + Rectangle? TestStatic_GetRectangle(Models.OutputResolution outputResolution, DatabaseFile databaseFile, Marker marker) => GetRectangle(outputResolution, databaseFile, marker); static Rectangle? GetRectangle(Models.OutputResolution outputResolution, DatabaseFile databaseFile, Marker marker) => @@ -15,15 +25,20 @@ public interface ILocation static Models.Location? GetLocation(Models.OutputResolution outputResolution, DatabaseFile databaseFile, Marker marker) => Location.GetLocation(outputResolution, databaseFile, marker); - List TestStatic_GetLocations(List mappingFromPhotoPrismCollection, List faces) => - GetLocations(mappingFromPhotoPrismCollection, faces); - static List GetLocations(List mappingFromPhotoPrismCollection, List faces) => - Location.GetLocations(mappingFromPhotoPrismCollection, faces); + List TestStatic_GetLocations(List mappingFromPhotoPrismCollection, List faces, List> containers) => + GetLocations(mappingFromPhotoPrismCollection, faces, containers); + static List GetLocations(List mappingFromPhotoPrismCollection, List faces, List> containers) => + Location.GetLocations(mappingFromPhotoPrismCollection, faces, containers); - Rectangle TestStatic_GetRectangle(Rectangle checkRectangle, int locationDigits, int locationFactor, int normalizedRectangle, Models.OutputResolution outputResolution, bool useOldWay) => + Rectangle? TestStatic_GetRectangle(Rectangle checkRectangle, int locationDigits, int locationFactor, int normalizedRectangle, Models.OutputResolution outputResolution, bool useOldWay) => GetRectangle(checkRectangle, locationDigits, locationFactor, normalizedRectangle, outputResolution, useOldWay); - static Rectangle GetRectangle(Rectangle checkRectangle, int locationDigits, int locationFactor, int normalizedRectangle, Models.OutputResolution outputResolution, bool useOldWay) => - Location.GetRectangle(checkRectangle, locationDigits, locationFactor, normalizedRectangle, OutputResolution.Get(outputResolution).Height, OutputResolution.Get(outputResolution).Width, useOldWay); + static Rectangle? GetRectangle(Rectangle checkRectangle, int locationDigits, int locationFactor, int normalizedRectangle, Models.OutputResolution outputResolution, bool useOldWay) => + Location.GetRectangle(checkRectangle, OutputResolution.Get(outputResolution).Height, locationDigits, locationFactor, normalizedRectangle.ToString(), OutputResolution.Get(outputResolution).Width, useOldWay); + + Rectangle? TestStatic_GetRectangle(int height, int locationDigits, int locationFactor, int normalizedRectangle, int outputResolutionHeight, int outputResolutionWidth, int width) => + GetRectangle(height, locationDigits, locationFactor, normalizedRectangle, outputResolutionHeight, outputResolutionWidth, width); + static Rectangle? GetRectangle(int height, int locationDigits, int locationFactor, int normalizedRectangle, int outputResolutionHeight, int outputResolutionWidth, int width) => + Location.GetRectangle(height, locationDigits, locationFactor, normalizedRectangle.ToString(), outputResolutionHeight, outputResolutionWidth, width); string TestStatic_GetLeftPadded(int locationDigits, string value) => GetLeftPadded(locationDigits, value); diff --git a/Shared/Models/Stateless/Methods/IProperty.cs b/Shared/Models/Stateless/Methods/IProperty.cs index 30853e0..fe7a680 100644 --- a/Shared/Models/Stateless/Methods/IProperty.cs +++ b/Shared/Models/Stateless/Methods/IProperty.cs @@ -11,11 +11,6 @@ public interface IProperty static int GetDeterministicHashCode(byte[] value) => Property.GetDeterministicHashCode(value); - List TestStatic_GetMetadataDateTimesByPattern(string dateTimeFormat, Models.FileHolder fileHolder) => - GetMetadataDateTimesByPattern(dateTimeFormat, fileHolder); - static List GetMetadataDateTimesByPattern(string dateTimeFormat, Models.FileHolder fileHolder) => - Property.GetMetadataDateTimesByPattern(dateTimeFormat, fileHolder.FullName); - int TestStatic_GetDeterministicHashCode(string value) => GetDeterministicHashCode(value); static int GetDeterministicHashCode(string value) => diff --git a/Shared/Models/Stateless/Methods/Location.cs b/Shared/Models/Stateless/Methods/Location.cs index 54966d7..53bc69f 100644 --- a/Shared/Models/Stateless/Methods/Location.cs +++ b/Shared/Models/Stateless/Methods/Location.cs @@ -167,26 +167,43 @@ internal abstract class Location return result; } - internal static Rectangle GetRectangle(Rectangle checkRectangle, int locationDigits, int locationFactor, int normalizedRectangleValue, int height, int width, bool useOldWay) + internal static Rectangle? GetRectangle(Rectangle checkRectangle, int height, int locationDigits, int locationFactor, string normalizedRectangle, int width, bool useOldWay) { Rectangle? result; - string normalizedRectangle = normalizedRectangleValue.ToString(); - if (normalizedRectangle.Length != locationDigits) - throw new NotImplementedException(); - if (!useOldWay) - { - result = GetRectangle(locationDigits, height, normalizedRectangle, width); - if (result is null) - throw new NullReferenceException(nameof(result)); - } - else + if (useOldWay) { (int? x, int? y) = GetXY(locationDigits, locationFactor, width, height, normalizedRectangle); if (x is null || y is null) throw new Exception(); result = new(x.Value - (checkRectangle.Width / 2), y.Value - (checkRectangle.Height / 2), checkRectangle.Width, checkRectangle.Height); } - return result.Value; + else + { + if (normalizedRectangle.Length != locationDigits) + result = null; + else + { + result = GetRectangle(locationDigits, height, normalizedRectangle, width); + if (result is null) + throw new NullReferenceException(nameof(result)); + } + } + return result; + } + + internal static Rectangle? GetRectangle(int height, int locationDigits, int locationFactor, string normalizedRectangle, int outputResolutionHeight, int outputResolutionWidth, int width) + { + Rectangle? result; + if (normalizedRectangle.Length == locationDigits && normalizedRectangle[0] is '4' or '8') + result = GetRectangle(locationDigits, outputResolutionHeight, normalizedRectangle, outputResolutionWidth); + else + { + (int? x, int? y) = GetXY(locationDigits, locationFactor, outputResolutionWidth, outputResolutionHeight, normalizedRectangle); + if (x is null || y is null) + throw new Exception(); + result = new(x.Value - (width / 2), y.Value - (height / 2), width, height); + } + return result; } private static bool Matches(Models.OutputResolution outputResolution, DatabaseFile databaseFile) @@ -217,6 +234,18 @@ internal abstract class Location return result; } + internal static Models.Location? GetLocation(int height, Rectangle rectangle, int width) + { + Models.Location? result; + double confidence = 0; + bool verified = Check(rectangle.Bottom, height, rectangle.Left, rectangle.Right, rectangle.Top, width, zCount: 1, throwException: false); + if (!verified) + result = null; + else + result = new(rectangle.Bottom, confidence, rectangle.Left, rectangle.Right, rectangle.Top); + return result; + } + internal static Models.Location? GetLocation(Models.OutputResolution outputResolution, DatabaseFile databaseFile, Marker marker) { Models.Location? result; @@ -228,7 +257,7 @@ internal abstract class Location return result; } - internal static List GetLocations(List mappingFromPhotoPrismCollection, List faces) + internal static List GetLocations(List mappingFromPhotoPrismCollection, List faces, List> containers) { List results = new(); bool any; @@ -246,6 +275,12 @@ internal abstract class Location outputResolution ??= face.OutputResolution; } int before = results.Count; + foreach (LocationContainer locationContainer in containers) + { + if (locationContainer.Location is null) + continue; + results.Add(locationContainer.Location); + } foreach (MappingFromPhotoPrism mappingFromPhotoPrism in mappingFromPhotoPrismCollection) { if (outputResolution is null) @@ -262,12 +297,27 @@ internal abstract class Location location = GetLocation(mappingFromPhotoPrism.DatabaseFile, marker, prismRectangle.Value); if (location is null) break; + foreach (LocationContainer locationContainer in containers) + { + if (any) + continue; + if (locationContainer.Location is null) + continue; + dlibRectangle = new(locationContainer.Location.Left, locationContainer.Location.Top, locationContainer.Location.Right - locationContainer.Location.Left, locationContainer.Location.Bottom - locationContainer.Location.Top); + intersectRectangle = Rectangle.Intersect(prismRectangle.Value, dlibRectangle); + if (intersectRectangle.Width == 0 && intersectRectangle.Height == 0) + continue; + any = true; + break; + } foreach (Models.Face face in faces) { + if (any) + continue; if (face.Location is null || face.OutputResolution is null) continue; dlibRectangle = new(face.Location.Left, face.Location.Top, face.Location.Right - face.Location.Left, face.Location.Bottom - face.Location.Top); - intersectRectangle = Rectangle.Intersect(dlibRectangle, prismRectangle.Value); + intersectRectangle = Rectangle.Intersect(prismRectangle.Value, dlibRectangle); if (intersectRectangle.Width == 0 && intersectRectangle.Height == 0) continue; any = true; @@ -282,4 +332,35 @@ internal abstract class Location return results; } + internal static List FilterByIntersect(Models.Face[] faces, int normalizedRectangle) + { + List results = new(); + bool useOldWay; + double? percent; + Rectangle checkRectangle; + Rectangle? sourceRectangle; + Rectangle intersectRectangle; + foreach (Models.Face face in faces) + { + if (face.Location is null || face.OutputResolution is null) + continue; + checkRectangle = new(face.Location.Left, face.Location.Top, face.Location.Right - face.Location.Left, face.Location.Bottom - face.Location.Top); + for (int i = 1; i < 3; i++) + { + useOldWay = i == 1; + sourceRectangle = ILocation.GetRectangle(checkRectangle, Stateless.ILocation.Digits, Stateless.ILocation.Factor, normalizedRectangle, face.OutputResolution, useOldWay); + if (sourceRectangle is null) + continue; + intersectRectangle = Rectangle.Intersect(checkRectangle, sourceRectangle.Value); + if (intersectRectangle.Width == 0 || intersectRectangle.Height == 0) + continue; + percent = (double)intersectRectangle.Width * intersectRectangle.Height / (checkRectangle.Width * checkRectangle.Height); + if (percent < 0.000001) + continue; + results.Add(face); + } + } + return results; + } + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Property.cs b/Shared/Models/Stateless/Methods/Property.cs index dfd7410..851dbd3 100644 --- a/Shared/Models/Stateless/Methods/Property.cs +++ b/Shared/Models/Stateless/Methods/Property.cs @@ -343,32 +343,6 @@ internal abstract class Property return result; } - internal static List GetMetadataDateTimesByPattern(string dateTimeFormat, string sourceDirectoryFile) - { - List results = new(); - try - { - DateTime checkDateTime; - DateTime kristy = new(1976, 3, 8); - IReadOnlyList directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(sourceDirectoryFile); - foreach (MetadataExtractor.Directory directory in directories) - { - foreach (MetadataExtractor.Tag tag in directory.Tags) - { - if (string.IsNullOrEmpty(tag.Description) || tag.Description.Length != dateTimeFormat.Length) - continue; - if (!DateTime.TryParseExact(tag.Description, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) - continue; - if (checkDateTime < kristy) - continue; - results.Add(checkDateTime); - } - } - } - catch (Exception) { } - return results; - } - #pragma warning disable CA1416 internal static (DateTime?, int?, string?) Get(Models.FileHolder fileHolder) diff --git a/Shared/View-by-Distance.Shared.csproj b/Shared/View-by-Distance.Shared.csproj index 93f5579..1fc9aea 100644 --- a/Shared/View-by-Distance.Shared.csproj +++ b/Shared/View-by-Distance.Shared.csproj @@ -35,6 +35,5 @@ - \ No newline at end of file