From 7f8b09e66c41f76d79152ad9ff0ca7122cfbc645 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Sat, 25 May 2024 23:05:37 -0700 Subject: [PATCH] ReSaveFace Removed GetLocationContainers Split SetCreationTime and MoveToDecade GetFilteredDistinctFileNameFirstSegments instead of GetFilteredDistinctIds for some void Tick(); FaceFile --- .vscode/tasks.json | 10 ++ Distance/Models/_E_Distance.cs | 12 +- Face/Models/_D_Face.cs | 130 +++++++----------- FaceRecognitionDotNet/FaceRecognition.cs | 6 +- Instance/DlibDotNet.cs | 60 ++++---- Map/Models/MapLogic.cs | 16 ++- Map/Models/Stateless/DecadeLogic.cs | 25 ++-- Map/Models/Stateless/LookForAbandonedLogic.cs | 9 +- Map/Models/Stateless/Methods/IMapLogic.cs | 38 ++--- Metadata/Models/Stateless/Exif.cs | 8 +- Shared/Models/FaceFile.cs | 17 +++ Shared/Models/Stateless/Methods/Container.cs | 47 +++++-- Shared/Models/Stateless/Methods/IContainer.cs | 13 +- .../Models/Stateless/Methods/IDlibDotNet.cs | 8 ++ Shared/Models/Stateless/Methods/IFaceD.cs | 9 ++ Shared/Models/Stateless/Methods/ILocation.cs | 8 +- Shared/Models/Stateless/Methods/Location.cs | 21 +-- Shared/Models/Stateless/Methods/XDirectory.cs | 9 +- 18 files changed, 260 insertions(+), 186 deletions(-) create mode 100644 Shared/Models/FaceFile.cs create mode 100644 Shared/Models/Stateless/Methods/IDlibDotNet.cs create mode 100644 Shared/Models/Stateless/Methods/IFaceD.cs diff --git a/.vscode/tasks.json b/.vscode/tasks.json index bf3ef5b..501194d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -42,6 +42,16 @@ ], "problemMatcher": "$msCompile" }, + { + "label": "Format-Whitespaces", + "command": "dotnet", + "type": "process", + "args": [ + "format", + "whitespace" + ], + "problemMatcher": "$msCompile" + }, { "label": "build", "command": "dotnet", diff --git a/Distance/Models/_E_Distance.cs b/Distance/Models/_E_Distance.cs index e85b0a1..20b0774 100644 --- a/Distance/Models/_E_Distance.cs +++ b/Distance/Models/_E_Distance.cs @@ -205,14 +205,14 @@ public partial class E_Distance : IDistance } } - public void LookForMatchFacesAndPossiblyRename(IDistanceLimits distanceLimits, string facesFileNameExtension, FilePath filePath, MappingFromItem mappingFromItem, List faces, ReadOnlyCollection locationContainers) + public void LookForMatchFacesAndPossiblyRename(IDistanceLimits distanceLimits, Shared.Models.Stateless.Methods.IFaceD dFace, FilePath filePath, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces, ReadOnlyCollection locationContainers) { string? json; string[] matches; FileInfo? fileInfo; List intersectFaces; - List<(Face, double?)> checkFaces = []; Shared.Models.FaceEncoding? modelsFaceEncoding; + List<(Face Face, double? Distance)> checkFaces = []; 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(); @@ -265,7 +265,7 @@ public partial class E_Distance : IDistance MoveUnableToMatch(locationContainer.FilePath); continue; } - fileInfo = CheckFileThenGetFileInfo(facesFileNameExtension, filePath, mappingFromItem, locationContainer.FilePath.FullName, checkFaces); + fileInfo = CheckFileThenGetFileInfo(dFace.FileNameExtension, filePath, mappingFromItem, locationContainer.FilePath.FullName, checkFaces); if (fileInfo is not null) { if (_DistanceRenameToMatch && fileInfo is not null) @@ -279,6 +279,12 @@ public partial class E_Distance : IDistance } continue; } + if (checkFaces.Count == 1) + { + json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(locationContainer.ExifDirectory); + if (json is null || json.Contains(nameof(DateTime))) + dFace.ReSaveFace(exifDirectory, locationContainer, checkFaces[0].Face); + } if (_AllMappedFaceFileNames.Contains(locationContainer.FilePath.Name)) { lock (_AllMappedFaceFiles) diff --git a/Face/Models/_D_Face.cs b/Face/Models/_D_Face.cs index 64ff2af..f5209d9 100644 --- a/Face/Models/_D_Face.cs +++ b/Face/Models/_D_Face.cs @@ -7,18 +7,18 @@ using System.Text.Json.Serialization; using View_by_Distance.FaceRecognitionDotNet; using View_by_Distance.Metadata.Models; using View_by_Distance.Property.Models; -using View_by_Distance.Property.Models.Stateless; using View_by_Distance.Resize.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless; +using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Face.Models; /// // List /// -public class D_Face +public class D_Face : IFaceD { protected readonly string _FileNameExtension; @@ -149,22 +149,21 @@ public class D_Face #pragma warning disable CA1416 - private void SaveFaces(FileHolder resizedFileHolder, List<(Shared.Models.Face, FileInfo?, string, bool)> collection) + private void SaveFaces(FileHolder resizedFileHolder, MetadataExtractor.GeoLocation? geoLocation, string? maker, string? model, List<(Shared.Models.Face, FileInfo?, string, bool)> collection) { int width; int height; Bitmap bitmap; short type = 2; + FaceFile faceFile; Graphics graphics; Location? location; Rectangle rectangle; - string locationJson; + string faceFileJson; string faceEncodingJson; PropertyItem? propertyItem; - string outputResolutionJson; using Bitmap source = new(resizedFileHolder.FullName); int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; - int fileSource = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagFileSource; int userComment = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagUserComment; foreach ((Shared.Models.Face face, FileInfo? fileInfo, string fileName, bool save) in collection) { @@ -179,19 +178,17 @@ public class D_Face continue; width = location.Right - location.Left; height = location.Bottom - location.Top; - locationJson = JsonSerializer.Serialize(face.Location); faceEncodingJson = JsonSerializer.Serialize(face.FaceEncoding); - outputResolutionJson = JsonSerializer.Serialize(face.OutputResolution); rectangle = new Rectangle(location.Left, location.Top, width, height); + faceFile = new(face.DateTime, geoLocation?.ToDmsString(), face.FaceParts, face.Location, maker, model, face.OutputResolution); + faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile); using (bitmap = new(width, height)) { using (graphics = Graphics.FromImage(bitmap)) graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel); - propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, fileSource, type, locationJson); + propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson); bitmap.SetPropertyItem(propertyItem); - propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, outputResolutionJson); - bitmap.SetPropertyItem(propertyItem); - propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, userComment, type, faceEncodingJson); + propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(_ConstructorInfo, userComment, type, faceEncodingJson); bitmap.SetPropertyItem(propertyItem); bitmap.Save(fileInfo.FullName, _ImageCodecInfo, _EncoderParameters); } @@ -213,7 +210,7 @@ public class D_Face } } - private List GetFaces(string outputResolution, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List? locations) + private List GetFaces(string outputResolution, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List locations) { if (_PropertyConfiguration.NumberOfJitters is null) throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters)); @@ -260,69 +257,13 @@ public class D_Face #pragma warning restore CA1416 - private static List GetLocationContainers(string outputResolution, ReadOnlyCollection locationContainers, Dictionary outputResolutionToResize, List faces) - { - List results = []; - string? json; - Location? location; - List skip = []; - Rectangle? rectangle; - OutputResolution? outputResolutionCheck = null; - (int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation) = Resize.Models.Stateless.Methods.IResize.Get(outputResolution, outputResolutionToResize); - foreach (Shared.Models.Face face in faces) - { - if (face.Location is null || face.OutputResolution is null) - continue; - skip.Add(Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution)); - } - foreach (LocationContainer locationContainer in locationContainers) - { - if (locationContainer.ExifDirectory is null) - continue; - if (skip.Contains(locationContainer.WholePercentages)) - continue; - foreach (Shared.Models.Face face in faces) - { - if (face.Location is not null && face.OutputResolution is not null) - continue; - json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(locationContainer.ExifDirectory); - if (json is null) - continue; - outputResolutionCheck = JsonSerializer.Deserialize(json); - if (outputResolutionCheck is null || outputResolutionCheck.Width != outputResolutionWidth || outputResolutionCheck.Height != outputResolutionHeight) - continue; - rectangle = Shared.Models.Stateless.Methods.ILocation.GetRectangle(Shared.Models.Stateless.ILocation.Digits, outputResolutionCheck, locationContainer.WholePercentages); - 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.WholePercentages == locationContainer.WholePercentages)) - results.Add(new(locationContainer.CreationDateOnly, - locationContainer.ExifDirectory, - locationContainer.DirectoryNumber, - locationContainer.DisplayDirectoryName, - locationContainer.FilePath, - locationContainer.FromDistanceContent, - locationContainer.Id, - location, - locationContainer.PersonKey, - rectangle.Value, - locationContainer.WholePercentages)); - } - } - if (results.Count > 0) - outputResolutionCheck = null; - return results; - } - - public List GetFaces(string outputResolution, string dResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, ReadOnlyCollection? locationContainers, List? mappingFromPhotoPrismCollection) + public List GetFaces(string outputResolution, string dResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List? mappingFromPhotoPrismCollection) { List? results; if (string.IsNullOrEmpty(dResultsFullGroupDirectory)) throw new NullReferenceException(nameof(dResultsFullGroupDirectory)); string? json; - List? locations; + List locations; string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)]; List dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList(); (_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, filePath); @@ -359,16 +300,11 @@ public class D_Face parseExceptions.Add(nameof(D_Face)); } } - List collection; - if (results is null || locationContainers is null) - collection = []; - else - collection = GetLocationContainers(outputResolution, locationContainers, outputResolutionToResize, results); if (!_LoadPhotoPrismLocations || mappingFromPhotoPrismCollection is null || results is null) - locations = (from l in collection where l is not null select l.Location).ToList(); + locations = []; else - locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(collection, results, mappingFromPhotoPrismCollection, _RectangleIntersectMinimum); - if (results is null || (locations is not null && locations.Count > 0)) + locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(results, mappingFromPhotoPrismCollection, _RectangleIntersectMinimum); + if (results is null || locations.Count > 0) { results = GetFaces(outputResolution, property, mappingFromItem, outputResolutionToResize, locations); if (results.Count == 0) @@ -394,7 +330,7 @@ public class D_Face return results; } - public List<(Shared.Models.Face, FileInfo?, string, bool)> SaveFaces(string f, string dResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, MappingFromItem mappingFromItem, List faces) + public List<(Shared.Models.Face, FileInfo?, string, bool)> SaveFaces(string f, string dResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces) { List<(Shared.Models.Face, FileInfo?, string, bool Save)> results = []; bool save; @@ -429,9 +365,41 @@ public class D_Face { if (!directoryExists) _ = Directory.CreateDirectory(directory); - SaveFaces(mappingFromItem.ResizedFileHolder, results); + string? maker = Metadata.Models.Stateless.Methods.IMetadata.GetMaker(exifDirectory); + string? model = Metadata.Models.Stateless.Methods.IMetadata.GetModel(exifDirectory); + MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(exifDirectory); + SaveFaces(mappingFromItem.ResizedFileHolder, geoLocation, maker, model, results); } return results; } +#pragma warning disable CA1416 + + void IFaceD.ReSaveFace(ExifDirectory exifDirectory, LocationContainer locationContainer, Shared.Models.Face face) + { + FileInfo fileInfo = new(locationContainer.FilePath.FullName); + if (fileInfo.Exists) + { + short type = 2; + string checkFile = $"{locationContainer.FilePath.FullName}.exif"; + int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; + string? maker = Metadata.Models.Stateless.Methods.IMetadata.GetMaker(exifDirectory); + string? model = Metadata.Models.Stateless.Methods.IMetadata.GetModel(exifDirectory); + MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(exifDirectory); + FaceFile faceFile = new(face.DateTime, geoLocation?.ToDmsString(), face.FaceParts, face.Location, maker, model, face.OutputResolution); + string faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile); + ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception(); + PropertyItem? propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(constructorInfo, artist, type, faceFileJson); + Bitmap bitmap = new(fileInfo.FullName); + bitmap.SetPropertyItem(propertyItem); + bitmap.Save(checkFile); + bitmap.Dispose(); + File.SetLastWriteTime(checkFile, fileInfo.LastWriteTime); + File.Delete(fileInfo.FullName); + File.Move(checkFile, fileInfo.FullName); + } + } + +#pragma warning restore CA1416 + } \ No newline at end of file diff --git a/FaceRecognitionDotNet/FaceRecognition.cs b/FaceRecognitionDotNet/FaceRecognition.cs index 38dee50..b307aef 100644 --- a/FaceRecognitionDotNet/FaceRecognition.cs +++ b/FaceRecognitionDotNet/FaceRecognition.cs @@ -203,7 +203,7 @@ public class FaceRecognition : DisposableObject return results; } - public List<(Location, FaceEncoding?, Dictionary?)> GetCollection(Image image, List? locations, bool includeFaceEncoding, bool includeFaceParts) + public List<(Location, FaceEncoding?, Dictionary?)> GetCollection(Image image, List locations, bool includeFaceEncoding, bool includeFaceParts) { List<(Location, FaceEncoding?, Dictionary?)> results = []; if (image is null) @@ -212,9 +212,7 @@ public class FaceRecognition : DisposableObject ThrowIfDisposed(); if (_PredictorModel == PredictorModel.Custom) throw new NotSupportedException("FaceRecognition.PredictorModel.Custom is not supported."); - if (locations is null) - locations = GetLocations(image); - else if (locations.Count == 0) + if (locations.Count == 0) locations.AddRange(GetLocations(image)); List fullObjectDetections = GetFullObjectDetections(image, locations); if (fullObjectDetections.Count != locations.Count) diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index 71ab2e4..74d8ffe 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -22,19 +22,20 @@ using WindowsShortcutFactory; namespace View_by_Distance.Instance; -public partial class DlibDotNet +public partial class DlibDotNet : IDlibDotNet, IDisposable { [GeneratedRegex(@"[\\,\/,\:,\*,\?,\"",\<,\>,\|]")] private static partial Regex CameraRegex(); private readonly D_Face _Faces; + private ProgressBar? _ProgressBar; private readonly C_Resize _Resize; private readonly F_Random _Random; private readonly IConsole _Console; private readonly E_Distance _Distance; - private readonly IBlurHasher _BlurHasher; private readonly D2_FaceParts _FaceParts; + private readonly IBlurHasher _BlurHasher; private readonly AppSettings _AppSettings; private readonly List _Exceptions; private readonly ILogger? _Logger; @@ -167,6 +168,15 @@ public partial class DlibDotNet _Logger?.LogInformation("First run completed. Run again if wanted"); } + void IDlibDotNet.Tick() => + _ProgressBar?.Tick(); + + void IDisposable.Dispose() + { + _ProgressBar?.Dispose(); + GC.SuppressFinalize(this); + } + private static void Verify(Models.Configuration configuration) { if (configuration.RangeDaysDeltaTolerance.Length != 3) @@ -361,15 +371,12 @@ public partial class DlibDotNet if (filesCollectionRootDirectory is null || filesCollection is null) throw new NullReferenceException(nameof(filesCollection)); message = $") Building Container(s) - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; - using (ProgressBar progressBar = new(2, message, options)) - { - progressBar.Tick(); - string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton); - if (!Directory.Exists(aPropertySingletonDirectory)) - _ = Directory.CreateDirectory(aPropertySingletonDirectory); - (t, containers) = Shared.Models.Stateless.Methods.IContainer.GetContainers(_Configuration.PropertyConfiguration, aPropertySingletonDirectory, filesCollectionRootDirectory, filesCollection); - progressBar.Tick(); - } + string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton); + if (!Directory.Exists(aPropertySingletonDirectory)) + _ = Directory.CreateDirectory(aPropertySingletonDirectory); + _ProgressBar = new(short.MaxValue, message, options); + (t, containers) = Shared.Models.Stateless.Methods.IContainer.GetContainers(this, _Configuration.PropertyConfiguration, aPropertySingletonDirectory, filesCollectionRootDirectory, filesCollection); + _ProgressBar.Dispose(); ReadOnlyCollection readOnlyContainers = new(containers); SaveDistinctIds(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers); mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); @@ -382,8 +389,10 @@ public partial class DlibDotNet string d2ResultsFullGroupDirectory; foreach (string outputResolution in _Configuration.OutputResolutions) { + _ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }); (cResultsFullGroupDirectory, _, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution); - mapLogic.LookForAbandoned(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); + mapLogic.LookForAbandoned(this, _Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); + _ProgressBar.Dispose(); } } _Distance.Clear(); @@ -843,7 +852,7 @@ public partial class DlibDotNet _Logger?.LogInformation(string.Concat("Moved <", item.FilePath.FullName, '>')); } - private int GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(MapLogic mapLogic, Item item, bool? isFocusRelativePath, ReadOnlyCollection locationContainers, MappingFromItem mappingFromItem, List? mappingFromPhotoPrismCollection, List faces) + private int GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(MapLogic mapLogic, Item item, bool? isFocusRelativePath, MappingFromItem mappingFromItem, List? mappingFromPhotoPrismCollection, List faces) { int result; double? α; @@ -864,7 +873,6 @@ public partial class DlibDotNet bool? isFocusModel = GetIsFocusModel(item.Property); long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); ReadOnlyDictionary>? wholePercentagesToPersonContainers; - ReadOnlyCollection locationContainersFiles = new((from l in locationContainers select l.FilePath.FullName).ToArray()); foreach (Shared.Models.Face face in faces) { wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(item.Property?.Id); @@ -987,7 +995,6 @@ public partial class DlibDotNet List parseExceptions = []; string[] changesFrom = [nameof(A_Property)]; List> subFileTuples = []; - ReadOnlyCollection locationContainers = mapLogic.GetLocationContainers(item); FileHolder resizedFileHolder = _Resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber); if (item.Property is null || item.Property.Id is null || !item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any()) { @@ -1030,7 +1037,6 @@ public partial class DlibDotNet item.SetResizedFileHolder(_Resize.FileNameExtension, resizedFileHolder); MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder); ExifDirectory exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); - Map.Models.Stateless.Methods.IMapLogic.SetCreationTimeMaybeMoveToDecade(_Configuration.PropertyConfiguration, _Configuration.MoveToDecade && _Configuration.LocationContainerDistanceTolerance is null, mappingFromItem, locationContainers); if (_AppSettings.Places.Count > 0) { float latitude; @@ -1065,15 +1071,23 @@ public partial class DlibDotNet List? mappingFromPhotoPrismCollection; if (!fileNameToCollection.TryGetValue(mappingFromItem.Id, out mappingFromPhotoPrismCollection)) mappingFromPhotoPrismCollection = null; - faces = _Faces.GetFaces(outputResolution, dResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionToResize, locationContainers, mappingFromPhotoPrismCollection); - result = GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(mapLogic, item, isFocusRelativePath, locationContainers, mappingFromItem, mappingFromPhotoPrismCollection, faces); - List<(Shared.Models.Face, FileInfo?, string, bool Saved)> faceCollection = _Faces.SaveFaces(_FaceParts.FileNameExtension, dResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, mappingFromItem, faces); + bool move = _Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution); + faces = _Faces.GetFaces(outputResolution, dResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionToResize, mappingFromPhotoPrismCollection); + result = GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(mapLogic, item, isFocusRelativePath, mappingFromItem, mappingFromPhotoPrismCollection, faces); + List<(Shared.Models.Face, FileInfo?, string, bool Saved)> faceCollection = _Faces.SaveFaces(_FaceParts.FileNameExtension, dResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, mappingFromItem, exifDirectory, faces); if (_Configuration.CopyFacesAndSaveFaceLandmarkForOutputResolutions.Contains(outputResolution)) _FaceParts.CopyFacesAndSaveFaceLandmarkImage(facePartsCollectionDirectory, mappingFromItem, faceCollection); - if ((_Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch) - && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution) - && locationContainers is not null && faceCollection.All(l => !l.Saved)) - _Distance.LookForMatchFacesAndPossiblyRename(_DistanceLimits, _Faces.FileNameExtension, item.FilePath, mappingFromItem, faces, locationContainers); + if (move && faceCollection.All(l => !l.Saved)) + { + ReadOnlyCollection locationContainers = mapLogic.GetLocationContainers(item); + if (locationContainers.Count > 0) + { + Map.Models.Stateless.Methods.IMapLogic.SetCreationTime(mappingFromItem, locationContainers); + if (_Configuration.LocationContainerDistanceTolerance is null && _Configuration.MoveToDecade) + Map.Models.Stateless.Methods.IMapLogic.MoveToDecade(_Configuration.PropertyConfiguration, mappingFromItem, locationContainers); + _Distance.LookForMatchFacesAndPossiblyRename(_DistanceLimits, _Faces, item.FilePath, mappingFromItem, exifDirectory, faces, locationContainers); + } + } (bool review, int[] eyesCollection) = Shared.Models.Stateless.Methods.IFace.GetEyeCollection(_Configuration.EyeThreshold, faces); if (review || _Configuration.SaveFaceLandmarkForOutputResolutions.Contains(outputResolution)) { diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index 2f5e905..bc3688b 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -1355,37 +1355,43 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return result; } - public void LookForAbandoned(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) + public void LookForAbandoned(IDlibDotNet dlibDotNet, Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) { string[] directories; string? directoryName; List distinctFilteredIds = IContainer.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); LookForAbandoned(propertyConfiguration, distinctFilteredIds); - Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, bResultsFullGroupDirectory, distinctFilteredIds); + dlibDotNet.Tick(); + List distinctFilteredFileNameFirstSegments = IContainer.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); + Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, bResultsFullGroupDirectory, distinctFilteredFileNameFirstSegments); + dlibDotNet.Tick(); directories = Directory.GetDirectories(cResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { directoryName = Path.GetFileName(directory); if (string.IsNullOrEmpty(directoryName) || directoryName.Length != 2 && directoryName.Length != 4) continue; - Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredIds, directory, directoryName); + Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); } + dlibDotNet.Tick(); directories = Directory.GetDirectories(dResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { directoryName = Path.GetFileName(directory); if (string.IsNullOrEmpty(directoryName) || directoryName.Length != 2 && directoryName.Length != 4) continue; - Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredIds, directory, directoryName); + Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); } + dlibDotNet.Tick(); directories = Directory.GetDirectories(d2ResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { directoryName = Path.GetFileName(directory); if (string.IsNullOrEmpty(directoryName) || directoryName.Length != 2 && directoryName.Length != 4) continue; - Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredIds, directory, directoryName); + Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); } + dlibDotNet.Tick(); } } \ No newline at end of file diff --git a/Map/Models/Stateless/DecadeLogic.cs b/Map/Models/Stateless/DecadeLogic.cs index 936ef2d..5b7f99c 100644 --- a/Map/Models/Stateless/DecadeLogic.cs +++ b/Map/Models/Stateless/DecadeLogic.cs @@ -32,9 +32,23 @@ internal abstract class DecadeLogic return result; } - internal static void SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) + internal static void SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) { DateTime dateTime; + foreach (LocationContainer locationContainer in locationContainers) + { + dateTime = mappingFromItem.DateTimeOriginal is null ? mappingFromItem.MinimumDateTime : mappingFromItem.DateTimeOriginal.Value; + if (locationContainer.CreationDateOnly.Year != dateTime.Year || locationContainer.CreationDateOnly.Month != dateTime.Month || locationContainer.CreationDateOnly.Day != dateTime.Day) + { + if (!File.Exists(locationContainer.FilePath.FullName)) + continue; + File.SetCreationTime(locationContainer.FilePath.FullName, dateTime); + } + } + } + + internal static void MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) + { string halfDecade; string checkDirectory; string? yearDirectory; @@ -44,13 +58,6 @@ internal abstract class DecadeLogic string? personKeyFormattedDirectoryName; foreach (LocationContainer locationContainer in locationContainers) { - if (!File.Exists(locationContainer.FilePath.FullName)) - continue; - dateTime = mappingFromItem.DateTimeOriginal is null ? mappingFromItem.MinimumDateTime : mappingFromItem.DateTimeOriginal.Value; - if (locationContainer.CreationDateOnly.Year != dateTime.Year || locationContainer.CreationDateOnly.Month != dateTime.Month || locationContainer.CreationDateOnly.Day != dateTime.Day) - File.SetCreationTime(locationContainer.FilePath.FullName, dateTime); - if (!moveToDecade) - continue; if (string.IsNullOrEmpty(locationContainer.FilePath.DirectoryName)) continue; personNameDirectoryName = Path.GetFileName(locationContainer.FilePath.DirectoryName); @@ -68,6 +75,8 @@ internal abstract class DecadeLogic if (halfDecade == yearDirectoryName) continue; checkDirectory = Path.Combine(personKeyFormattedDirectory, halfDecade, personNameDirectoryName); + if (!File.Exists(locationContainer.FilePath.FullName)) + continue; if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); File.Move(locationContainer.FilePath.FullName, Path.Combine(checkDirectory, locationContainer.FilePath.Name)); diff --git a/Map/Models/Stateless/LookForAbandonedLogic.cs b/Map/Models/Stateless/LookForAbandonedLogic.cs index 2d16a3d..ed0921a 100644 --- a/Map/Models/Stateless/LookForAbandonedLogic.cs +++ b/Map/Models/Stateless/LookForAbandonedLogic.cs @@ -6,13 +6,12 @@ namespace View_by_Distance.Map.Models.Stateless; internal abstract class LookForAbandonedLogic { - internal static void LookForAbandoned(Property.Models.Configuration propertyConfiguration, List distinctFilteredIds, string directory, string directoryName) + internal static void LookForAbandoned(Property.Models.Configuration propertyConfiguration, List distinctFilteredFileNameFirstSegments, string directory, string directoryName) { FilePath filePath; FileHolder fileHolder; string fileNameFirstSegment; List renameCollection = []; - string[] distinctFilteredIdsValues = distinctFilteredIds.Select(l => l.ToString()).ToArray(); string[] files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories); foreach (string file in files) { @@ -21,7 +20,7 @@ internal abstract class LookForAbandonedLogic fileNameFirstSegment = fileHolder.NameWithoutExtension.Split('.')[0]; if (!filePath.IsIntelligentIdFormat && filePath.SortOrder is null) continue; - if (distinctFilteredIdsValues.Contains(fileNameFirstSegment)) + if (distinctFilteredFileNameFirstSegments.Contains(fileNameFirstSegment)) continue; renameCollection.Add(file); } @@ -36,7 +35,7 @@ internal abstract class LookForAbandonedLogic } } - internal static void LookForAbandoned(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, List distinctFilteredIds) + internal static void LookForAbandoned(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, List distinctFilteredFileNameFirstSegments) { string[] directories = Directory.GetDirectories(bResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) @@ -44,7 +43,7 @@ internal abstract class LookForAbandonedLogic string? directoryName = Path.GetFileName(directory); if (string.IsNullOrEmpty(directoryName) || (directoryName.Length != 2 && directoryName.Length != 4)) continue; - LookForAbandoned(propertyConfiguration, distinctFilteredIds, directory, directoryName); + LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); } } diff --git a/Map/Models/Stateless/Methods/IMapLogic.cs b/Map/Models/Stateless/Methods/IMapLogic.cs index a5931dd..6a2a7df 100644 --- a/Map/Models/Stateless/Methods/IMapLogic.cs +++ b/Map/Models/Stateless/Methods/IMapLogic.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using View_by_Distance.Shared.Models; namespace View_by_Distance.Map.Models.Stateless.Methods; @@ -10,24 +11,24 @@ public interface IMapLogic static ReadOnlyDictionary> GetIdToPersonKeys(ReadOnlyDictionary> personKeyToIds) => MapLogic.GetIdToPersonKeys(personKeyToIds); - ReadOnlyCollection TestStatic_GetFaces(ReadOnlyCollection items) => + ReadOnlyCollection TestStatic_GetFaces(ReadOnlyCollection items) => GetFaces(items); - static ReadOnlyCollection GetFaces(ReadOnlyCollection items) => + static ReadOnlyCollection GetFaces(ReadOnlyCollection items) => MapLogic.GetFaces(items); - Shared.Models.Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection items) => + Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection items) => GetSelectedMappingCollection(items); - static Shared.Models.Mapping[] GetSelectedMappingCollection(ReadOnlyCollection items) => + static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection items) => MapLogic.GetSelectedMappingCollection(items); - Shared.Models.Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection faces) => + Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection faces) => GetSelectedMappingCollection(faces); - static Shared.Models.Mapping[] GetSelectedMappingCollection(ReadOnlyCollection faces) => + static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection faces) => MapLogic.GetSelectedMappingCollection(faces); - ReadOnlyDictionary> TestStatic_GetIdToWholePercentagesToFace(ReadOnlyCollection distinctValidImageMappingCollection) => + ReadOnlyDictionary> TestStatic_GetIdToWholePercentagesToFace(ReadOnlyCollection distinctValidImageMappingCollection) => GetIdToWholePercentagesToFace(distinctValidImageMappingCollection); - static ReadOnlyDictionary> GetIdToWholePercentagesToFace(ReadOnlyCollection distinctValidImageMappingCollection) => + static ReadOnlyDictionary> GetIdToWholePercentagesToFace(ReadOnlyCollection distinctValidImageMappingCollection) => MapLogic.GetIdToWholePercentagesToFace(distinctValidImageMappingCollection); List<(string, long)> TestStatic_GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) => @@ -35,19 +36,24 @@ public interface IMapLogic static List<(string, long)> GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) => MapLogic.GetJLinkDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat, personCharacters, a2PeopleSingletonDirectory, a2PeopleContentDirectory); - void TestStatic_SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, Shared.Models.MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => - SetCreationTimeMaybeMoveToDecade(propertyConfiguration, moveToDecade, mappingFromItem, locationContainers); - static void SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, Shared.Models.MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => - DecadeLogic.SetCreationTimeMaybeMoveToDecade(propertyConfiguration, moveToDecade, mappingFromItem, locationContainers); + void TestStatic_SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => + SetCreationTime(mappingFromItem, locationContainers); + static void SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => + DecadeLogic.SetCreationTime(mappingFromItem, locationContainers); - bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, Shared.Models.MappingFromLocation mappingFromLocation) => + void TestStatic_MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => + MoveToDecade(propertyConfiguration, mappingFromItem, locationContainers); + static void MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => + DecadeLogic.MoveToDecade(propertyConfiguration, mappingFromItem, locationContainers); + + bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) => CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); - static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, Shared.Models.MappingFromLocation mappingFromLocation) => + static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) => MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); - string TestStatic_GetDecade(Shared.Models.MappingFromItem mappingFromItem) => + string TestStatic_GetDecade(MappingFromItem mappingFromItem) => GetDecade(mappingFromItem); - static string GetDecade(Shared.Models.MappingFromItem mappingFromItem) => + static string GetDecade(MappingFromItem mappingFromItem) => DecadeLogic.GetDecade(mappingFromItem, null); } \ No newline at end of file diff --git a/Metadata/Models/Stateless/Exif.cs b/Metadata/Models/Stateless/Exif.cs index 0ce16f3..468cbaf 100644 --- a/Metadata/Models/Stateless/Exif.cs +++ b/Metadata/Models/Stateless/Exif.cs @@ -519,8 +519,12 @@ internal abstract class Exif internal static Shared.Models.ExifDirectory GetExifDirectory(Shared.Models.FilePath filePath) { - Shared.Models.ExifDirectory? result; - System.Drawing.Size? size = Dimensions.GetDimensions(filePath.FullName); + Shared.Models.ExifDirectory result; + System.Drawing.Size? size; + try + { size = Dimensions.GetDimensions(filePath.FullName); } + catch (Exception) + { size = null; } IReadOnlyList directories = ImageMetadataReader.ReadMetadata(filePath.FullName); result = Covert(filePath, size, directories); return result; diff --git a/Shared/Models/FaceFile.cs b/Shared/Models/FaceFile.cs new file mode 100644 index 0000000..c59a536 --- /dev/null +++ b/Shared/Models/FaceFile.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; + +namespace View_by_Distance.Shared.Models; + +public record FaceFile(DateTime DateTime, + string? DMS, + Dictionary? FaceParts, + Location? Location, + string? Maker, + string? Model, + OutputResolution? OutputResolution); + +[JsonSourceGenerationOptions(WriteIndented = false)] +[JsonSerializable(typeof(FaceFile))] +public partial class FaceFileGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Container.cs b/Shared/Models/Stateless/Methods/Container.cs index 15cad09..83f44f5 100644 --- a/Shared/Models/Stateless/Methods/Container.cs +++ b/Shared/Models/Stateless/Methods/Container.cs @@ -29,7 +29,7 @@ internal abstract class Container return container.Items.Count == results.Count ? container.Items : new(results); } - internal static List GetFilePairs(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string aPropertySingletonDirectory, ReadOnlyCollection filesCollection) + internal static List GetFilePairs(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string aPropertySingletonDirectory, ReadOnlyCollection filesCollection) { int renamed; const bool useCeilingAverage = true; @@ -37,16 +37,19 @@ internal abstract class Container ReadOnlyCollection? jsonFilesCollection = null; IReadOnlyDictionary>? compareFileNamesToFiles = null; IReadOnlyDictionary> fileNamesToFiles = XDirectory.GetFilesKeyValuePairs(filesCollection); - for (int i = 0; i < int.MaxValue; i++) + for (int i = 0; i < short.MaxValue; i++) { renamed = 0; + dlibDotNet?.Tick(); jsonFilesCollection = IDirectory.GetFilesCollection(aPropertySingletonDirectory, directorySearchFilter, extension, useCeilingAverage); compareFileNamesToFiles = XDirectory.GetFilesKeyValuePairs(jsonFilesCollection); renamed += XDirectory.LookForAbandoned(jsonFilesCollection, fileNamesToFiles, extension); - filePairs = XDirectory.GetFiles(filesCollection, fileNamesToFiles, extension, compareFileNamesToFiles); + filePairs = XDirectory.GetFiles(propertyConfiguration, filesCollection, fileNamesToFiles, extension, compareFileNamesToFiles); renamed += XDirectory.MaybeMove(propertyConfiguration, filePairs, aPropertySingletonDirectory, extension); if (renamed == 0) break; + if (i > 10) + throw new NotImplementedException(); } if (filePairs is null || jsonFilesCollection is null || compareFileNamesToFiles is null) throw new NullReferenceException(nameof(filePairs)); @@ -119,19 +122,19 @@ internal abstract class Container results.Add(new(filePair.IsUnique, filePair.Collection, filePath, item)); } - private static List GetFilePairs(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection, string directorySearchFilter) + private static List GetFilePairs(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection, string directorySearchFilter) { List results = []; const string extension = ".json"; int maxDegreeOfParallelism = Environment.ProcessorCount; int filesCollectionDirectoryLength = filesCollectionDirectory.Length; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; - List filePairs = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, aPropertySingletonDirectory, filesCollection); + List filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, directorySearchFilter, extension, aPropertySingletonDirectory, filesCollection); _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(propertyConfiguration, aPropertySingletonDirectory, extension, filesCollectionDirectoryLength, filePairs[i], results)); return results; } - private static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection, string directorySearchFilter) + private static (int, Models.Container[]) GetContainers(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection, string directorySearchFilter) { List results = []; string? directory; @@ -155,7 +158,7 @@ internal abstract class Container throw new Exception(); } } - List filePairs = GetFilePairs(propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, filesCollection, directorySearchFilter); + List filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, filesCollection, directorySearchFilter); foreach (FilePair filePair in filePairs) { if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryName, out items)) @@ -176,12 +179,12 @@ internal abstract class Container return (filePairs.Count, results.ToArray()); } - internal static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection) + internal static (int, Models.Container[]) GetContainers(IDlibDotNet dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection) { int count; Models.Container[] results; const string directorySearchFilter = "*"; - (count, results) = GetContainers(propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, filesCollection, directorySearchFilter); + (count, results) = GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, filesCollection, directorySearchFilter); return (count, results); } @@ -189,11 +192,12 @@ internal abstract class Container { int count; Models.Container[] results; + IDlibDotNet? dlibDotNet = null; const bool useCeilingAverage = true; const string fileSearchFilter = "*"; const string directorySearchFilter = "*"; ReadOnlyCollection filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage); - (count, results) = GetContainers(propertyConfiguration, aPropertySingletonDirectory, propertyConfiguration.RootDirectory, filesCollection, directorySearchFilter); + (count, results) = GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, propertyConfiguration.RootDirectory, filesCollection, directorySearchFilter); return (count, results); } @@ -220,6 +224,29 @@ internal abstract class Container return results; } + internal static List GetFilteredDistinctFileNameFirstSegments(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) + { + List results = []; + ReadOnlyCollection filteredItems; + foreach (Models.Container container in readOnlyContainers) + { + if (container.Items.Count == 0) + continue; + filteredItems = GetValidImageItems(propertyConfiguration, container); + if (filteredItems.Count == 0) + continue; + foreach (Models.Item item in filteredItems) + { + if (item.Property?.Id is null || item.ResizedFileHolder is null) + continue; + if (results.Contains(item.FilePath.FileNameFirstSegment)) + continue; + results.Add(item.FilePath.FileNameFirstSegment); + } + } + return results; + } + internal static ReadOnlyCollection GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) { List results = []; diff --git a/Shared/Models/Stateless/Methods/IContainer.cs b/Shared/Models/Stateless/Methods/IContainer.cs index a7537ee..a8fa000 100644 --- a/Shared/Models/Stateless/Methods/IContainer.cs +++ b/Shared/Models/Stateless/Methods/IContainer.cs @@ -20,16 +20,21 @@ public interface IContainer static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => Container.GetContainers(propertyConfiguration, aPropertySingletonDirectory); - (int, Models.Container[]) TestStatic_GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection) => - GetContainers(propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, filesCollection); - static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection) => - Container.GetContainers(propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, filesCollection); + (int, Models.Container[]) TestStatic_GetContainers(IDlibDotNet dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection) => + GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, filesCollection); + static (int, Models.Container[]) GetContainers(IDlibDotNet dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection) => + Container.GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, filesCollection); List TestStatic_GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); static List GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => Container.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); + List TestStatic_GetFilteredDistinctFileNameFirstSegments(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => + GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); + static List GetFilteredDistinctFileNameFirstSegments(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => + Container.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); + ReadOnlyCollection TestStatic_GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems); static ReadOnlyCollection GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => diff --git a/Shared/Models/Stateless/Methods/IDlibDotNet.cs b/Shared/Models/Stateless/Methods/IDlibDotNet.cs new file mode 100644 index 0000000..ac13a76 --- /dev/null +++ b/Shared/Models/Stateless/Methods/IDlibDotNet.cs @@ -0,0 +1,8 @@ +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +public interface IDlibDotNet +{ + + void Tick(); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IFaceD.cs b/Shared/Models/Stateless/Methods/IFaceD.cs new file mode 100644 index 0000000..0a02b10 --- /dev/null +++ b/Shared/Models/Stateless/Methods/IFaceD.cs @@ -0,0 +1,9 @@ +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +public interface IFaceD +{ + + public string FileNameExtension { get; } + void ReSaveFace(ExifDirectory exifDirectory, LocationContainer locationContainer, Models.Face face); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/ILocation.cs b/Shared/Models/Stateless/Methods/ILocation.cs index a9dca6e..607e438 100644 --- a/Shared/Models/Stateless/Methods/ILocation.cs +++ b/Shared/Models/Stateless/Methods/ILocation.cs @@ -25,10 +25,10 @@ public interface ILocation static Models.Location? GetLocation(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) => Location.GetLocation(databaseFile, marker, outputResolution); - List TestStatic_GetLocations(List locationContainers, List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) => - GetLocations(locationContainers, faces, mappingFromPhotoPrismCollection, rectangleIntersectMinimum); - static List GetLocations(List locationContainers, List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) => - Location.GetLocations(locationContainers, faces, mappingFromPhotoPrismCollection, rectangleIntersectMinimum); + List TestStatic_GetLocations(List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) => + GetLocations(faces, mappingFromPhotoPrismCollection, rectangleIntersectMinimum); + static List GetLocations(List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) => + Location.GetLocations(faces, mappingFromPhotoPrismCollection, rectangleIntersectMinimum); RectangleF? TestStatic_GetPercentagesRectangle(int locationDigits, int wholePercentages) => GetPercentagesRectangle(locationDigits, wholePercentages); diff --git a/Shared/Models/Stateless/Methods/Location.cs b/Shared/Models/Stateless/Methods/Location.cs index c00c484..0e1d2fe 100644 --- a/Shared/Models/Stateless/Methods/Location.cs +++ b/Shared/Models/Stateless/Methods/Location.cs @@ -245,7 +245,7 @@ internal abstract class Location return result; } - internal static List GetLocations(List locationContainers, List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) + internal static List GetLocations(List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) { List results = []; bool any; @@ -266,12 +266,6 @@ internal abstract class Location outputResolution ??= face.OutputResolution; } int before = results.Count; - foreach (LocationContainer locationContainer in locationContainers) - { - if (locationContainer.Location is null) - continue; - results.Add(locationContainer.Location); - } foreach (MappingFromPhotoPrism mappingFromPhotoPrism in mappingFromPhotoPrismCollection) { if (outputResolution is null) @@ -289,19 +283,6 @@ internal abstract class Location location = GetLocation(mappingFromPhotoPrism.DatabaseFile, marker, prismRectangle.Value); if (location is null) break; - foreach (LocationContainer locationContainer in locationContainers) - { - if (any) - continue; - if (locationContainer.Rectangle is null) - continue; - percent = GetIntersectPercent(prismRectangle.Value, prismArea, locationContainer.Rectangle.Value); - if (percent is null || percent < rectangleIntersectMinimum) - continue; - if (!any) - any = true; - break; - } foreach (Models.Face face in faces) { if (any) diff --git a/Shared/Models/Stateless/Methods/XDirectory.cs b/Shared/Models/Stateless/Methods/XDirectory.cs index 9dac0e3..5159a02 100644 --- a/Shared/Models/Stateless/Methods/XDirectory.cs +++ b/Shared/Models/Stateless/Methods/XDirectory.cs @@ -147,12 +147,13 @@ internal abstract partial class XDirectory return result; } - internal static List GetFiles(ReadOnlyCollection filesCollection, IReadOnlyDictionary> fileNamesToFiles, string extension, IReadOnlyDictionary> compareFileNamesToFiles) + internal static List GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection, IReadOnlyDictionary> fileNamesToFiles, string extension, IReadOnlyDictionary> compareFileNamesToFiles) { List results = []; string? match; string fileName; bool uniqueFileName; + string fileExtension; List? collection; bool? isNotUniqueAndNeedsReview; foreach (string[] files in filesCollection) @@ -161,6 +162,9 @@ internal abstract partial class XDirectory { isNotUniqueAndNeedsReview = null; fileName = Path.GetFileName(file); + fileExtension = Path.GetExtension(file); + if (propertyConfiguration.IgnoreExtensions.Contains(fileExtension)) + continue; if (!fileNamesToFiles.TryGetValue(fileName, out collection)) throw new Exception(); uniqueFileName = collection.Count == 1; @@ -260,7 +264,10 @@ internal abstract partial class XDirectory continue; checkFile = file.Replace(find, replace); if (File.Exists(checkFile)) + { + File.Delete(checkFile); continue; + } File.Move(file, checkFile); } }