From 37c7b6760d01d39deefa7ba8a8343f44676a4550 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Sun, 25 Dec 2022 13:54:17 -0700 Subject: [PATCH] PhotoPrism for more locations --- Distance/Models/_E_Distance.cs | 2 +- Drag-Drop/Form.cs | 17 +- Face/Models/_D_Face.cs | 164 +-------- FaceRecognitionDotNet/FaceRecognition.cs | 4 +- Instance/DlibDotNet.cs | 122 ++++--- Instance/Models/Binder/Configuration.cs | 4 - Instance/Models/Configuration.cs | 3 - Instance/appsettings.json | 1 - Map/Models/DatabaseFileRoot.cs | 8 - Map/Models/MapLogic.cs | 343 +++++++----------- Map/Models/Stateless/MapLogic.cs | 2 +- Map/Models/Stateless/Methods/IMapLogic.cs | 8 +- Metadata/Models/B_Metadata.cs | 2 +- PrepareForOld/PrepareForOld.cs | 18 +- Property-Compare/Models/PropertyCompare.cs | 2 +- .../Models/PropertyCompareLogic.cs | 18 +- Property/Models/A_Property.cs | 2 +- Property/Models/Binder/Configuration.cs | 2 +- Property/Models/Stateless/Container.cs | 4 + Resize/Models/_C_Resize.cs | 2 +- {Map => Shared}/Models/DatabaseFile.cs | 2 +- Shared/Models/Mapping.cs | 23 +- Shared/Models/Marker.cs | 4 +- Shared/Models/MarkerWith.cs | 36 -- Shared/Models/Properties/IMapping.cs | 9 + Shared/Models/Stateless/Methods/ILocation.cs | 15 + Shared/Models/Stateless/Methods/IPath.cs | 1 + Shared/Models/Stateless/Methods/Location.cs | 77 ++++ .../Models/Binder/Configuration.cs | 4 - .../Models/Configuration.cs | 3 - .../UnitTestFace.cs | 2 +- 31 files changed, 395 insertions(+), 509 deletions(-) delete mode 100644 Map/Models/DatabaseFileRoot.cs rename {Map => Shared}/Models/DatabaseFile.cs (98%) delete mode 100644 Shared/Models/MarkerWith.cs diff --git a/Distance/Models/_E_Distance.cs b/Distance/Models/_E_Distance.cs index 32eb9d7..7ba0bec 100644 --- a/Distance/Models/_E_Distance.cs +++ b/Distance/Models/_E_Distance.cs @@ -257,7 +257,7 @@ public partial class E_Distance { 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(sourceRectangle, checkRectangle); + 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); diff --git a/Drag-Drop/Form.cs b/Drag-Drop/Form.cs index 28e7405..bf3b6d1 100644 --- a/Drag-Drop/Form.cs +++ b/Drag-Drop/Form.cs @@ -179,8 +179,11 @@ public partial class Form : System.Windows.Forms.Form _ProgressBar.Step = 1; bool isIgnoreExtension; _ProgressBar.Value = 0; + string checkFileExtension; bool skipOneAllAreNumbers; DateTime? minimumDateTime; + const string jpg = ".jpg"; + const string jpeg = ".jpeg"; _ProgressBar.Visible = true; bool isValidImageFormatExtension; string? extraLargeBitmapThumbnail; @@ -201,7 +204,15 @@ public partial class Form : System.Windows.Forms.Form { skipOneAllAreNumbers = fileHolder.NameWithoutExtension[1..].All(l => char.IsNumber(l)); if ((skipOneAllAreNumbers && fileHolder.NameWithoutExtension[0] == '-') || (skipOneAllAreNumbers && char.IsNumber(fileHolder.NameWithoutExtension[0]))) + { + if (fileHolder.ExtensionLowered == jpeg) + { + if (File.Exists($"{fileHolder.FullName}.id")) + File.Move($"{fileHolder.FullName}.id", Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension}{jpg}.id")); + File.Move(fileHolder.FullName, Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension}{jpg}")); + } continue; + } } if (!isIgnoreExtension && isValidImageFormatExtension) extraLargeBitmapThumbnail = null; @@ -224,7 +235,8 @@ public partial class Form : System.Windows.Forms.Form dateTime = IProperty.GetDateTimeFromName(fileHolder); if (dateTime is not null && minimumDateTime is not null && new TimeSpan(Math.Abs(minimumDateTime.Value.Ticks - dateTime.Value.Ticks)).TotalMinutes > 2) { - checkFile = Path.Combine(fileHolder.DirectoryName, $"{dateTime.Value:yyyy-MM-dd}.{dateTime.Value.Ticks}{fileHolder.ExtensionLowered}"); + checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered; + checkFile = Path.Combine(fileHolder.DirectoryName, $"{dateTime.Value:yyyy-MM-dd}.{dateTime.Value.Ticks}{checkFileExtension}"); if (checkFile == fileHolder.FullName || File.Exists(checkFile)) continue; File.Move(fileHolder.FullName, checkFile); @@ -238,7 +250,8 @@ public partial class Form : System.Windows.Forms.Form if (fileHolder.DirectoryName is null) continue; } - checkFile = Path.Combine(fileHolder.DirectoryName, $"{id.Value}{fileHolder.ExtensionLowered}"); + checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered; + checkFile = Path.Combine(fileHolder.DirectoryName, $"{id.Value}{checkFileExtension}"); if (checkFile == fileHolder.FullName || File.Exists(checkFile)) continue; File.Move(fileHolder.FullName, checkFile); diff --git a/Face/Models/_D_Face.cs b/Face/Models/_D_Face.cs index 6ae740f..75a01f7 100644 --- a/Face/Models/_D_Face.cs +++ b/Face/Models/_D_Face.cs @@ -43,7 +43,6 @@ public class D_Face private readonly int _FaceDistanceHiddenImageFactor; private readonly EncoderParameters _EncoderParameters; private readonly ImageCodecInfo _HiddenImageCodecInfo; - private readonly bool _RetryImagesWithoutAFaceLocation; private readonly bool _ForceFaceLastWriteTimeToCreationTime; private readonly EncoderParameters _HiddenEncoderParameters; private readonly JsonSerializerOptions _WriteIndentedAndWhenWritingNull; @@ -63,7 +62,6 @@ public class D_Face string modelDirectory, string modelName, bool overrideForFaceImages, - bool retryImagesWithoutAFaceLocation, string predictorModelName, bool propertiesChangedForFaces) { @@ -81,7 +79,6 @@ public class D_Face _CheckDFaceAndUpWriteDates = checkDFaceAndUpWriteDates; _PropertiesChangedForFaces = propertiesChangedForFaces; _FaceDistanceHiddenImageFactor = faceDistanceHiddenImageFactor; - _RetryImagesWithoutAFaceLocation = retryImagesWithoutAFaceLocation; _ForceFaceLastWriteTimeToCreationTime = forceFaceLastWriteTimeToCreationTime; (Model model, PredictorModel predictorModel, ModelParameter modelParameter) = GetModel(modelDirectory, modelName, predictorModelName); _Model = model; @@ -215,7 +212,7 @@ public class D_Face } } - private List GetFaces(Shared.Models.Property property, MappingFromItem mappingFromItem, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation) + private List GetFaces(Shared.Models.Property property, MappingFromItem mappingFromItem, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, List locations) { if (_Log is null) throw new NullReferenceException(nameof(_Log)); @@ -225,25 +222,18 @@ public class D_Face throw new NullReferenceException(nameof(_Configuration.NumberOfTimesToUpsample)); List results = new(); FaceRecognitionDotNet.Image? unknownImage; - if (!mappingFromItem.ResizedFileHolder.Exists) - unknownImage = null; - else + try + { unknownImage = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName); } + catch (Exception) { - try - { unknownImage = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName); } - catch (Exception) - { - unknownImage = null; - _Log.Info(string.Concat(new StackFrame().GetMethod()?.Name, " <", mappingFromItem.ResizedFileHolder.FullName, ">")); - } + unknownImage = null; + _Log.Info(string.Concat(new StackFrame().GetMethod()?.Name, " <", mappingFromItem.ResizedFileHolder.FullName, ">")); } - if (unknownImage is null) - results.Add(new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location: null)); - else + if (unknownImage is not null) { List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary? FaceParts)> collection; FaceRecognition faceRecognition = new(_Configuration.NumberOfJitters.Value, _Configuration.NumberOfTimesToUpsample.Value, _Model, _ModelParameter, _PredictorModel); - collection = faceRecognition.GetCollection(unknownImage, includeFaceEncoding: true, includeFaceParts: true); + collection = faceRecognition.GetCollection(unknownImage, locations, includeFaceEncoding: true, includeFaceParts: true); if (!collection.Any()) results.Add(new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location: null)); else @@ -268,8 +258,6 @@ public class D_Face unknownImage.Dispose(); faceRecognition.Dispose(); } - if (!results.Any()) - throw new Exception(); return results; } @@ -303,12 +291,13 @@ 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) + public List GetFaces(string dResultsFullGroupDirectory, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, List? mappingFromPhotoPrismCollection) { List? results; if (string.IsNullOrEmpty(dResultsFullGroupDirectory)) throw new NullReferenceException(nameof(dResultsFullGroupDirectory)); string json; + List locations = new(); 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(); string dCollectionFile = Path.Combine(dResultsFullGroupDirectory, "[]", _Configuration.ResultAllInOne, $"{mappingFromItem.Id}{mappingFromItem.ImageFileHolder.ExtensionLowered}.json"); @@ -345,18 +334,21 @@ public class D_Face parseExceptions.Add(nameof(D_Face)); } } - if (results is null || (_RetryImagesWithoutAFaceLocation && results.Count == 1 && results[0].Location is null)) + if (mappingFromPhotoPrismCollection is not null && results is not null) + locations.AddRange(Shared.Models.Stateless.Methods.ILocation.GetLocations(mappingFromPhotoPrismCollection, results)); + if (results is null || locations.Any()) { - bool wasNull = results is null; - results = GetFaces(property, mappingFromItem, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation); - if (wasNull || (!wasNull && results.Any(l => l.Location is not null))) + results = GetFaces(property, mappingFromItem, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, locations); + if (!results.Any()) + File.Move(mappingFromItem.ResizedFileHolder.FullName, $"{mappingFromItem.ResizedFileHolder.FullName}.err"); + else { json = JsonSerializer.Serialize(results, _WriteIndentedAndWhenWritingNull); bool updateDateWhenMatches = dateTimes.Any() && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime; DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max(); if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime)) { - if (!_ForceFaceLastWriteTimeToCreationTime && (!fileInfo.Exists || fileInfo.LastWriteTime == fileInfo.CreationTime)) + if (!_ForceFaceLastWriteTimeToCreationTime) subFileTuples.Add(new Tuple(nameof(D_Face), DateTime.Now)); else { @@ -370,126 +362,6 @@ public class D_Face return results; } - private void WriteAllText(Dictionary>? results, List dateTimes, FileInfo fileInfo) - { - string json = JsonSerializer.Serialize(results, _WriteIndentedAndWhenWritingNull); - bool updateDateWhenMatches = dateTimes.Any() && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime; - DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max(); - if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime)) - { - if (!_ForceFaceLastWriteTimeToCreationTime && (!fileInfo.Exists || fileInfo.LastWriteTime == fileInfo.CreationTime)) - ; // subFileTuples.Add(new Tuple(nameof(D_Face), DateTime.Now)); - else - { - File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime); - fileInfo.Refresh(); - // subFileTuples.Add(new Tuple(nameof(D_Face), fileInfo.CreationTime)); - } - } - } - - public Dictionary> GetRelativeLocations(string outputResolution, string dResultsDateGroupDirectory, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, string original, Dictionary imageResizeKeyValuePairs, List faces) - { - Dictionary>? results; - if (string.IsNullOrEmpty(dResultsDateGroupDirectory)) - throw new NullReferenceException(nameof(dResultsDateGroupDirectory)); - if (!imageResizeKeyValuePairs.ContainsKey(original)) - throw new Exception(); - if (!imageResizeKeyValuePairs.ContainsKey(outputResolution)) - throw new Exception(); - string json; - decimal? h, l, t, w; - RelativeLocation relativeLocation; - List relativeLocations = new(); - int[] resize = imageResizeKeyValuePairs[outputResolution]; - string key = $"{resize[0].ToString().PadLeft(6, '0')}.{resize[1].ToString().PadLeft(6, '0')}"; - string[] changesFrom = new string[] { nameof(A_Property), nameof(B_Metadata), nameof(C_Resize), nameof(D_Face) }; - List dateTimes = (from d in subFileTuples where changesFrom.Contains(d.Item1) select d.Item2).ToList(); - string dCollectionDirectory = Path.Combine(dResultsDateGroupDirectory, "[]", _Configuration.ResultAllInOne); - string dCollectionFile = Path.Combine(dCollectionDirectory, $"{mappingFromItem.Id}{mappingFromItem.ImageFileHolder.ExtensionLowered}.json"); - if (!Directory.Exists(dCollectionDirectory)) - _ = Directory.CreateDirectory(dCollectionDirectory); - FileInfo fileInfo = new(dCollectionFile); - if (_ForceFaceLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete"))) - { - File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName); - fileInfo.Refresh(); - } - if (_ForceFaceLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime) - { - File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime); - fileInfo.Refresh(); - } - if (_PropertiesChangedForFaces) - results = null; - else if (!fileInfo.Exists) - results = null; - else if (_CheckDFaceAndUpWriteDates && dateTimes.Any() && dateTimes.Max() > fileInfo.LastWriteTime) - results = null; - else - { - json = Shared.Models.Stateless.Methods.IFace.GetJson(fileInfo.FullName); - try - { - results = JsonSerializer.Deserialize>>(json); - if (results is null) - throw new NullReferenceException(nameof(results)); - bool added = false; - relativeLocations.Clear(); - foreach (KeyValuePair> keyValuePair in results) - relativeLocations.AddRange(keyValuePair.Value); - if (!results.ContainsKey(key)) - results.Add(key, new()); - foreach (Shared.Models.Face face in faces) - { - if (face.Location is null || face.OutputResolution is null) - continue; - (h, l, t, w) = Shared.Models.Stateless.Methods.ILocation.GetHeightLeftTopWidth(face.Location, face.OutputResolution); - if (h is null || l is null || t is null || w is null) - continue; - relativeLocation = new(face.Location.Confidence, h.Value.ToString(), l.Value.ToString(), t.Value.ToString(), w.Value.ToString()); - if (relativeLocations.Any(r => r.Match(h, l, t, w))) - continue; - if (!added) - added = true; - results[key].Add(relativeLocation); - } - if (added) - { - List<(string Key, List Value)> sort = new(); - foreach (KeyValuePair> keyValuePair in results) - sort.Add(new(keyValuePair.Key, keyValuePair.Value)); - results.Clear(); - foreach ((string k, List v) in sort.OrderBy(l => l.Key)) - results.Add(k, v.OrderByDescending(l => l.Confidence).ToList()); - WriteAllText(results, dateTimes, fileInfo); - } - // subFileTuples.Add(new Tuple(nameof(D_Face), fileInfo.LastWriteTime)); - } - catch (Exception) - { - results = null; - parseExceptions.Add(nameof(D_Face)); - } - } - if (results is null) - { - results = new() { { key, new() } }; - foreach (Shared.Models.Face face in faces) - { - if (face.Location is null || face.OutputResolution is null) - continue; - (h, l, t, w) = Shared.Models.Stateless.Methods.ILocation.GetHeightLeftTopWidth(face.Location, face.OutputResolution); - if (h is null || l is null || t is null || w is null) - continue; - relativeLocation = new(face.Location.Confidence, h.Value.ToString(), l.Value.ToString(), t.Value.ToString(), w.Value.ToString()); - results[key].Add(relativeLocation); - } - WriteAllText(results, dateTimes, fileInfo); - } - return results; - } - public bool SaveFaces(string dResultsFullGroupDirectory, List> subFileTuples, List parseExceptions, MappingFromItem mappingFromItem, string facesDirectory, List faces) { FileInfo fileInfo; diff --git a/FaceRecognitionDotNet/FaceRecognition.cs b/FaceRecognitionDotNet/FaceRecognition.cs index 419ac16..1038539 100644 --- a/FaceRecognitionDotNet/FaceRecognition.cs +++ b/FaceRecognitionDotNet/FaceRecognition.cs @@ -198,7 +198,7 @@ public class FaceRecognition : DisposableObject return results; } - public List<(Location, FaceEncoding?, Dictionary?)> GetCollection(Image image, bool includeFaceEncoding, bool includeFaceParts) + public List<(Location, FaceEncoding?, Dictionary?)> GetCollection(Image image, List locations, bool includeFaceEncoding, bool includeFaceParts) { List<(Location, FaceEncoding?, Dictionary?)> results = new(); if (image is null) @@ -207,7 +207,7 @@ public class FaceRecognition : DisposableObject ThrowIfDisposed(); if (_PredictorModel == PredictorModel.Custom) throw new NotSupportedException("FaceRecognition.PredictorModel.Custom is not supported."); - List locations = GetLocations(image); + locations.AddRange(GetLocations(image)); List fullObjectDetections = GetFullObjectDetections(image, locations); if (fullObjectDetections.Count != locations.Count) throw new Exception(); diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index 327d417..5880006 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -33,9 +33,11 @@ public partial class DlibDotNet private readonly List _Exceptions; private readonly IsEnvironment _IsEnvironment; private readonly bool _PropertyRootExistedBefore; + private readonly PersonContainer[] _PersonContainers; private readonly Models.Configuration _Configuration; private readonly bool _ArgZeroIsConfigurationRootDirectory; private readonly Map.Models.Configuration _MapConfiguration; + private readonly Dictionary> _FileNameToCollection; public DlibDotNet( List args, @@ -46,13 +48,13 @@ public partial class DlibDotNet bool isSilent, IConsole console) { - _Console = console; string message; + _Console = console; _AppSettings = appSettings; + _FileNameToCollection = new(); _IsEnvironment = isEnvironment; long ticks = DateTime.Now.Ticks; _Exceptions = new List(); - PersonContainer[] personContainers; _Log = Serilog.Log.ForContext(); Property.Models.Configuration propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot); Models.Configuration configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration); @@ -94,7 +96,6 @@ public partial class DlibDotNet configuration.ModelDirectory, configuration.ModelName, configuration.OverrideForFaceImages, - configuration.RetryImagesWithoutAFaceLocation, configuration.PredictorModelName, configuration.PropertiesChangedForFaces); } @@ -106,7 +107,7 @@ public partial class DlibDotNet _MapConfiguration = Get(configuration, _Faces.FileNameExtension, _Faces.HiddenFileNameExtension, _FaceParts.FileNameExtension); _Distance = new(configuration.DistanceMoveUnableToMatch, configuration.DistanceRenameToMatch, _Configuration.FaceConfidencePercent, configuration.RangeDistanceTolerance, configuration.RangeFaceConfidence); if (_PropertyRootExistedBefore || !_ArgZeroIsConfigurationRootDirectory) - personContainers = Array.Empty(); + _PersonContainers = Array.Empty(); else { int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); @@ -120,7 +121,7 @@ public partial class DlibDotNet if (rootResultsDirectory is null) throw new Exception(); Storage storage = new(rootDirectory, rootResultsDirectory, peopleRootDirectory); - personContainers = Shared.Models.Stateless.Methods.IPersonContainer.GetPersonContainers(storage, configuration.PersonBirthdayFormat, _Faces.FileNameExtension); + _PersonContainers = Shared.Models.Stateless.Methods.IPersonContainer.GetPersonContainers(storage, configuration.PersonBirthdayFormat, _Faces.FileNameExtension); } if (!isSilent && configuration.TestDistanceResults) { @@ -142,7 +143,7 @@ public partial class DlibDotNet filenameExtension); } if (!configuration.SkipSearch) - Search(ticks, argZero, propertyRoot, personContainers); + Search(ticks, argZero, propertyRoot); if (!_PropertyRootExistedBefore && !_IsEnvironment.Development && _Exceptions.Count == 0 && _ArgZeroIsConfigurationRootDirectory) { List directoryCollections = _Rename.GetDirectoryRenameCollections( @@ -205,7 +206,7 @@ public partial class DlibDotNet private void Verify(Models.Configuration configuration) { if (!configuration.OutputResolutions.Any() || string.IsNullOrEmpty(configuration.OutputResolutions[0]) || !configuration.ValidResolutions.Contains(configuration.OutputResolutions[0])) - throw new NullReferenceException($"{nameof(configuration.OutputResolutions)} must be a valid outputResolution!"); + throw new NullReferenceException($"{nameof(configuration.OutputResolutions)} must be _FileNameToCollection valid outputResolution!"); if ((from l in configuration.OutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) throw new Exception($"One or more {nameof(configuration.OutputResolutions)} are not in the ValidResolutions list!"); if ((from l in configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) @@ -249,13 +250,13 @@ public partial class DlibDotNet string[] segments = sourceDirectoryNames[0].Split(' '); century = segments[^1].Length == 4 ? segments[^1][..2] : null; if (segments.Length < 2 || century is null || (century != "18" && century != "19" && century != "20")) - throw new Exception("root subdirectory must have a year at the end or directory name needs to be added to the exclude list!"); + throw new Exception("root subdirectory must have _FileNameToCollection year at the end or directory name needs to be added to the exclude list!"); } } } string[] resizeMatch = (from l in sourceDirectoryNames where configuration.ValidResolutions.Contains(l) select l).ToArray(); if (resizeMatch.Any()) - throw new Exception("Input directory should be the source and not a resized directory!"); + throw new Exception("Input directory should be the source and not _FileNameToCollection resized directory!"); if (configuration.LocationDigits != Shared.Models.Stateless.ILocation.Digits) throw new Exception("Configuration has to match interface!"); if (configuration.LocationFactor != Shared.Models.Stateless.ILocation.Factor) @@ -294,7 +295,6 @@ public partial class DlibDotNet Dictionary imageResizeKeyValuePairs; List> subFileTuples = new(); List> metadataCollection; - // Dictionary> relativeLocations; if (item.Property is not null && item.Property.Id is not null && !item.Any()) { property = item.Property; @@ -354,17 +354,21 @@ public partial class DlibDotNet string path = Path.Combine(resizedFileHolder.DirectoryName, resizedFileHolder.NameWithoutExtension); File.WriteAllBytes(path, bytes); } - if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution)) + if (!mappingFromItem.ResizedFileHolder.Exists && !File.Exists(mappingFromItem.ResizedFileHolder.FullName)) + faces = new(); + else if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution)) faces = new(); else { - List<(string, int, IReadOnlyList?)>? collection; int[] outputResolutionCollection = imageResizeKeyValuePairs[outputResolution]; int outputResolutionWidth = outputResolutionCollection[0]; int outputResolutionHeight = outputResolutionCollection[1]; int outputResolutionOrientation = outputResolutionCollection[2]; - faces = _Faces.GetFaces(dResultsFullGroupDirectory, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation); - // relativeLocations = _Faces.GetRelativeLocations(outputResolution, dResultsDateGroupDirectory, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, original, imageResizeKeyValuePairs, faces); + List? mappingFromPhotoPrismCollection; + List<(string, int, IReadOnlyList?)>? collection; + if (!_FileNameToCollection.TryGetValue(mappingFromItem.RelativePath[1..], out mappingFromPhotoPrismCollection)) + mappingFromPhotoPrismCollection = null; + faces = _Faces.GetFaces(dResultsFullGroupDirectory, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, mappingFromPhotoPrismCollection); if (_AppSettings.MaxDegreeOfParallelism < 2) ticks = LogDelta(ticks, nameof(D_Face.GetFaces)); bool anyFacesSaved = _Faces.SaveFaces(dResultsFullGroupDirectory, subFileTuples, parseExceptions, mappingFromItem, facesDirectory, faces); @@ -439,25 +443,24 @@ public partial class DlibDotNet { try { - FullParallelForWork( - propertyLogic, - idToMappedFaceFilesWithCollection, - outputResolution, - bResultsFullGroupDirectory, - cResultsFullGroupDirectory, - dResultsDateGroupDirectory, - dResultsFullGroupDirectory, - eDistanceContentDirectory, - sourceDirectoryChanges, - propertyFileHolderCollection, - propertyCollection, - metadataCollection, - resizeKeyValuePairs, - imageFaceCollections, - container, - index: i, - filteredItems[i], - containerDateTimes); + FullParallelForWork(propertyLogic, + idToMappedFaceFilesWithCollection, + outputResolution, + bResultsFullGroupDirectory, + cResultsFullGroupDirectory, + dResultsDateGroupDirectory, + dResultsFullGroupDirectory, + eDistanceContentDirectory, + sourceDirectoryChanges, + propertyFileHolderCollection, + propertyCollection, + metadataCollection, + resizeKeyValuePairs, + imageFaceCollections, + container, + index: i, + filteredItems[i], + containerDateTimes); if (i == 0 || sourceDirectoryChanges.Any()) progressBar.Tick(); } @@ -627,7 +630,7 @@ public partial class DlibDotNet _Metadata.SetAngleBracketCollection(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, container.SourceDirectory); } - private void FullDoWork(string argZero, string propertyRoot, long ticks, PersonContainer[] personContainers, A_Property propertyLogic, int t, Container[] containers, string? a2PeopleContentDirectory, string eDistanceContentDirectory) + private void FullDoWork(string argZero, string propertyRoot, long ticks, A_Property propertyLogic, int t, Container[] containers, string? a2PeopleContentDirectory, string eDistanceContentDirectory) { if (_Log is null) throw new NullReferenceException(nameof(_Log)); @@ -651,7 +654,7 @@ public partial class DlibDotNet int maxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism; List nullablePropertyCollection = new(); List>> metadataCollection = new(); - Dictionary?)>> idToMappedFaceFilesWithCollection = GetCollection(ticks, personContainers, a2PeopleContentDirectory, eDistanceContentDirectory); + Dictionary?)>> idToMappedFaceFilesWithCollection = GetCollection(ticks, a2PeopleContentDirectory, eDistanceContentDirectory); string dResultsDateGroupDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(D_Face)); foreach (string outputResolution in _Configuration.OutputResolutions) { @@ -739,7 +742,7 @@ public partial class DlibDotNet } } - private void SetMapping(string argZero, Container[] containers) + private void SetMapping(Dictionary> fileNameToCollection, string argZero, Container[] containers) { Mapping mapping; int faceAreaPermille; @@ -751,6 +754,7 @@ public partial class DlibDotNet string deterministicHashCodeKey; MappingFromItem mappingFromItem; MappingFromLocation mappingFromLocation; + List? mappingFromPhotoPrismCollection; foreach (Container container in containers) { if (!container.Items.Any()) @@ -776,7 +780,9 @@ public partial class DlibDotNet normalizedRectangle = Shared.Models.Stateless.Methods.ILocation.GetNormalizedRectangle(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item.Property.Id.Value, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); mappingFromLocation = new(faceAreaPermille, confidencePercent, deterministicHashCodeKey, normalizedRectangle); - mapping = new(mappingFromItem, mappingFromLocation); + if (!fileNameToCollection.TryGetValue(mappingFromItem.RelativePath[1..], out mappingFromPhotoPrismCollection)) + mappingFromPhotoPrismCollection = null; + mapping = new(mappingFromItem, mappingFromLocation, mappingFromPhotoPrismCollection); face.SetMapping(mapping); } } @@ -847,7 +853,7 @@ public partial class DlibDotNet return items; } - private void MapLogic(string argZero, long ticks, PersonContainer[] personContainers, Container[] containers, string a2PeopleSingletonDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, MapLogicSupport mapLogicSupport, MapLogic mapLogic, string outputResolution) + private void MapLogic(string argZero, long ticks, Container[] containers, string a2PeopleSingletonDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, MapLogicSupport mapLogicSupport, MapLogic mapLogic, string outputResolution) { int? useFiltersCounter = null; SortingContainer[] sortingContainers; @@ -857,17 +863,12 @@ public partial class DlibDotNet Mapping[] mappingCollection = MapLogicSupport.GetSelectedMappingCollection(distinctFilteredFaces); string dFacesCollectionDirectory = Path.Combine(dResultsFullGroupDirectory, "[]", _Configuration.PropertyConfiguration.ResultAllInOne); (Dictionary personKeyToCount, int totalNotMapped) = mapLogic.AddToMapping(mappingCollection); - if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution) || (!string.IsNullOrEmpty(_Configuration.PhotoPrismDirectory) && Directory.Exists(_Configuration.PhotoPrismDirectory))) + if (!string.IsNullOrEmpty(_Configuration.PhotoPrismDirectory) && Directory.Exists(_Configuration.PhotoPrismDirectory)) + mapLogic.WriteMatches(ticks, distinctFilteredFaces); + if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) { List filteredItems = GetItems(argZero, containers); - if (!string.IsNullOrEmpty(_Configuration.PhotoPrismDirectory)) - { - mapLogic.SaveMarkers(); - mapLogic.FindMatch(filteredItems); - mapLogic.LoadMatches(); - } - if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) - mapLogic.SaveShortcutsForOutputResolutions(filteredItems, mappingCollection, personKeyToCount); + mapLogic.SaveShortcutsForOutputResolutions(filteredItems, mappingCollection, personKeyToCount); } Dictionary> idToNormalizedRectangleToMapping = MapLogicSupport.GetIdToNormalizedRectangleToFace(mappingCollection); mapLogic.CopyManualFiles(dFacesContentDirectory, idToNormalizedRectangleToMapping); @@ -876,7 +877,7 @@ public partial class DlibDotNet if (_Configuration.SaveMappedForOutputResolutions.Contains(outputResolution)) mapLogic.SaveMapped(dFacesContentDirectory, d2FacePartsContentDirectory, mappingCollection, idToNormalizedRectangleToMapping, personKeyToCount, totalNotMapped); if (_Configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions.Contains(outputResolution)) - mapLogic.SaveFilteredOriginalImagesFromJLinks(_Configuration.JLinks, a2PeopleSingletonDirectory, personContainers, mappingCollection, personKeyToCount, totalNotMapped); + mapLogic.SaveFilteredOriginalImagesFromJLinks(_Configuration.JLinks, _PersonContainers, a2PeopleSingletonDirectory, mappingCollection, personKeyToCount, totalNotMapped); if (_Configuration.SaveFaceDistancesForOutputResolutions.Contains(outputResolution)) { mapLogic.UpdatedPersonKeyToRanges(_MapConfiguration, ticks, mappingCollection); @@ -952,7 +953,7 @@ public partial class DlibDotNet bool isValidImageFormatExtension; for (int y = 0; y < int.MaxValue; y++) { - _Log.Information("Enter a url for a image"); + _Log.Information("Enter _FileNameToCollection url for _FileNameToCollection image"); line = _Console.ReadLine(); if (string.IsNullOrEmpty(line)) break; @@ -1011,12 +1012,12 @@ public partial class DlibDotNet collection.Add(new(value.MappedFaceFile, normalizedRectangle.Value, directories)); } - private Dictionary?)>> GetCollection(long ticks, PersonContainer[] personContainers, string? a2PeopleContentDirectory, string? eDistanceContentDirectory) + private Dictionary?)>> GetCollection(long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory) { Dictionary?)>> results = new(); int? id; int? normalizedRectangle; - List<(string, string[], string)> collection = Map.Models.Stateless.Methods.IMapLogic.DeleteEmptyDirectoriesAndGetMappedFaceFiles(_MapConfiguration, ticks, a2PeopleContentDirectory, eDistanceContentDirectory, personContainers); + List<(string, string[], string)> collection = Map.Models.Stateless.Methods.IMapLogic.DeleteEmptyDirectoriesAndGetMappedFaceFiles(_MapConfiguration, _PersonContainers, ticks, a2PeopleContentDirectory, eDistanceContentDirectory); foreach ((string personKeyFormatted, string[] personDisplayDirectoryNames, string mappedFaceFile) in collection) { (id, normalizedRectangle) = Shared.Models.Stateless.Methods.IMapping.GetConverted(_Faces.FileNameExtension, mappedFaceFile); @@ -1043,7 +1044,7 @@ public partial class DlibDotNet return results; } - private void Search(long ticks, string argZero, string propertyRoot, PersonContainer[] personContainers) + private void Search(long ticks, string argZero, string propertyRoot) { int j; int f; @@ -1056,6 +1057,13 @@ public partial class DlibDotNet string dResultsFullGroupDirectory; string? eDistanceContentDirectory; string d2ResultsFullGroupDirectory; + if (!string.IsNullOrEmpty(_Configuration.PhotoPrismDirectory) && Directory.Exists(_Configuration.PhotoPrismDirectory)) + { + Map.Models.MapLogic.SaveMarkers(_Configuration.PhotoPrismDirectory); + Dictionary> fileNameToCollection = Map.Models.MapLogic.GetFileNameToCollection(_Configuration.PhotoPrismDirectory); + foreach (KeyValuePair> keyValuePair in fileNameToCollection) + _FileNameToCollection.Add(keyValuePair.Key, keyValuePair.Value); + } string a2PeopleSingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), "{}"); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string message = $") Building Container(s) - {totalSeconds} total second(s)"; @@ -1092,10 +1100,10 @@ public partial class DlibDotNet } containers = Shared.Models.Stateless.Methods.IContainer.SortContainers(_Configuration.PropertyConfiguration, _Configuration.IgnoreRelativePaths, _ArgZeroIsConfigurationRootDirectory, argZero, containers); MapLogicSupport mapLogicSupport = new(_Configuration.FaceConfidencePercent, _Configuration.FaceDistancePermyriad, _Configuration.RangeDaysDeltaTolerance, _Configuration.RangeFaceAreaPermilleTolerance, _Configuration.SortingMaximumPerFaceShouldBeHigh); - MapLogic? mapLogic = _Configuration.DistanceMoveUnableToMatch ? null : new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory, mapLogicSupport); - FullDoWork(argZero, propertyRoot, ticks, personContainers, propertyLogic, t, containers, a2PeopleContentDirectory, eDistanceContentDirectory); + MapLogic? mapLogic = _Configuration.DistanceMoveUnableToMatch ? null : new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, ticks, _PersonContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory, mapLogicSupport); + FullDoWork(argZero, propertyRoot, ticks, propertyLogic, t, containers, a2PeopleContentDirectory, eDistanceContentDirectory); _Distance.Clear(); - mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory, mapLogicSupport); + mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, ticks, _PersonContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory, mapLogicSupport); foreach (string outputResolution in _Configuration.OutputResolutions) { if (_PropertyRootExistedBefore || container is not null) @@ -1112,8 +1120,8 @@ public partial class DlibDotNet { if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Any()) break; - SetMapping(argZero, containers); - MapLogic(argZero, ticks, personContainers, containers, a2PeopleSingletonDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, mapLogicSupport, mapLogic, outputResolution); + SetMapping(_FileNameToCollection, argZero, containers); + MapLogic(argZero, ticks, containers, a2PeopleSingletonDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, mapLogicSupport, mapLogic, outputResolution); if (_IsEnvironment.Development) continue; List mappingFromItemCollection = GetMappingFromItemCollection(containers); diff --git a/Instance/Models/Binder/Configuration.cs b/Instance/Models/Binder/Configuration.cs index fd8a2af..e7557e6 100644 --- a/Instance/Models/Binder/Configuration.cs +++ b/Instance/Models/Binder/Configuration.cs @@ -62,7 +62,6 @@ public class Configuration [Display(Name = "Face Distance Tolerance"), Required] public double[] RangeDistanceTolerance { get; set; } [Display(Name = "Face Area Permille Tolerance"), Required] public int[] RangeFaceAreaPermilleTolerance { get; set; } [Display(Name = "Location Minimum Confidence"), Required] public double[] RangeFaceConfidence { get; set; } - [Display(Name = "Retry Images Without a Face"), Required] public bool? RetryImagesWithoutAFaceLocation { get; set; } [Display(Name = "Reverse"), Required] public bool? Reverse { get; set; } [Display(Name = "Save Face Distances"), Required] public string[] SaveFaceDistancesForOutputResolutions { get; set; } [Display(Name = "Save Face Landmark For Output Resolutions"), Required] public string[] SaveFaceLandmarkForOutputResolutions { get; set; } @@ -180,8 +179,6 @@ public class Configuration throw new NullReferenceException(nameof(configuration.RangeFaceConfidence)); if (configuration.RangeDistanceTolerance is null || configuration.RangeDistanceTolerance.Length != 3) throw new NullReferenceException(nameof(configuration.RangeDistanceTolerance)); - if (configuration.RetryImagesWithoutAFaceLocation is null) - throw new NullReferenceException(nameof(configuration.RetryImagesWithoutAFaceLocation)); if (configuration.Reverse is null) throw new NullReferenceException(nameof(configuration.Reverse)); configuration.SaveFaceDistancesForOutputResolutions ??= Array.Empty(); @@ -258,7 +255,6 @@ public class Configuration configuration.RangeFaceAreaPermilleTolerance, configuration.RangeFaceConfidence, configuration.RangeDistanceTolerance, - configuration.RetryImagesWithoutAFaceLocation.Value, configuration.Reverse.Value, configuration.SaveFaceDistancesForOutputResolutions, configuration.SaveFaceLandmarkForOutputResolutions, diff --git a/Instance/Models/Configuration.cs b/Instance/Models/Configuration.cs index a627186..2068162 100644 --- a/Instance/Models/Configuration.cs +++ b/Instance/Models/Configuration.cs @@ -58,7 +58,6 @@ public class Configuration public int[] RangeFaceAreaPermilleTolerance { init; get; } public double[] RangeFaceConfidence { init; get; } public double[] RangeDistanceTolerance { init; get; } - public bool RetryImagesWithoutAFaceLocation { init; get; } public bool Reverse { init; get; } public string[] SaveFaceDistancesForOutputResolutions { init; get; } public string[] SaveFaceLandmarkForOutputResolutions { init; get; } @@ -126,7 +125,6 @@ public class Configuration int[] rangeFaceAreaPermilleTolerance, double[] rangeFaceConfidence, double[] rangeDistanceTolerance, - bool retryImagesWithoutAFaceLocation, bool reverse, string[] saveFaceDistancesForOutputResolutions, string[] saveFaceLandmarkForOutputResolutions, @@ -193,7 +191,6 @@ public class Configuration RangeFaceAreaPermilleTolerance = rangeFaceAreaPermilleTolerance; RangeFaceConfidence = rangeFaceConfidence; RangeDistanceTolerance = rangeDistanceTolerance; - RetryImagesWithoutAFaceLocation = retryImagesWithoutAFaceLocation; Reverse = reverse; SaveFaceDistancesForOutputResolutions = saveFaceDistancesForOutputResolutions; SaveFaceLandmarkForOutputResolutions = saveFaceLandmarkForOutputResolutions; diff --git a/Instance/appsettings.json b/Instance/appsettings.json index f06e38f..85331e1 100644 --- a/Instance/appsettings.json +++ b/Instance/appsettings.json @@ -101,7 +101,6 @@ "ResultCollection": "[]", "ResultContent": "()", "ResultSingleton": "{}", - "RetryImagesWithoutAFaceLocation": false, "Reverse": false, "RootDirectory": "D:/Images", "SaveFullYearOfRandomFiles": true, diff --git a/Map/Models/DatabaseFileRoot.cs b/Map/Models/DatabaseFileRoot.cs deleted file mode 100644 index 01e31b4..0000000 --- a/Map/Models/DatabaseFileRoot.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace View_by_Distance.Map.Models; - -public record DatabaseFileRoot( - [property: JsonPropertyName("table")] string Table, - [property: JsonPropertyName("rows")] IReadOnlyList Files - ); \ No newline at end of file diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index ed0452f..821739d 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -936,7 +936,7 @@ public class MapLogic return results; } - public void SaveFilteredOriginalImagesFromJLinks(string[] jLinks, string a2PeopleSingletonDirectory, PersonContainer[] personContainers, Mapping[] mappingCollection, Dictionary personKeyToCount, int totalNotMapped) + public void SaveFilteredOriginalImagesFromJLinks(string[] jLinks, PersonContainer[] personContainers, string a2PeopleSingletonDirectory, Mapping[] mappingCollection, Dictionary personKeyToCount, int totalNotMapped) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); @@ -1149,237 +1149,166 @@ public class MapLogic } } - private DatabaseFileRoot GetDatabaseFileRoot() + private static JsonProperty[] GetJsonProperty(string fileName) { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - string file = Path.Combine(_Configuration.PhotoPrismDirectory, "files.json"); - string json = File.ReadAllText(file); - DatabaseFileRoot? databaseFileRoot = JsonSerializer.Deserialize(json); - if (databaseFileRoot is null) - throw new NullReferenceException(nameof(databaseFileRoot)); - return databaseFileRoot; - } - - private Marker[] GetMarkers() - { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - string file = Path.Combine(_Configuration.PhotoPrismDirectory, "markers.json"); - string json = File.ReadAllText(file); - Marker[]? markerRoot = JsonSerializer.Deserialize(json); - if (markerRoot is null) - throw new NullReferenceException(nameof(markerRoot)); - return markerRoot; - } - - private static Dictionary Get(DatabaseFileRoot databaseFileRoot) - { - Dictionary fileUidToFile = new(); - for (int i = 0; i < databaseFileRoot.Files.Count; i++) - fileUidToFile.Add(databaseFileRoot.Files[i].FileUid, databaseFileRoot.Files[i]); - return fileUidToFile; - } - - private static MarkerWith GetMarkerWith(int? dlib, DatabaseFile databaseFile, Marker marker, int? count, double? percent, int? normalizedRectangle, long? personKey, string personKeyFormatted) - { - return new(marker.MarkerUid, - marker.FileUid, - marker.MarkerType, - marker.MarkerSrc, - marker.MarkerName, - marker.MarkerReview, - marker.MarkerInvalid, - marker.SubjUid, - marker.SubjSrc, - marker.FaceId, - marker.FaceDist, - marker.EmbeddingsJson, - marker.LandmarksJson, - marker.X, - marker.Y, - marker.W, - marker.H, - marker.Q, - marker.Size, - marker.Score, - marker.Thumb, - marker.MatchedAt, - marker.CreatedAt, - marker.UpdatedAt, - databaseFile.Id, - databaseFile.FileName, - dlib, - count, - percent, - normalizedRectangle, - personKey, - personKeyFormatted); - } - - private static Dictionary> GetFacesByFileName(List filteredItems) - { - Dictionary> results = new(); - string key; - foreach (Item item in filteredItems) - { - foreach (Face face in item.Faces) - { - if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null || face.Mapping is null) - continue; - key = Path.GetFileNameWithoutExtension(face.Mapping.MappingFromItem.RelativePath); - if (!results.ContainsKey(key)) - results.Add(key, new()); - results[key].Add(face); - } - } + JsonProperty[] results; + string json = File.ReadAllText(fileName); + JsonElement? jsonElement = JsonSerializer.Deserialize(json); + results = jsonElement.Value.EnumerateObject().ToArray(); return results; } - public void FindMatch(List filteredItems) + private static Marker[]? GetMarkers(string photoPrismDirectory) { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - int? dlib; - double? percent; - long? personKey; - const int zero = 0; - List? matches; - MarkerWith markerWith; - int? normalizedRectangle; - string personKeyFormatted; - DatabaseFile? databaseFile; - PersonBirthday personBirthday; - Marker[] markers = GetMarkers(); - string fileNameWithoutExtension; - PersonContainer[]? personContainers; - System.Drawing.Rectangle dlibRectangle; - System.Drawing.Rectangle prismRectangle; - System.Drawing.Rectangle intersectRectangle; - (Face Face, double Percent)[] sortedCollection; - List<(Face Face, double Percent)> collection = new(); - DatabaseFileRoot databaseFileRoot = GetDatabaseFileRoot(); - Dictionary fileUidToFile = Get(databaseFileRoot); - Dictionary? normalizedRectangleToPersonContainers; - Dictionary> keyValuePairs = GetFacesByFileName(filteredItems); - foreach (Marker marker in markers) - { - dlib = null; - personKey = null; - collection.Clear(); - normalizedRectangle = null; - personKeyFormatted = string.Empty; - normalizedRectangleToPersonContainers = null; - if (!fileUidToFile.TryGetValue(marker.FileUid, out databaseFile)) - continue; - fileNameWithoutExtension = Path.GetFileNameWithoutExtension(Path.Combine("C:", databaseFile.FileName)); - prismRectangle = new((int)(marker.X * databaseFile.FileWidth), (int)(marker.Y * databaseFile.FileHeight), (int)(marker.W * databaseFile.FileWidth), (int)(marker.H * databaseFile.FileHeight)); - if (!keyValuePairs.TryGetValue(fileNameWithoutExtension, out matches) || !int.TryParse(fileNameWithoutExtension, out int id)) - percent = null; - else - { - dlib = id; - _ = _IdThenNormalizedRectangleToPersonContainers.TryGetValue(dlib.Value, out normalizedRectangleToPersonContainers); - foreach (Face face in matches) - { - 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 = System.Drawing.Rectangle.Intersect(prismRectangle, dlibRectangle); - if (intersectRectangle.Width == 0 || intersectRectangle.Height == 0) - continue; - percent = (double)intersectRectangle.Width * intersectRectangle.Height / (dlibRectangle.Width * dlibRectangle.Height); - if (percent < 0.000001) - continue; - collection.Add(new(face, percent.Value)); - } - } - if (!collection.Any()) - percent = null; - else - { - sortedCollection = collection.OrderByDescending(l => l.Percent).ToArray(); - percent = sortedCollection[zero].Percent; - normalizedRectangle = sortedCollection[zero].Face.Mapping?.MappingFromLocation.NormalizedRectangle; - if (normalizedRectangleToPersonContainers is null || normalizedRectangle is null || !normalizedRectangleToPersonContainers.TryGetValue(normalizedRectangle.Value, out personContainers)) - personContainers = null; - else - { - foreach (PersonContainer personContainer in personContainers) - { - if (personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any()) - continue; - personBirthday = personContainer.Birthdays[zero]; - personKey = personBirthday.Value.Ticks; - personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personBirthday); - break; - } - } - } - markerWith = GetMarkerWith(dlib, databaseFile, marker, collection.Count, percent, normalizedRectangle, personKey, personKeyFormatted); - string json = JsonSerializer.Serialize(markerWith, new JsonSerializerOptions() { WriteIndented = true }); - if (IPath.WriteAllText(Path.Combine(_Configuration.PhotoPrismDirectory, "With", $"{marker.MarkerUid}.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true)) - continue; - } + Marker[]? results; + string file = Path.Combine(photoPrismDirectory, "markers.json"); + JsonProperty[] jsonProperties = GetJsonProperty(file); + results = JsonSerializer.Deserialize(jsonProperties[1].Value); + return results; } - public void SaveMarkers() + public static void SaveMarkers(string photoPrismDirectory) { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - double[]? encoding; - string file = Path.Combine(_Configuration.PhotoPrismDirectory, "markers.json"); - string json = File.ReadAllText(file); - Marker[]? markers = JsonSerializer.Deserialize(json); + if (string.IsNullOrEmpty(photoPrismDirectory)) + throw new Exception(); + // double[]? encoding; + // Marker[]? markers = GetMarkers(photoPrismDirectory); + // if (markers is null) + // throw new NullReferenceException(nameof(markers)); + // foreach (Marker marker in markers) + // { + // encoding = JsonSerializer.Deserialize(marker.EmbeddingsJson[1..^1]); + // File.WriteAllText(Path.Combine(photoPrismDirectory, "EmbeddingsJson", $"{marker.MarkerUid}.json"), marker.EmbeddingsJson); + // if (encoding is null) + // continue; + // } + } + + private static Dictionary> GetFileUIdToMarkers(string photoPrismDirectory) + { + Dictionary> results = new(); + Marker[]? markers = GetMarkers(photoPrismDirectory); if (markers is null) throw new NullReferenceException(nameof(markers)); foreach (Marker marker in markers) { - encoding = JsonSerializer.Deserialize(marker.EmbeddingsJson[1..^1]); - File.WriteAllText(Path.Combine(_Configuration.PhotoPrismDirectory, "EmbeddingsJson", $"{marker.MarkerUid}.json"), marker.EmbeddingsJson); - if (encoding is null) - continue; + if (!results.ContainsKey(marker.FileUid)) + results.Add(marker.FileUid, new()); + results[marker.FileUid].Add(marker); } + return results; } - public void LoadMatches() + private static DatabaseFile[]? GetDatabaseFiles(string photoPrismDirectory) + { + DatabaseFile[]? results; + string file = Path.Combine(photoPrismDirectory, "files.json"); + JsonProperty[] jsonProperties = GetJsonProperty(file); + results = JsonSerializer.Deserialize(jsonProperties[1].Value); + return results; + } + + public static Dictionary> GetFileNameToCollection(string photoPrismDirectory) + { + Dictionary> results = new(); + List? makers; + MappingFromPhotoPrism mappingFromPhotoPrism; + List? mappingFromPhotoPrismCollection; + DatabaseFile[]? databaseFiles = GetDatabaseFiles(photoPrismDirectory); + if (databaseFiles is null) + throw new NullReferenceException(nameof(databaseFiles)); + Dictionary> fileUIdToMarkers = GetFileUIdToMarkers(photoPrismDirectory); + foreach (DatabaseFile databaseFile in databaseFiles) + { + if (!results.TryGetValue(databaseFile.FileName, out mappingFromPhotoPrismCollection)) + { + results.Add(databaseFile.FileName, new()); + if (!results.TryGetValue(databaseFile.FileName, out mappingFromPhotoPrismCollection)) + throw new Exception(); + } + if (!fileUIdToMarkers.TryGetValue(databaseFile.FileUid, out makers)) + mappingFromPhotoPrism = new(databaseFile, new()); + else + mappingFromPhotoPrism = new(databaseFile, makers); + mappingFromPhotoPrismCollection.Add(mappingFromPhotoPrism); + } + return results; + } + + public void WriteMatches(long ticks, Face[] distinctFilteredFaces) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); - string json; - MarkerWith? markerWith; - List collection = new(); + long? personKey; + const int zero = 0; + int? normalizedRectangle; + string personKeyFormatted; + List subjects = new(); + PersonBirthday personBirthday; + string personDisplayDirectoryName; StringBuilder stringBuilder = new(); - List<(int Count, MarkerWith MarkerWith)> countCollection = new(); - List<(double Percent, MarkerWith MarkerWith)> percentCollection = new(); - string[] files = Directory.GetFiles(Path.Combine(_Configuration.PhotoPrismDirectory, "With"), "*.json", SearchOption.TopDirectoryOnly); - foreach (string file in files) + PersonContainer[]? personContainers; + System.Drawing.Rectangle dlibRectangle; + System.Drawing.Rectangle? prismRectangle; + System.Drawing.Rectangle intersectRectangle; + Dictionary? normalizedRectangleToPersonContainers; + (MappingFromPhotoPrism MappingFromPhotoPrism, Marker Marker, double Percent)[] sortedCollection; + List<(MappingFromPhotoPrism MappingFromPhotoPrism, Marker Marker, double Percent)> collection = new(); + foreach (Face face in distinctFilteredFaces) { - json = File.ReadAllText(file); - markerWith = JsonSerializer.Deserialize(json); - if (markerWith is null || markerWith.DlibId is null) + collection.Clear(); + normalizedRectangle = face.Mapping?.MappingFromLocation.NormalizedRectangle; + if (normalizedRectangle is null) continue; - collection.Add(markerWith); - if (markerWith.Count is null || markerWith.Count.Value == 0) + if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null || face.Mapping is null) continue; - countCollection.Add(new(markerWith.Count.Value, markerWith)); - if (markerWith.Percent is null) + if (face.Mapping.MappingFromPhotoPrismCollection is null) continue; - percentCollection.Add(new(markerWith.Percent.Value, markerWith)); - if (string.IsNullOrEmpty(markerWith.PersonKeyFormatted)) + _ = _IdThenNormalizedRectangleToPersonContainers.TryGetValue(face.Mapping.MappingFromItem.Id, out normalizedRectangleToPersonContainers); + if (normalizedRectangleToPersonContainers is null || !normalizedRectangleToPersonContainers.TryGetValue(normalizedRectangle.Value, out personContainers)) continue; - _ = stringBuilder. - Append("update `markers` set subj_src = 'manual' marker_name = '"). - Append(markerWith.PersonKeyFormatted). - Append("' where marker_uid = '"). - Append(markerWith.MarkerUid). - AppendLine("';"); + dlibRectangle = new(face.Location.Left, face.Location.Top, face.Location.Right - face.Location.Left, face.Location.Bottom - face.Location.Top); + foreach (MappingFromPhotoPrism mappingFromPhotoPrism in face.Mapping.MappingFromPhotoPrismCollection) + { + foreach (Marker marker in mappingFromPhotoPrism.Markers) + { + prismRectangle = ILocation.GetRectangle(face.OutputResolution, mappingFromPhotoPrism.DatabaseFile, marker); + if (prismRectangle is null) + continue; + intersectRectangle = System.Drawing.Rectangle.Intersect(dlibRectangle, prismRectangle.Value); + if (intersectRectangle.Width == 0 || intersectRectangle.Height == 0) + continue; + double percent = (double)intersectRectangle.Width * intersectRectangle.Height / (dlibRectangle.Width * dlibRectangle.Height); + if (percent < 0.000001) + continue; + collection.Add(new(mappingFromPhotoPrism, marker, percent)); + } + } + if (!collection.Any()) + continue; + sortedCollection = collection.OrderByDescending(l => l.Percent).ToArray(); + foreach ((MappingFromPhotoPrism mappingFromPhotoPrism, Marker marker, double percent) in sortedCollection) + { + foreach (PersonContainer personContainer in personContainers) + { + if (personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any()) + continue; + personBirthday = personContainer.Birthdays[zero]; + personKey = personBirthday.Value.Ticks; + personDisplayDirectoryName = personContainer.DisplayDirectoryName; + personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personBirthday); + subjects.Add($"update `subjects` set subj_alias = '{personKeyFormatted}' where subj_name = '{personDisplayDirectoryName}';"); + _ = stringBuilder. + Append("update `markers` set subj_src = 'manual', marker_name = '"). + Append(personDisplayDirectoryName). + Append("' where marker_uid = '"). + Append(marker.MarkerUid). + AppendLine("';"); + } + } } - (int, MarkerWith)[] countSorted = countCollection.OrderByDescending(l => l.Count).ToArray(); - (double, MarkerWith)[] percentSorted = percentCollection.OrderBy(l => l.Percent).ToArray(); - if (collection.Any()) - { } - File.WriteAllText(Path.Combine(_Configuration.PhotoPrismDirectory, "marker_name_update.sql"), stringBuilder.ToString()); + File.WriteAllLines(Path.Combine(_Configuration.PhotoPrismDirectory, $"{ticks}-subject_alias_update.sql"), subjects.Distinct()); + File.WriteAllText(Path.Combine(_Configuration.PhotoPrismDirectory, $"{ticks}-marker_name_update.sql"), stringBuilder.ToString()); } public Dictionary> GetMissing(Dictionary> idToNormalizedRectangleToMapping) diff --git a/Map/Models/Stateless/MapLogic.cs b/Map/Models/Stateless/MapLogic.cs index d74c548..7e00a67 100644 --- a/Map/Models/Stateless/MapLogic.cs +++ b/Map/Models/Stateless/MapLogic.cs @@ -189,7 +189,7 @@ internal abstract class MapLogic return results; } - internal static List<(string, string[], string)> DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory, PersonContainer[] personContainers) + internal static List<(string, string[], string)> DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, PersonContainer[] personContainers, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory) { List<(string, string[], string)> results; string personKeyFormatted; diff --git a/Map/Models/Stateless/Methods/IMapLogic.cs b/Map/Models/Stateless/Methods/IMapLogic.cs index 515e30e..eab6c4e 100644 --- a/Map/Models/Stateless/Methods/IMapLogic.cs +++ b/Map/Models/Stateless/Methods/IMapLogic.cs @@ -3,9 +3,9 @@ namespace View_by_Distance.Map.Models.Stateless.Methods; public interface IMapLogic { // ... - List<(string, string[], string)> TestStatic_DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory, Shared.Models.PersonContainer[] personContainers) => - DeleteEmptyDirectoriesAndGetMappedFaceFiles(configuration, ticks, a2PeopleContentDirectory, eDistanceContentDirectory, personContainers); - static List<(string, string[], string)> DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory, Shared.Models.PersonContainer[] personContainers) => - MapLogic.DeleteEmptyDirectoriesAndGetMappedFaceFiles(configuration, ticks, a2PeopleContentDirectory, eDistanceContentDirectory, personContainers); + List<(string, string[], string)> TestStatic_DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, Shared.Models.PersonContainer[] personContainers, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory) => + DeleteEmptyDirectoriesAndGetMappedFaceFiles(configuration, personContainers, ticks, a2PeopleContentDirectory, eDistanceContentDirectory); + static List<(string, string[], string)> DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, Shared.Models.PersonContainer[] personContainers, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory) => + MapLogic.DeleteEmptyDirectoriesAndGetMappedFaceFiles(configuration, personContainers, ticks, a2PeopleContentDirectory, eDistanceContentDirectory); } \ No newline at end of file diff --git a/Metadata/Models/B_Metadata.cs b/Metadata/Models/B_Metadata.cs index b6ee581..26fc853 100644 --- a/Metadata/Models/B_Metadata.cs +++ b/Metadata/Models/B_Metadata.cs @@ -142,7 +142,7 @@ public class B_Metadata DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max(); if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime)) { - if (!_ForceMetadataLastWriteTimeToCreationTime && (!fileInfo.Exists || fileInfo.LastWriteTime == fileInfo.CreationTime)) + if (!_ForceMetadataLastWriteTimeToCreationTime) subFileTuples.Add(new Tuple(nameof(B_Metadata), DateTime.Now)); else { diff --git a/PrepareForOld/PrepareForOld.cs b/PrepareForOld/PrepareForOld.cs index 2a32434..27ad15a 100644 --- a/PrepareForOld/PrepareForOld.cs +++ b/PrepareForOld/PrepareForOld.cs @@ -267,7 +267,7 @@ // propertyTicks = null; // foreach (PropertyCompare.Models.PropertyCompare propertyCompare in propertyCompareCollection) // { -// if (propertyCompare?.Property?.Id is null) +// if (propertyCompare?.Property?.FileId is null) // continue; // if (ticks != propertyCompare.MinimumDateTime.Ticks) // { @@ -276,7 +276,7 @@ // } // if (!regexResult.Equals(propertyCompare.RegexResult, StringComparison.CurrentCultureIgnoreCase)) // continue; -// propertyId = propertyCompare.Property.Id; +// propertyId = propertyCompare.Property.FileId; // propertyTicks = propertyCompare.MinimumDateTime.Ticks; // mappedIndex = mappedIndices.IndexOf(index); // if (mappedIndex > -1) @@ -290,7 +290,7 @@ // string[] duplicatesLines = ( // from l // in duplicates -// select $"{l.Property.Id}\t{l.RegexResult}\t{l.MinimumDateTime:yyyy-MM-dd_HH-mm-ss}\t{l.MinimumDateTime}\t{l.RelativeDirectory}\t{l.FileNameWithoutExtension}{l.Extension}.___" +// select $"{l.Property.FileId}\t{l.RegexResult}\t{l.MinimumDateTime:yyyy-MM-dd_HH-mm-ss}\t{l.MinimumDateTime}\t{l.RelativeDirectory}\t{l.FileNameWithoutExtension}{l.Extension}.___" // ).ToArray(); // string[] matchedLines = ( // from l @@ -575,11 +575,11 @@ // Dictionary> results = new(); // foreach (PropertyCompare.Models.PropertyCompare propertyCompare in propertyCompares) // { -// if (propertyCompare?.Property?.Id is null) +// if (propertyCompare?.Property?.FileId is null) // continue; -// if (!results.ContainsKey(propertyCompare.Property.Id.Value)) -// results.Add(propertyCompare.Property.Id.Value, new()); -// results[propertyCompare.Property.Id.Value].Add(propertyCompare); +// if (!results.ContainsKey(propertyCompare.Property.FileId.Value)) +// results.Add(propertyCompare.Property.FileId.Value, new()); +// results[propertyCompare.Property.FileId.Value].Add(propertyCompare); // } // return results; // } @@ -628,9 +628,9 @@ // if (keyValuePairs.ContainsKey(index)) // { // propertyCompare = keyValuePairs[index][0]; -// if (propertyCompare?.Property?.Id is null) +// if (propertyCompare?.Property?.FileId is null) // continue; -// propertyId = propertyCompare.Property.Id; +// propertyId = propertyCompare.Property.FileId; // propertyTicks = propertyCompare.MinimumDateTime.Ticks; // } // collection.Add(new(index, relativeDirectory, $"{fileNameWithoutExtension}{extension}", regexResult, ticks, propertyId, propertyTicks)); diff --git a/Property-Compare/Models/PropertyCompare.cs b/Property-Compare/Models/PropertyCompare.cs index 40c5b50..9f97480 100644 --- a/Property-Compare/Models/PropertyCompare.cs +++ b/Property-Compare/Models/PropertyCompare.cs @@ -44,7 +44,7 @@ public partial class PropertyCompare return result; } - // internal static string GetSelect(PropertyCompare item) => string.Concat(item.JsonFileNameWithoutExtension, item.ImageFileName, '\t', item.Property.CreationTime.Ticks, '\t', GetDateTime(item.Property).ToString("yyyy-MM-dd_HH-mm-ss"), '\t', item.Property.Id, '\t', item.Property.FileSize, '\t', item.Property.Width, '\t', item.Property.Height); + // internal static string GetSelect(PropertyCompare item) => string.Concat(item.JsonFileNameWithoutExtension, item.ImageFileName, '\t', item.Property.CreationTime.Ticks, '\t', GetDateTime(item.Property).ToString("yyyy-MM-dd_HH-mm-ss"), '\t', item.Property.FileId, '\t', item.Property.FileSize, '\t', item.Property.Width, '\t', item.Property.Height); #nullable disable diff --git a/Property-Compare/Models/PropertyCompareLogic.cs b/Property-Compare/Models/PropertyCompareLogic.cs index 172d012..ee7d001 100644 --- a/Property-Compare/Models/PropertyCompareLogic.cs +++ b/Property-Compare/Models/PropertyCompareLogic.cs @@ -176,7 +176,7 @@ // extension = Path.GetExtension(files[index]); // string json = File.ReadAllText(jsonFile); // Shared.Models.Property? property = JsonSerializer.Deserialize(json); -// if (property?.Id is null) +// if (property?.FileId is null) // throw new NullReferenceException(nameof(property)); // DateTime minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(property); // corrected = string.Concat(relativeDirectory, jsonFileNameWithoutExtension); @@ -195,11 +195,11 @@ // { // n = i switch // { -// 0 => property.Id.Value, -// 1 => property.Id.Value, -// 2 => property.Id.Value, -// 3 => property.Id.Value, -// 4 => property.Id.Value, +// 0 => property.FileId.Value, +// 1 => property.FileId.Value, +// 2 => property.FileId.Value, +// 3 => property.FileId.Value, +// 4 => property.FileId.Value, // 5 => Shared.Models.Stateless.Methods.IProperty.GetDateTime(property).Ticks, // 6 => property.CreationTime.Ticks, // 7 => property.FileSize, @@ -229,8 +229,8 @@ // results.Add(propertyCompare); // if (duplicates is not null) // { -// string value = $"{property.Id.Value}\t{property}"; -// index = ids.IndexOf(property.Id.Value); +// string value = $"{property.FileId.Value}\t{property}"; +// index = ids.IndexOf(property.FileId.Value); // if (index > -1) // { // lock (@lock) @@ -241,7 +241,7 @@ // } // lock (@lock) // { -// ids.Add(property.Id.Value); +// ids.Add(property.FileId.Value); // collection.Add(propertyCompare); // } // } diff --git a/Property/Models/A_Property.cs b/Property/Models/A_Property.cs index 4aa4cac..f938b5c 100644 --- a/Property/Models/A_Property.cs +++ b/Property/Models/A_Property.cs @@ -333,7 +333,7 @@ public class A_Property json = JsonSerializer.Serialize(result, _WriteIndentedJsonSerializerOptions); if (populateId && Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: true, compareBeforeWrite: true)) { - if (!_Configuration.ForcePropertyLastWriteTimeToCreationTime && (!fileInfo.Exists || fileInfo.LastWriteTime == fileInfo.CreationTime)) + if (!_Configuration.ForcePropertyLastWriteTimeToCreationTime) sourceDirectoryFileTuples.Add(new Tuple(nameof(A_Property), DateTime.Now)); else { diff --git a/Property/Models/Binder/Configuration.cs b/Property/Models/Binder/Configuration.cs index 33c46fd..cd5cc8a 100644 --- a/Property/Models/Binder/Configuration.cs +++ b/Property/Models/Binder/Configuration.cs @@ -16,7 +16,7 @@ public class Configuration [Display(Name = "Ignore Extensions"), Required] public string[] IgnoreExtensions { get; set; } [Display(Name = "Max Images In Directory For Top Level First Pass"), Required] public int? MaxImagesInDirectoryForTopLevelFirstPass { get; set; } [Display(Name = "Pattern"), Required] public string Pattern { get; set; } - [Display(Name = "Populate Properties Id"), Required] public bool? PopulatePropertyId { get; set; } + [Display(Name = "Populate Properties FileId"), Required] public bool? PopulatePropertyId { get; set; } [Display(Name = "Properties Changed For Property"), Required] public bool? PropertiesChangedForProperty { get; set; } [Display(Name = "Property Content Collection Files"), Required] public string[] PropertyContentCollectionFiles { get; set; } [Display(Name = "Result All In One"), Required] public string ResultAllInOne { get; set; } diff --git a/Property/Models/Stateless/Container.cs b/Property/Models/Stateless/Container.cs index 148cadd..c6069b0 100644 --- a/Property/Models/Stateless/Container.cs +++ b/Property/Models/Stateless/Container.cs @@ -216,6 +216,7 @@ public class Container { string relativePath; string checkFileName; + string? checkDirectoryName; List checkCollection = new(); checkCollection.AddRange(otherCollection); int length = configuration.RootDirectory.Length; @@ -225,6 +226,9 @@ public class Container { relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(fileHolder.FullName, length); checkFileName = Path.GetFullPath(string.Concat(aPropertySingletonDirectory, relativePath)); + checkDirectoryName = Path.GetDirectoryName(checkFileName); + if (string.IsNullOrEmpty(checkDirectoryName) || !Directory.Exists(checkDirectoryName)) + continue; if (!checkCollection.Remove(checkFileName)) File.WriteAllText(checkFileName, string.Empty); } diff --git a/Resize/Models/_C_Resize.cs b/Resize/Models/_C_Resize.cs index db4456a..18d44a1 100644 --- a/Resize/Models/_C_Resize.cs +++ b/Resize/Models/_C_Resize.cs @@ -505,7 +505,7 @@ public class C_Resize DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max(); if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime)) { - if (!_ForceResizeLastWriteTimeToCreationTime && (!fileInfo.Exists || fileInfo.LastWriteTime == fileInfo.CreationTime)) + if (!_ForceResizeLastWriteTimeToCreationTime) subFileTuples.Add(new Tuple(nameof(C_Resize), DateTime.Now)); else { diff --git a/Map/Models/DatabaseFile.cs b/Shared/Models/DatabaseFile.cs similarity index 98% rename from Map/Models/DatabaseFile.cs rename to Shared/Models/DatabaseFile.cs index 1735503..6d07108 100644 --- a/Map/Models/DatabaseFile.cs +++ b/Shared/Models/DatabaseFile.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace View_by_Distance.Map.Models; +namespace View_by_Distance.Shared.Models; public record DatabaseFile( [property: JsonPropertyName("id")] int Id, diff --git a/Shared/Models/Mapping.cs b/Shared/Models/Mapping.cs index 40906cb..5524d8f 100644 --- a/Shared/Models/Mapping.cs +++ b/Shared/Models/Mapping.cs @@ -74,6 +74,21 @@ public class MappingFromLocation : Properties.IMappingFromLocation } +public class MappingFromPhotoPrism : Properties.IMappingFromPhotoPrism +{ + + public DatabaseFile DatabaseFile { init; get; } + public List Markers { init; get; } + + [JsonConstructor] + public MappingFromPhotoPrism(DatabaseFile databaseFile, List markers) + { + DatabaseFile = databaseFile; + Markers = markers; + } + +} + public class MappingFromPerson : Properties.IMappingFromPerson { @@ -109,23 +124,25 @@ public class Mapping : Properties.IMapping public int? By => _By; public MappingFromItem MappingFromItem { init; get; } public MappingFromLocation MappingFromLocation { init; get; } + public List? MappingFromPhotoPrismCollection { init; get; } public MappingFromPerson? MappingFromPerson => _MappingFromPerson; public string? SegmentC => _SegmentC; public SortingContainer? SortingContainer => _SortingContainer; [JsonConstructor] - public Mapping(int? by, MappingFromItem mappingFromItem, MappingFromLocation mappingFromLocation, MappingFromPerson? mappingFromPerson, string? segmentC, SortingContainer? sortingContainer) + public Mapping(int? by, MappingFromItem mappingFromItem, MappingFromLocation mappingFromLocation, List? mappingFromPhotoPrismCollection, MappingFromPerson? mappingFromPerson, string? segmentC, SortingContainer? sortingContainer) { _By = by; _SegmentC = segmentC; MappingFromItem = mappingFromItem; MappingFromLocation = mappingFromLocation; + MappingFromPhotoPrismCollection = mappingFromPhotoPrismCollection; _MappingFromPerson = mappingFromPerson; _SortingContainer = sortingContainer; } - public Mapping(MappingFromItem mappingFromItem, MappingFromLocation mappingFromLocation) : - this(null, mappingFromItem, mappingFromLocation, null, null, null) + public Mapping(MappingFromItem mappingFromItem, MappingFromLocation mappingFromLocation, List? mappingFromPhotoPrismCollection) : + this(null, mappingFromItem, mappingFromLocation, mappingFromPhotoPrismCollection, null, null, null) { } public override string ToString() diff --git a/Shared/Models/Marker.cs b/Shared/Models/Marker.cs index bafd9d8..9c06229 100644 --- a/Shared/Models/Marker.cs +++ b/Shared/Models/Marker.cs @@ -14,8 +14,8 @@ public record Marker( [property: JsonPropertyName("subj_src")] string SubjSrc, [property: JsonPropertyName("face_id")] string FaceId, [property: JsonPropertyName("face_dist")] double FaceDist, - [property: JsonPropertyName("embeddings_json")] string EmbeddingsJson, - [property: JsonPropertyName("landmarks_json")] string LandmarksJson, + // [property: JsonPropertyName("embeddings_json")] string EmbeddingsJson, + // [property: JsonPropertyName("landmarks_json")] string LandmarksJson, [property: JsonPropertyName("x")] double X, [property: JsonPropertyName("y")] double Y, [property: JsonPropertyName("w")] double W, diff --git a/Shared/Models/MarkerWith.cs b/Shared/Models/MarkerWith.cs deleted file mode 100644 index 51e5acb..0000000 --- a/Shared/Models/MarkerWith.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace View_by_Distance.Shared.Models; - -public record MarkerWith( - string MarkerUid, - string FileUid, - string MarkerType, - string MarkerSrc, - string MarkerName, - int MarkerReview, - int MarkerInvalid, - string SubjUid, - string SubjSrc, - string FaceId, - double FaceDist, - string EmbeddingsJson, - string LandmarksJson, - double X, - double Y, - double W, - double H, - int Q, - int Size, - int Score, - string Thumb, - string MatchedAt, - string CreatedAt, - string UpdatedAt, - int FileId, - string FileName, - int? DlibId, - int? Count, - double? Percent, - int? NormalizedRectangle, - long? PersonKey, - string PersonKeyFormatted -); \ No newline at end of file diff --git a/Shared/Models/Properties/IMapping.cs b/Shared/Models/Properties/IMapping.cs index 069e6d7..315f3c4 100644 --- a/Shared/Models/Properties/IMapping.cs +++ b/Shared/Models/Properties/IMapping.cs @@ -23,6 +23,14 @@ public interface IMappingFromLocation } +public interface IMappingFromPhotoPrism +{ + + public DatabaseFile DatabaseFile { init; get; } + public List Markers { init; get; } + +} + public interface IMappingFromPerson { @@ -40,6 +48,7 @@ public interface IMapping public string? SegmentC { get; } public MappingFromItem MappingFromItem { init; get; } public MappingFromLocation MappingFromLocation { init; get; } + public List? MappingFromPhotoPrismCollection { init; get; } public MappingFromPerson? MappingFromPerson { get; } public SortingContainer? SortingContainer { get; } diff --git a/Shared/Models/Stateless/Methods/ILocation.cs b/Shared/Models/Stateless/Methods/ILocation.cs index 73b808a..958a1ad 100644 --- a/Shared/Models/Stateless/Methods/ILocation.cs +++ b/Shared/Models/Stateless/Methods/ILocation.cs @@ -5,6 +5,21 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface ILocation { // ... + Rectangle? TestStatic_GetRectangle(Models.OutputResolution outputResolution, DatabaseFile databaseFile, Marker marker) => + GetRectangle(outputResolution, databaseFile, marker); + static Rectangle? GetRectangle(Models.OutputResolution outputResolution, DatabaseFile databaseFile, Marker marker) => + Location.GetRectangle(outputResolution, databaseFile, marker); + + Models.Location? TestStatic_GetLocation(Models.OutputResolution outputResolution, DatabaseFile databaseFile, Marker marker) => + GetLocation(outputResolution, databaseFile, marker); + 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); + 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) => diff --git a/Shared/Models/Stateless/Methods/IPath.cs b/Shared/Models/Stateless/Methods/IPath.cs index add364f..b41259e 100644 --- a/Shared/Models/Stateless/Methods/IPath.cs +++ b/Shared/Models/Stateless/Methods/IPath.cs @@ -17,6 +17,7 @@ public interface IPath DeleteEmptyDirectories(rootDirectory, deletedDirectories); static void DeleteEmptyDirectories(string rootDirectory, List deletedDirectories) => XPath.DeleteEmptyDirectories(rootDirectory, deletedDirectories); + // $dirs = gci "" -directory -recurse | Where { (gci $_.fullName).count -eq 0 } | select -expandproperty FullName $dirs | Foreach-Object { Remove-Item $_ } string[] TestStatic_GetDirectoryNames(string directory) => GetDirectoryNames(directory); diff --git a/Shared/Models/Stateless/Methods/Location.cs b/Shared/Models/Stateless/Methods/Location.cs index e586c40..4356d47 100644 --- a/Shared/Models/Stateless/Methods/Location.cs +++ b/Shared/Models/Stateless/Methods/Location.cs @@ -189,4 +189,81 @@ internal abstract class Location return result.Value; } + private static bool Matches(Models.OutputResolution outputResolution, DatabaseFile databaseFile) + { + bool result = outputResolution.Height == databaseFile.FileHeight && outputResolution.Width == databaseFile.FileWidth; + return result; + } + + internal static Rectangle? GetRectangle(Models.OutputResolution outputResolution, DatabaseFile databaseFile, Marker marker) + { + Rectangle? result; + bool matches = Matches(outputResolution, databaseFile); + if (!matches) + result = null; + else + result = new((int)Math.Ceiling(marker.X * databaseFile.FileWidth), (int)Math.Ceiling(marker.Y * databaseFile.FileHeight), (int)Math.Ceiling(marker.W * databaseFile.FileWidth), (int)Math.Ceiling(marker.H * databaseFile.FileHeight)); + return result; + } + + internal static Models.Location? GetLocation(Marker marker, Rectangle rectangle) + { + Models.Location? result; + bool verified = Check(rectangle.Bottom, rectangle.Height, rectangle.Left, rectangle.Right, rectangle.Top, rectangle.Width, zCount: 1, throwException: false); + if (!verified) + result = null; + else + result = new(rectangle.Bottom, marker.Score / 100, rectangle.Left, rectangle.Right, rectangle.Top); + return result; + } + + internal static Models.Location? GetLocation(Models.OutputResolution outputResolution, DatabaseFile databaseFile, Marker marker) + { + Models.Location? result; + Rectangle? rectangle = GetRectangle(outputResolution, databaseFile, marker); + if (rectangle is null) + result = null; + else + result = GetLocation(marker, rectangle.Value); + return result; + } + + internal static List GetLocations(List mappingFromPhotoPrismCollection, List faces) + { + List results = new(); + double percent; + Rectangle dlibRectangle; + Rectangle? prismRectangle; + Models.Location? location; + Rectangle intersectRectangle; + foreach (Models.Face face in faces) + { + if (face.Location is null || face.OutputResolution is null) + continue; + foreach (MappingFromPhotoPrism mappingFromPhotoPrism in mappingFromPhotoPrismCollection) + { + dlibRectangle = new(face.Location.Left, face.Location.Top, face.Location.Right - face.Location.Left, face.Location.Bottom - face.Location.Top); + foreach (Marker marker in mappingFromPhotoPrism.Markers) + { + prismRectangle = GetRectangle(face.OutputResolution, mappingFromPhotoPrism.DatabaseFile, marker); + if (prismRectangle is null) + { + location = new(1, 0, 0, 1, 0); + results.Add(location); + continue; + } + location = GetLocation(marker, prismRectangle.Value); + if (location is null) + continue; + intersectRectangle = Rectangle.Intersect(dlibRectangle, prismRectangle.Value); + percent = (double)intersectRectangle.Width * intersectRectangle.Height / (dlibRectangle.Width * dlibRectangle.Height); + if (percent > 0.000001) + continue; + results.Add(location); + } + } + } + return results; + } + } \ No newline at end of file diff --git a/TestsWithFaceRecognitionDotNet/Models/Binder/Configuration.cs b/TestsWithFaceRecognitionDotNet/Models/Binder/Configuration.cs index fc2feae..4135e98 100644 --- a/TestsWithFaceRecognitionDotNet/Models/Binder/Configuration.cs +++ b/TestsWithFaceRecognitionDotNet/Models/Binder/Configuration.cs @@ -60,7 +60,6 @@ public class Configuration [Display(Name = "Face Area Permille Tolerance"), Required] public int[] RangeFaceAreaPermilleTolerance { get; set; } [Display(Name = "Location Minimum Confidence"), Required] public double[] RangeFaceConfidence { get; set; } [Display(Name = "Face Distance Tolerance"), Required] public double[] RangeDistanceTolerance { get; set; } - [Display(Name = "Retry Images Without a Face"), Required] public bool? RetryImagesWithoutAFaceLocation { get; set; } [Display(Name = "Reverse"), Required] public bool? Reverse { get; set; } [Display(Name = "Save Face Landmark For Output Resolutions"), Required] public string[] SaveFaceLandmarkForOutputResolutions { get; set; } [Display(Name = "Save Full Year Of Random Files"), Required] public bool? SaveFullYearOfRandomFiles { get; set; } @@ -171,8 +170,6 @@ public class Configuration throw new NullReferenceException(nameof(configuration.RangeFaceConfidence)); if (configuration.RangeDistanceTolerance is null || configuration.RangeDistanceTolerance.Length != 3) throw new NullReferenceException(nameof(configuration.RangeDistanceTolerance)); - if (configuration.RetryImagesWithoutAFaceLocation is null) - throw new NullReferenceException(nameof(configuration.RetryImagesWithoutAFaceLocation)); if (configuration.Reverse is null) throw new NullReferenceException(nameof(configuration.Reverse)); configuration.SaveFaceLandmarkForOutputResolutions ??= Array.Empty(); @@ -245,7 +242,6 @@ public class Configuration configuration.RangeFaceAreaPermilleTolerance, configuration.RangeFaceConfidence, configuration.RangeDistanceTolerance, - configuration.RetryImagesWithoutAFaceLocation.Value, configuration.Reverse.Value, configuration.SaveFaceLandmarkForOutputResolutions, configuration.SaveFullYearOfRandomFiles.Value, diff --git a/TestsWithFaceRecognitionDotNet/Models/Configuration.cs b/TestsWithFaceRecognitionDotNet/Models/Configuration.cs index 0dbefcb..7327dfd 100644 --- a/TestsWithFaceRecognitionDotNet/Models/Configuration.cs +++ b/TestsWithFaceRecognitionDotNet/Models/Configuration.cs @@ -58,7 +58,6 @@ public class Configuration public int[] RangeFaceAreaPermilleTolerance { init; get; } public double[] RangeFaceConfidence { init; get; } public double[] RangeDistanceTolerance { init; get; } - public bool RetryImagesWithoutAFaceLocation { init; get; } public bool Reverse { init; get; } public string[] SaveFaceLandmarkForOutputResolutions { init; get; } public bool SaveFullYearOfRandomFiles { init; get; } @@ -123,7 +122,6 @@ public class Configuration int[] rangeFaceAreaPermilleTolerance, double[] rangeFaceConfidence, double[] rangeDistanceTolerance, - bool retryImagesWithoutAFaceLocation, bool reverse, string[] saveFaceLandmarkForOutputResolutions, bool saveFullYearOfRandomFiles, @@ -187,7 +185,6 @@ public class Configuration RangeFaceAreaPermilleTolerance = rangeFaceAreaPermilleTolerance; RangeFaceConfidence = rangeFaceConfidence; RangeDistanceTolerance = rangeDistanceTolerance; - RetryImagesWithoutAFaceLocation = retryImagesWithoutAFaceLocation; Reverse = reverse; SaveFaceLandmarkForOutputResolutions = saveFaceLandmarkForOutputResolutions; SaveFullYearOfRandomFiles = saveFullYearOfRandomFiles; diff --git a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs index 016ed16..4accd81 100644 --- a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs +++ b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs @@ -231,7 +231,7 @@ public class UnitTestFace (Model model, PredictorModel predictorModel, ModelParameter modelParameter) = GetModel(_Configuration); FaceRecognition faceRecognition = new(_Configuration.NumberOfJitters, _Configuration.NumberOfTimesToUpsample, model, modelParameter, predictorModel); List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary? FaceParts)> collection; - collection = faceRecognition.GetCollection(image, includeFaceEncoding: true, includeFaceParts: true); + collection = faceRecognition.GetCollection(image, locations: new(), includeFaceEncoding: true, includeFaceParts: true); Assert.IsTrue(collection.Count == 2); List faceDistanceEncodings = (from l in collection where l.FaceEncoding is not null select new FaceDistance(l.FaceEncoding)).ToList(); List faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncodings[0]);