diff --git a/.kanbn/index.md b/.kanbn/index.md index 14e19b7..9e37da4 100644 --- a/.kanbn/index.md +++ b/.kanbn/index.md @@ -13,6 +13,9 @@ completedColumns: ## Todo +- [nef-support](tasks/nef-support.md) +- [determine-if-location-container-collection-is-needed-in-get-faces](tasks/determine-if-location-container-collection-is-needed-in-get-faces.md) +- [use-eyes-to-find-orientation](tasks/use-eyes-to-find-orientation.md) - [use-photo-prism-to-map](tasks/use-photo-prism-to-map.md) - [import-face-region-metadata](tasks/import-face-region-metadata.md) diff --git a/.kanbn/tasks/determine-if-location-container-collection-is-needed-in-get-faces.md b/.kanbn/tasks/determine-if-location-container-collection-is-needed-in-get-faces.md new file mode 100644 index 0000000..a894a39 --- /dev/null +++ b/.kanbn/tasks/determine-if-location-container-collection-is-needed-in-get-faces.md @@ -0,0 +1,17 @@ +--- +created: 2023-06-12T15:44:11.932Z +updated: 2023-06-12T15:45:59.891Z +assigned: "" +progress: 0 +tags: [] +--- + +# determine-if-location-container-collection-is-needed-in-get-faces + +```c# +locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(collection, results, mappingFromPhotoPrismCollection, _RectangleIntersectMinimum); +``` + +## Sub-tasks + +- [ ] See code above diff --git a/.kanbn/tasks/nef-support.md b/.kanbn/tasks/nef-support.md new file mode 100644 index 0000000..5c99289 --- /dev/null +++ b/.kanbn/tasks/nef-support.md @@ -0,0 +1,13 @@ +--- +created: 2023-06-12T15:44:11.932Z +updated: 2023-06-12T15:45:59.891Z +assigned: "" +progress: 0 +tags: [] +--- + +# nef-support + +## Sub-tasks + +- [ ] asdf diff --git a/.kanbn/tasks/use-eyes-to-find-orientation.md b/.kanbn/tasks/use-eyes-to-find-orientation.md new file mode 100644 index 0000000..4f97d08 --- /dev/null +++ b/.kanbn/tasks/use-eyes-to-find-orientation.md @@ -0,0 +1,13 @@ +--- +created: 2023-06-12T15:44:11.932Z +updated: 2023-06-12T15:45:59.891Z +assigned: "" +progress: 0 +tags: [] +--- + +# use-eyes-to-find-orientation + +## Sub-tasks + +- [ ] asdf diff --git a/.vscode/settings.json b/.vscode/settings.json index f5bb064..e1798cd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,6 +18,7 @@ "Greyscale", "Hasher", "jfif", + "JOSN", "mmod", "Nicéphore", "Niépce", @@ -42,5 +43,8 @@ "**/.git": false }, "coverage-gutters.coverageBaseDir": "./.vscode/ReportGenerator/Cobertura/*", - "extensions.ignoreRecommendations": true + "extensions.ignoreRecommendations": true, + "[markdown]": { + "editor.wordWrap": "off" + } } \ No newline at end of file diff --git a/Compare/appsettings.json b/Compare/appsettings.json index de0168d..c59f23b 100644 --- a/Compare/appsettings.json +++ b/Compare/appsettings.json @@ -92,6 +92,8 @@ "IgnoreExtensions": [ ".gif", ".GIF", + ".nef", + ".NEF", ".pdf", ".PDF" ], diff --git a/Copy-Distinct/appsettings.json b/Copy-Distinct/appsettings.json index bebad25..7728b8e 100644 --- a/Copy-Distinct/appsettings.json +++ b/Copy-Distinct/appsettings.json @@ -72,6 +72,8 @@ "IgnoreExtensions": [ ".gif", ".GIF", + ".nef", + ".NEF", ".pdf", ".PDF" ], diff --git a/Date-Group/appsettings.json b/Date-Group/appsettings.json index 0399e35..e69bb92 100644 --- a/Date-Group/appsettings.json +++ b/Date-Group/appsettings.json @@ -72,6 +72,8 @@ "IgnoreExtensions": [ ".gif", ".GIF", + ".nef", + ".NEF", ".pdf", ".PDF" ], diff --git a/Delete-By-Distinct/appsettings.json b/Delete-By-Distinct/appsettings.json index eebe64a..d5592fb 100644 --- a/Delete-By-Distinct/appsettings.json +++ b/Delete-By-Distinct/appsettings.json @@ -74,6 +74,8 @@ "IgnoreExtensions": [ ".gif", ".GIF", + ".nef", + ".NEF", ".pdf", ".PDF" ], diff --git a/Delete-By-Relative/appsettings.json b/Delete-By-Relative/appsettings.json index 7d41ede..d0e1ad8 100644 --- a/Delete-By-Relative/appsettings.json +++ b/Delete-By-Relative/appsettings.json @@ -69,6 +69,8 @@ "IgnoreExtensions": [ ".gif", ".GIF", + ".nef", + ".NEF", ".pdf", ".PDF" ], diff --git a/Distance/Models/_E_Distance.cs b/Distance/Models/_E_Distance.cs index bede9d6..34fae4d 100644 --- a/Distance/Models/_E_Distance.cs +++ b/Distance/Models/_E_Distance.cs @@ -23,7 +23,7 @@ public partial class E_Distance private readonly double _RangeDistanceToleranceAverage; private readonly List _DuplicateMappedFaceFiles; - public E_Distance(bool distanceMoveUnableToMatch, bool distanceRenameToMatch, int faceConfidencePercent, float[] rangeDistanceTolerance, float[] rangeFaceConfidence, float[] rectangleIntersectMinimum) + public E_Distance(bool distanceMoveUnableToMatch, bool distanceRenameToMatch, int faceConfidencePercent, float[] rangeDistanceTolerance, float[] rangeFaceConfidence, float[] rectangleIntersectMinimums) { _Debug = new(); _Moved = new(); @@ -36,7 +36,7 @@ public partial class E_Distance _DistanceRenameToMatch = distanceRenameToMatch; _FaceConfidencePercent = faceConfidencePercent; _DistanceMoveUnableToMatch = distanceMoveUnableToMatch; - _RectangleIntersectMinimum = rectangleIntersectMinimum.Max(); + _RectangleIntersectMinimum = rectangleIntersectMinimums.Max(); _RangeDistanceToleranceAverage = rangeDistanceTolerance.Average(); } @@ -199,7 +199,7 @@ public partial class E_Distance } } - public void LookForMatchFacesAndPossiblyRename(string facesFileNameExtension, MappingFromItem mappingFromItem, List faces, List> collection) + public void LookForMatchFacesAndPossiblyRename(string facesFileNameExtension, MappingFromItem mappingFromItem, List faces, List> locationContainers) { string? json; string fileName; @@ -211,7 +211,7 @@ public partial class E_Distance Face[] filteredFaces = (from l in faces where l.FaceEncoding is not null && l.Location is not null && l.OutputResolution is not null select l).ToArray(); if (filteredFaces.Length != faces.Count) checkFaces.Clear(); - foreach (LocationContainer? locationContainer in collection) + foreach (LocationContainer? locationContainer in locationContainers) { if (_Renamed.Contains(locationContainer.File)) continue; diff --git a/Duplicate-Search/appsettings.json b/Duplicate-Search/appsettings.json index dac2ccc..2640b31 100644 --- a/Duplicate-Search/appsettings.json +++ b/Duplicate-Search/appsettings.json @@ -71,6 +71,8 @@ "IgnoreExtensions": [ ".gif", ".GIF", + ".nef", + ".NEF", ".pdf", ".PDF" ], diff --git a/Face/Models/_D_Face.cs b/Face/Models/_D_Face.cs index 0693201..94f26b2 100644 --- a/Face/Models/_D_Face.cs +++ b/Face/Models/_D_Face.cs @@ -34,12 +34,14 @@ public class D_Face private readonly Serilog.ILogger? _Log; private readonly bool _OverrideForFaceImages; private readonly Configuration _Configuration; + private readonly bool _LoadPhotoPrismLocations; private readonly ImageCodecInfo _ImageCodecInfo; private readonly ModelParameter _ModelParameter; private readonly PredictorModel _PredictorModel; private readonly bool _CheckDFaceAndUpWriteDates; private readonly bool _PropertiesChangedForFaces; private readonly ConstructorInfo _ConstructorInfo; + private readonly float _RectangleIntersectMinimum; private readonly int _FaceDistanceHiddenImageFactor; private readonly EncoderParameters _EncoderParameters; private readonly ImageCodecInfo _HiddenImageCodecInfo; @@ -59,11 +61,13 @@ public class D_Face string hiddenFileNameExtension, ImageCodecInfo hiddenImageCodecInfo, ImageCodecInfo imageCodecInfo, + bool loadPhotoPrismLocations, string modelDirectory, string modelName, bool overrideForFaceImages, string predictorModelName, - bool propertiesChangedForFaces) + bool propertiesChangedForFaces, + float[] rectangleIntersectMinimums) { _ArgZero = argZero; _Configuration = configuration; @@ -76,17 +80,17 @@ public class D_Face _OverrideForFaceImages = overrideForFaceImages; _HiddenEncoderParameters = hiddenEncoderParameters; _HiddenFileNameExtension = hiddenFileNameExtension; + _LoadPhotoPrismLocations = loadPhotoPrismLocations; _CheckDFaceAndUpWriteDates = checkDFaceAndUpWriteDates; _PropertiesChangedForFaces = propertiesChangedForFaces; + _RectangleIntersectMinimum = rectangleIntersectMinimums.Min(); _FaceDistanceHiddenImageFactor = faceDistanceHiddenImageFactor; _ForceFaceLastWriteTimeToCreationTime = forceFaceLastWriteTimeToCreationTime; (Model model, PredictorModel predictorModel, ModelParameter modelParameter) = GetModel(modelDirectory, modelName, predictorModelName); _Model = model; _PredictorModel = predictorModel; _ModelParameter = modelParameter; - ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, Array.Empty(), null); - if (constructorInfo is null) - throw new Exception(); + ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, Array.Empty(), null) ?? throw new Exception(); _ConstructorInfo = constructorInfo; _WriteIndentedAndWhenWritingNull = new JsonSerializerOptions { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; } @@ -294,7 +298,7 @@ public class D_Face #pragma warning restore CA1416 - private static List> GetCollection(string outputResolution, List> collection, Dictionary outputResolutionToResize, List faces) + private static List> GetLocationContainers(string outputResolution, List> locationContainers, Dictionary outputResolutionToResize, List faces) { List> results = new(); string? json; @@ -309,7 +313,7 @@ public class D_Face continue; skip.Add(Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, ILocation.Digits, face.OutputResolution)); } - foreach (LocationContainer locationContainer in collection) + foreach (LocationContainer locationContainer in locationContainers) { if (locationContainer.Directories is null) continue; @@ -340,7 +344,7 @@ public class D_Face return results; } - public List GetFaces(string outputResolution, string dResultsFullGroupDirectory, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List>? collection, List? mappingFromPhotoPrismCollection) + public List GetFaces(string outputResolution, string dResultsFullGroupDirectory, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List>? locationContainers, List? mappingFromPhotoPrismCollection) { List? results; if (string.IsNullOrEmpty(dResultsFullGroupDirectory)) @@ -383,15 +387,15 @@ public class D_Face parseExceptions.Add(nameof(D_Face)); } } - List> locationContainers; - if (results is null || collection is null) - locationContainers = new(); + List> collection; + if (results is null || locationContainers is null) + collection = new(); else - locationContainers = GetCollection(outputResolution, collection, outputResolutionToResize, results); - if (mappingFromPhotoPrismCollection is null || results is null) - locations = (from l in locationContainers where l is not null select l.Location).ToList(); + 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(); else - locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(mappingFromPhotoPrismCollection, results, locationContainers); + locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(collection, results, mappingFromPhotoPrismCollection, _RectangleIntersectMinimum); if (results is null || (locations is not null && locations.Any())) { results = GetFaces(outputResolution, property, mappingFromItem, outputResolutionToResize, locations); diff --git a/FaceRecognitionDotNet/Point.cs b/FaceRecognitionDotNet/Point.cs index 993d70f..b4206af 100644 --- a/FaceRecognitionDotNet/Point.cs +++ b/FaceRecognitionDotNet/Point.cs @@ -3,7 +3,7 @@ /// /// Represents an ordered pair of integer x- and y-coordinates that defines a point in a two-dimensional plane. /// -public struct Point : IEquatable +public readonly struct Point : IEquatable { #region Constructors @@ -73,7 +73,9 @@ public struct Point : IEquatable /// Returns the hash code for this . /// /// The hash code for this structure. + #pragma warning disable IDE0070 public override int GetHashCode() + #pragma warning restore IDE0070 { int hashCode = 1861411795; hashCode = hashCode * -1521134295 + X.GetHashCode(); diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index 319c3a6..cfcc717 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -95,11 +95,13 @@ public partial class DlibDotNet hiddenFileNameExtension, hiddenImageCodecInfo, imageCodecInfo, + configuration.LoadPhotoPrismLocations, configuration.ModelDirectory, configuration.ModelName, configuration.OverrideForFaceImages, configuration.PredictorModelName, - configuration.PropertiesChangedForFaces); + configuration.PropertiesChangedForFaces, + configuration.RectangleIntersectMinimums); } { (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetGifLowQuality(); @@ -456,14 +458,14 @@ public partial class DlibDotNet else { List? mappingFromPhotoPrismCollection; - List>? collection; + List>? locationContainers; if (item.Property?.Id is null) - collection = null; + locationContainers = null; else - _ = idToLocationContainers.TryGetValue(item.Property.Id.Value, out collection); + _ = idToLocationContainers.TryGetValue(item.Property.Id.Value, out locationContainers); if (!fileNameToCollection.TryGetValue(mappingFromItem.Id, out mappingFromPhotoPrismCollection)) mappingFromPhotoPrismCollection = null; - faces = _Faces.GetFaces(outputResolution, dResultsFullGroupDirectory, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionToResize, collection, mappingFromPhotoPrismCollection); + faces = _Faces.GetFaces(outputResolution, dResultsFullGroupDirectory, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionToResize, locationContainers, mappingFromPhotoPrismCollection); if (_AppSettings.MaxDegreeOfParallelism < 2) ticks = LogDelta(ticks, nameof(D_Face.GetFaces)); List<(Shared.Models.Face, FileInfo?, string, bool Saved)> faceCollection = _Faces.SaveFaces(_FaceParts.FileNameExtension, dResultsFullGroupDirectory, subFileTuples, parseExceptions, mappingFromItem, facesDirectory, faces); @@ -474,8 +476,8 @@ public partial class DlibDotNet ticks = LogDelta(ticks, nameof(D_Face.SaveFaces)); if ((_Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch) && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution) - && collection is not null && faceCollection.All(l => !l.Saved)) - _Distance.LookForMatchFacesAndPossiblyRename(_Faces.FileNameExtension, mappingFromItem, faces, collection); + && locationContainers is not null && faceCollection.All(l => !l.Saved)) + _Distance.LookForMatchFacesAndPossiblyRename(_Faces.FileNameExtension, mappingFromItem, faces, locationContainers); if (_Configuration.SaveFaceLandmarkForOutputResolutions.Contains(outputResolution)) { bool saveRotated = false; @@ -881,11 +883,11 @@ public partial class DlibDotNet private static void LookForAbandoned(ReadOnlyDictionary>> idToLocationContainers, List distinctFilteredIds) { List renameCollection = new(); - foreach (KeyValuePair>> idToCollection in idToLocationContainers) + foreach (KeyValuePair>> keyValuePair in idToLocationContainers) { - if (distinctFilteredIds.Contains(idToCollection.Key)) + if (distinctFilteredIds.Contains(keyValuePair.Key)) continue; - foreach (LocationContainer locationContainer in idToCollection.Value) + foreach (LocationContainer locationContainer in keyValuePair.Value) { if (locationContainer.File.Contains('!')) continue; diff --git a/Instance/Models/Binder/Configuration.cs b/Instance/Models/Binder/Configuration.cs index 4720091..1b96d50 100644 --- a/Instance/Models/Binder/Configuration.cs +++ b/Instance/Models/Binder/Configuration.cs @@ -34,6 +34,7 @@ public class Configuration [Display(Name = "Julie Phares Copy Birthdays"), Required] public string[] JLinks { get; set; } [Display(Name = "Load Or Create Then Save Distance Results"), Required] public string[] LoadOrCreateThenSaveDistanceResultsForOutputResolutions { get; set; } [Display(Name = "Load Or Create Then Save Image Faces Results"), Required] public string[] LoadOrCreateThenSaveImageFacesResultsForOutputResolutions { get; set; } + [Display(Name = "Load PhotoPrism Locations"), Required] public bool? LoadPhotoPrismLocations { get; set; } [Display(Name = "Location Digits"), Required] public int? LocationDigits { get; set; } [Display(Name = "Location Factor"), Required] public int? LocationFactor { get; set; } [Display(Name = "Look for Abandoned"), Required] public bool? LookForAbandoned { get; set; } @@ -145,6 +146,8 @@ public class Configuration configuration.IgnoreRelativePaths ??= Array.Empty(); configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions ??= Array.Empty(); configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions ??= Array.Empty(); + if (configuration.LoadPhotoPrismLocations is null) + throw new NullReferenceException(nameof(configuration.LoadPhotoPrismLocations)); if (configuration.LocationDigits is null) throw new NullReferenceException(nameof(configuration.LocationDigits)); if (configuration.LocationFactor is null) @@ -261,6 +264,7 @@ public class Configuration configuration.JLinks, configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions, configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions, + configuration.LoadPhotoPrismLocations.Value, configuration.LocationDigits.Value, configuration.LocationFactor.Value, configuration.LookForAbandoned.Value, diff --git a/Instance/Models/Configuration.cs b/Instance/Models/Configuration.cs index 8845d71..a5e69ce 100644 --- a/Instance/Models/Configuration.cs +++ b/Instance/Models/Configuration.cs @@ -33,6 +33,7 @@ public class Configuration public string[] JLinks { init; get; } public string[] LoadOrCreateThenSaveDistanceResultsForOutputResolutions { init; get; } public string[] LoadOrCreateThenSaveImageFacesResultsForOutputResolutions { init; get; } + public bool LoadPhotoPrismLocations { init; get; } public int LocationDigits { init; get; } public int LocationFactor { init; get; } public bool LookForAbandoned { init; get; } @@ -112,6 +113,7 @@ public class Configuration string[] jLinks, string[] loadOrCreateThenSaveDistanceResultsForOutputResolutions, string[] loadOrCreateThenSaveImageFacesResultsForOutputResolutions, + bool loadPhotoPrismLocations, int locationDigits, int locationFactor, bool lookForAbandoned, @@ -190,6 +192,7 @@ public class Configuration JLinks = jLinks; LoadOrCreateThenSaveDistanceResultsForOutputResolutions = loadOrCreateThenSaveDistanceResultsForOutputResolutions; LoadOrCreateThenSaveImageFacesResultsForOutputResolutions = loadOrCreateThenSaveImageFacesResultsForOutputResolutions; + LoadPhotoPrismLocations = loadPhotoPrismLocations; LocationDigits = locationDigits; LocationFactor = locationFactor; LookForAbandoned = lookForAbandoned; diff --git a/Instance/appsettings.json b/Instance/appsettings.json index 0a0ea7b..061eab2 100644 --- a/Instance/appsettings.json +++ b/Instance/appsettings.json @@ -71,6 +71,7 @@ "FocusDirectory": "", "FocusModel": "", "GenealogicalDataCommunicationFile": "", + "LoadPhotoPrismLocations": false, "LocationDigits": 9, "LocationFactor": 10000, "LookForAbandoned": true, diff --git a/Map/Models/Stateless/MapLogic.cs b/Map/Models/Stateless/MapLogic.cs index 65eda08..4550d59 100644 --- a/Map/Models/Stateless/MapLogic.cs +++ b/Map/Models/Stateless/MapLogic.cs @@ -607,7 +607,7 @@ internal abstract class MapLogic return results; } - private static void ParallelFor(Configuration configuration, string eDistanceContentDirectory, List> collection, long personKey, string file) + private static void ParallelFor(Configuration configuration, string eDistanceContentDirectory, List> locationContainers, long personKey, string file) { const string lnk = ".lnk"; int? id, wholePercentages; @@ -623,29 +623,29 @@ internal abstract class MapLogic directories = new List(); else directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(file); - Rectangle? rectangle = ILocation.GetWholePercentages(configuration.LocationDigits, wholePercentages.Value); - lock (collection) - collection.Add(new(fromDistanceContent, file, personKey, id.Value, wholePercentages.Value, directories, rectangle, null)); + RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value); + lock (locationContainers) + locationContainers.Add(new(fromDistanceContent, file, personKey, id.Value, wholePercentages.Value, directories, rectangle, null)); } - private static void OpenPossibleDuplicates(Configuration configuration, List<(long, int, string, double?)> duplicates) + private static void OpenPossibleDuplicates(Configuration configuration, List<(long, int, string, float?)> duplicates) { string personKeyFormatted; - foreach ((long personKey, int id, string file, double? percent) in duplicates) + foreach ((long personKey, int id, string file, float? percent) in duplicates) { if (percent is null) continue; _ = Process.Start("explorer.exe", string.Concat("\"", Path.GetDirectoryName(file), "\"")); personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personKey); } - foreach ((long personKey, int id, string file, double? percent) in duplicates) + foreach ((long personKey, int id, string file, float? percent) in duplicates) { if (percent is not null && percent.Value == 0) continue; _ = Process.Start("explorer.exe", string.Concat("\"", Path.GetDirectoryName(file), "\"")); personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personKey); } - foreach ((long personKey, int id, string file, double? percent) in duplicates) + foreach ((long personKey, int id, string file, float? percent) in duplicates) { if (percent is not null && percent.Value > 0) continue; @@ -654,35 +654,30 @@ internal abstract class MapLogic } } - private static void LookForPossibleDuplicates(Configuration configuration, List> collection) + private static void LookForPossibleDuplicates(Configuration configuration, List> locationContainers) { string key; - double? percent; - Rectangle? rectangle; + float? percent; + float itemPercentagesArea; List delete = new(); - Rectangle intersectRectangle; + RectangleF? itemPercentagesRectangle; (string File, int WholePercentages) item; Dictionary distinct = new(); - List<(long, int, string, double?)> duplicates = new(); - foreach (LocationContainer locationContainer in collection) + List<(long, int, string, float?)> duplicates = new(); + foreach (LocationContainer locationContainer in locationContainers) { key = string.Concat(locationContainer.PersonKey, locationContainer.Id); if (distinct.TryGetValue(key, out item)) { if (item.WholePercentages == locationContainer.WholePercentages) continue; - if (locationContainer.Rectangle is null) + itemPercentagesRectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, item.WholePercentages); + if (itemPercentagesRectangle is null || locationContainer.Rectangle is null) percent = null; else { - rectangle = ILocation.GetWholePercentages(configuration.LocationDigits, item.WholePercentages); - if (locationContainer.Rectangle is null || rectangle is null) - percent = null; - else - { - intersectRectangle = Rectangle.Intersect(locationContainer.Rectangle.Value, rectangle.Value); - percent = intersectRectangle.Width * intersectRectangle.Height; - } + itemPercentagesArea = itemPercentagesRectangle.Value.Width * itemPercentagesRectangle.Value.Height; + percent = ILocation.GetIntersectPercent(itemPercentagesRectangle.Value, itemPercentagesArea, locationContainer.Rectangle.Value); } delete.Add(item.File); delete.Add(locationContainer.File); diff --git a/Move-By-Id/appsettings.json b/Move-By-Id/appsettings.json index e60f7b7..73682af 100644 --- a/Move-By-Id/appsettings.json +++ b/Move-By-Id/appsettings.json @@ -72,6 +72,8 @@ "IgnoreExtensions": [ ".gif", ".GIF", + ".nef", + ".NEF", ".pdf", ".PDF" ], diff --git a/Person/appsettings.json b/Person/appsettings.json index b4f6647..b6fb5d6 100644 --- a/Person/appsettings.json +++ b/Person/appsettings.json @@ -22,6 +22,8 @@ "IgnoreExtensions": [ ".gif", ".GIF", + ".nef", + ".NEF", ".pdf", ".PDF" ], @@ -137,6 +139,8 @@ "IgnoreExtensions": [ ".gif", ".GIF", + ".nef", + ".NEF", ".pdf", ".PDF" ], diff --git a/PhotoPrism/Models/Marker.cs b/PhotoPrism/Models/Marker.cs index 6c026f5..c9d57fa 100644 --- a/PhotoPrism/Models/Marker.cs +++ b/PhotoPrism/Models/Marker.cs @@ -30,7 +30,7 @@ public record Marker( internal static Shared.Models.Marker Map(Marker marker) { Shared.Models.Marker result; - (double x, double y, double w, double h, double score) = (double.Parse(marker.X), double.Parse(marker.Y), double.Parse(marker.W), double.Parse(marker.H), double.Parse(marker.Score)); + (float x, float y, float w, float h, float score) = (float.Parse(marker.X), float.Parse(marker.Y), float.Parse(marker.W), float.Parse(marker.H), float.Parse(marker.Score)); result = new( F_PhotoPrism.HexStringToString(marker.MarkerUid), F_PhotoPrism.HexStringToString(marker.FileUid), diff --git a/PhotoPrism/Models/_F_PhotoPrism.cs b/PhotoPrism/Models/_F_PhotoPrism.cs index 6a6c730..9dd448e 100644 --- a/PhotoPrism/Models/_F_PhotoPrism.cs +++ b/PhotoPrism/Models/_F_PhotoPrism.cs @@ -1,3 +1,4 @@ +using System.Drawing; using System.Text; using System.Text.Json; using View_by_Distance.Shared.Models; @@ -139,14 +140,14 @@ public class F_PhotoPrism return results; } - private static void PopulateSubjects(string mappingDefaultName, string personBirthdayFormat, List subjects, StringBuilder stringBuilder, PersonContainer[] personContainers, (MappingFromPhotoPrism MappingFromPhotoPrism, Shared.Models.Marker Marker, double Percent)[] sortedCollection) + private static void PopulateSubjects(string mappingDefaultName, string personBirthdayFormat, List subjects, StringBuilder stringBuilder, PersonContainer[] personContainers, (MappingFromPhotoPrism MappingFromPhotoPrism, Shared.Models.Marker Marker, float Percent)[] sortedCollection) { long? personKey; string personName; const int zero = 0; string personKeyFormatted; PersonBirthday personBirthday; - foreach ((MappingFromPhotoPrism mappingFromPhotoPrism, Shared.Models.Marker marker, double percent) in sortedCollection) + foreach ((MappingFromPhotoPrism mappingFromPhotoPrism, Shared.Models.Marker marker, float percent) in sortedCollection) { foreach (PersonContainer personContainer in personContainers) { @@ -173,18 +174,22 @@ public class F_PhotoPrism { string file; string text; - double percent; + float dlibArea; + float? percent; + string directory; + int width, height; int? wholePercentages; + RectangleF? prismRectangle; List subjects = new(); + DateTime dateTime = new(ticks); + int dlibLocationWholePercentages; PersonContainer[]? personContainers; StringBuilder stringBuilder = new(); - System.Drawing.Rectangle dlibRectangle; - System.Drawing.Rectangle? prismRectangle; - System.Drawing.Rectangle intersectRectangle; + RectangleF? dlibPercentagesRectangle; float rectangleIntersectMinimum = rectangleIntersectMinimums.Min(); Dictionary? wholePercentagesToPersonContainers; - (MappingFromPhotoPrism MappingFromPhotoPrism, Shared.Models.Marker Marker, double Percent)[] sortedCollection; - List<(MappingFromPhotoPrism MappingFromPhotoPrism, Shared.Models.Marker Marker, double Percent)> collection = new(); + (MappingFromPhotoPrism MappingFromPhotoPrism, Shared.Models.Marker Marker, float Percent)[] sortedCollection; + List<(MappingFromPhotoPrism MappingFromPhotoPrism, Shared.Models.Marker Marker, float Percent)> collection = new(); foreach (Face face in distinctFilteredFaces) { collection.Clear(); @@ -198,21 +203,23 @@ public class F_PhotoPrism (_, wholePercentagesToPersonContainers) = mapLogic.GetWholePercentagesToPersonContainers(face.Mapping.MappingFromItem.Id); if (wholePercentagesToPersonContainers is null || !wholePercentagesToPersonContainers.TryGetValue(wholePercentages.Value, out personContainers)) continue; - dlibRectangle = new(face.Location.Left, face.Location.Top, face.Location.Right - face.Location.Left, face.Location.Bottom - face.Location.Top); + (width, height) = IOutputResolution.Get(face.OutputResolution); + dlibLocationWholePercentages = ILocation.GetWholePercentages(height, face.Location, Shared.Models.Stateless.ILocation.Digits, width); + dlibPercentagesRectangle = ILocation.GetPercentagesRectangle(Shared.Models.Stateless.ILocation.Digits, dlibLocationWholePercentages); + if (dlibPercentagesRectangle is null) + continue; + dlibArea = dlibPercentagesRectangle.Value.Width * dlibPercentagesRectangle.Value.Height; foreach (MappingFromPhotoPrism mappingFromPhotoPrism in face.Mapping.MappingFromPhotoPrismCollection) { foreach (Shared.Models.Marker marker in mappingFromPhotoPrism.Markers) { - prismRectangle = ILocation.GetRectangle(mappingFromPhotoPrism.DatabaseFile, marker, face.OutputResolution); + prismRectangle = ILocation.GetPercentagesRectangle(mappingFromPhotoPrism.DatabaseFile, marker, face.OutputResolution); if (prismRectangle is null) continue; - intersectRectangle = System.Drawing.Rectangle.Intersect(dlibRectangle, prismRectangle.Value); - if (intersectRectangle.Width == 0 || intersectRectangle.Height == 0) + percent = ILocation.GetIntersectPercent(dlibPercentagesRectangle.Value, dlibArea, prismRectangle.Value); + if (percent is null || percent < rectangleIntersectMinimum) continue; - percent = (double)intersectRectangle.Width * intersectRectangle.Height / (dlibRectangle.Width * dlibRectangle.Height); - if (percent < rectangleIntersectMinimum) - continue; - collection.Add(new(mappingFromPhotoPrism, marker, percent)); + collection.Add(new(mappingFromPhotoPrism, marker, percent.Value)); } } if (!collection.Any()) @@ -222,10 +229,13 @@ public class F_PhotoPrism } if (subjects.Any()) { - file = Path.Combine(fPhotoPrismContentDirectory, $"{ticks}-{rectangleIntersectMinimum}-subject_alias_update.sql"); + directory = Path.Combine(fPhotoPrismContentDirectory, dateTime.ToString("yyyy-MM-dd")); + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); + file = Path.Combine(directory, $"{ticks}-{rectangleIntersectMinimum}-subject_alias_update.sql"); text = string.Join(Environment.NewLine, subjects.Distinct()); _ = IPath.WriteAllText(file, text, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); - file = Path.Combine(fPhotoPrismContentDirectory, $"{ticks}-{rectangleIntersectMinimum}-marker_name_update.sql"); + file = Path.Combine(directory, $"{ticks}-{rectangleIntersectMinimum}-marker_name_update.sql"); text = stringBuilder.ToString(); _ = IPath.WriteAllText(file, text, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); } diff --git a/PrepareForOld/appsettings.Development.json b/PrepareForOld/appsettings.Development.json index 9af76fa..5a40cbe 100644 --- a/PrepareForOld/appsettings.Development.json +++ b/PrepareForOld/appsettings.Development.json @@ -63,6 +63,8 @@ "IgnoreExtensions": [ ".gif", ".GIF", + ".nef", + ".NEF", ".pdf", ".PDF" ], diff --git a/Rename/appsettings.json b/Rename/appsettings.json index ecdf9c1..088fcfb 100644 --- a/Rename/appsettings.json +++ b/Rename/appsettings.json @@ -73,6 +73,8 @@ "IgnoreExtensions": [ ".gif", ".GIF", + ".nef", + ".NEF", ".pdf", ".PDF" ], diff --git a/Shared/Models/FacePoint.cs b/Shared/Models/FacePoint.cs index 9bbbf48..5675946 100644 --- a/Shared/Models/FacePoint.cs +++ b/Shared/Models/FacePoint.cs @@ -37,7 +37,9 @@ public class FacePoint : Properties.IFacePoint return result; } + #pragma warning disable IDE0070 public override int GetHashCode() + #pragma warning restore IDE0070 { int hashCode = 1861411795; hashCode = hashCode * -1521134295 + _Point.GetHashCode(); diff --git a/Shared/Models/Location.cs b/Shared/Models/Location.cs index d78d3ef..d5698a4 100644 --- a/Shared/Models/Location.cs +++ b/Shared/Models/Location.cs @@ -74,7 +74,9 @@ public class Location : Properties.ILocation, IEquatable return result; } + #pragma warning disable IDE0070 public override int GetHashCode() + #pragma warning restore IDE0070 { int hashCode = -773114317; hashCode = hashCode * -1521134295 + Bottom.GetHashCode(); diff --git a/Shared/Models/LocationContainer.cs b/Shared/Models/LocationContainer.cs index c0e3c09..c62cb16 100644 --- a/Shared/Models/LocationContainer.cs +++ b/Shared/Models/LocationContainer.cs @@ -2,5 +2,5 @@ using System.Drawing; namespace View_by_Distance.Shared.Models; -public record LocationContainer(bool FromDistanceContent, string File, long PersonKey, int Id, int WholePercentages, IReadOnlyList Directories, Rectangle? Rectangle, Location? Location) +public record LocationContainer(bool FromDistanceContent, string File, long PersonKey, int Id, int WholePercentages, IReadOnlyList Directories, RectangleF? Rectangle, Location? Location) { } \ No newline at end of file diff --git a/Shared/Models/Marker.cs b/Shared/Models/Marker.cs index 4d0af5a..c0353fd 100644 --- a/Shared/Models/Marker.cs +++ b/Shared/Models/Marker.cs @@ -12,13 +12,13 @@ public record Marker( string? SubjSrc, string? FaceId, string FaceDist, - double X, - double Y, - double W, - double H, + float X, + float Y, + float W, + float H, string Q, string Size, - double Score, + float Score, string? Thumb, string MatchedAt, string CreatedAt, diff --git a/Shared/Models/Stateless/Methods/Face.cs b/Shared/Models/Stateless/Methods/Face.cs index 5d0b1db..81a6821 100644 --- a/Shared/Models/Stateless/Methods/Face.cs +++ b/Shared/Models/Stateless/Methods/Face.cs @@ -31,9 +31,7 @@ internal abstract class Face private static JsonElement[] GetJsonElements(string jsonFileFullName) { string json = GetJson(jsonFileFullName); - JsonElement[]? jsonElements = JsonSerializer.Deserialize(json); - if (jsonElements is null) - throw new Exception(); + JsonElement[]? jsonElements = JsonSerializer.Deserialize(json) ?? throw new Exception(); return jsonElements; } diff --git a/Shared/Models/Stateless/Methods/ILocation.cs b/Shared/Models/Stateless/Methods/ILocation.cs index 0a79980..d785784 100644 --- a/Shared/Models/Stateless/Methods/ILocation.cs +++ b/Shared/Models/Stateless/Methods/ILocation.cs @@ -15,25 +15,25 @@ public interface ILocation static List FilterByIntersect(Models.Face[] faces, float rectangleIntersectMinimum, int wholePercentages) => Location.FilterByIntersect(faces, rectangleIntersectMinimum, wholePercentages); - Rectangle? TestStatic_GetRectangle(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) => - GetRectangle(databaseFile, marker, outputResolution); - static Rectangle? GetRectangle(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) => - Location.GetRectangle(databaseFile, marker, outputResolution); + RectangleF? TestStatic_GetPercentagesRectangle(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) => + GetPercentagesRectangle(databaseFile, marker, outputResolution); + static RectangleF? GetPercentagesRectangle(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) => + Location.GetPercentagesRectangle(databaseFile, marker, outputResolution); Models.Location? TestStatic_GetLocation(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) => GetLocation(databaseFile, marker, outputResolution); static Models.Location? GetLocation(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) => Location.GetLocation(databaseFile, marker, outputResolution); - List TestStatic_GetLocations(List mappingFromPhotoPrismCollection, List faces, List> containers) => - GetLocations(mappingFromPhotoPrismCollection, faces, containers); - static List GetLocations(List mappingFromPhotoPrismCollection, List faces, List> containers) => - Location.GetLocations(mappingFromPhotoPrismCollection, faces, containers); + 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); - Rectangle? TestStatic_GetWholePercentages(int locationDigits, int wholePercentages) => - GetWholePercentages(locationDigits, wholePercentages); - static Rectangle? GetWholePercentages(int locationDigits, int wholePercentages) => - Location.GetWholePercentages(locationDigits, wholePercentages.ToString()); + RectangleF? TestStatic_GetPercentagesRectangle(int locationDigits, int wholePercentages) => + GetPercentagesRectangle(locationDigits, wholePercentages); + static RectangleF? GetPercentagesRectangle(int locationDigits, int wholePercentages) => + Location.GetPercentagesRectangle(locationDigits, wholePercentages.ToString()); Rectangle? TestStatic_GetRectangle(int locationDigits, Models.OutputResolution outputResolution, int wholePercentages) => GetRectangle(locationDigits, outputResolution, wholePercentages); @@ -90,6 +90,11 @@ public interface ILocation static int GetWholePercentages(int locationDigits) => Location.GetWholePercentages(1, 1, 0, locationDigits, 1, 0, 1, zCount: 1); + int TestStatic_GetWholePercentages(int height, Models.Location location, int locationDigits, int width) => + GetWholePercentages(height, location, locationDigits, width); + static int GetWholePercentages(int height, Models.Location location, int locationDigits, int width) => + Location.GetWholePercentages(height, location, locationDigits, width); + int TestStatic_GetWholePercentages(int bottom, int height, int left, int locationDigits, int right, int top, int width) => GetWholePercentages(bottom, height, left, locationDigits, right, top, width); static int GetWholePercentages(int bottom, int height, int left, int locationDigits, int right, int top, int width) => @@ -109,4 +114,9 @@ public interface ILocation width, facesCount); + float? TestStatic_GetIntersectPercent(RectangleF rectangleA, float? areaA, RectangleF rectangleB) => + GetIntersectPercent(rectangleA, areaA, rectangleB); + static float? GetIntersectPercent(RectangleF rectangleA, float? areaA, RectangleF rectangleB) => + Location.GetIntersectPercent(rectangleA, areaA, rectangleB); + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IOutputResolution.cs b/Shared/Models/Stateless/Methods/IOutputResolution.cs index 9248612..15aedc3 100644 --- a/Shared/Models/Stateless/Methods/IOutputResolution.cs +++ b/Shared/Models/Stateless/Methods/IOutputResolution.cs @@ -3,6 +3,11 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface IOutputResolution { // ... + (int, int) TestStatic_Get(Models.OutputResolution outputResolution) => + Get(outputResolution); + static (int, int) Get(Models.OutputResolution outputResolution) => + OutputResolution.Get(outputResolution); + int TestStatic_GetHeight(Models.OutputResolution outputResolution) => GetHeight(outputResolution); static int GetHeight(Models.OutputResolution outputResolution) => diff --git a/Shared/Models/Stateless/Methods/Location.cs b/Shared/Models/Stateless/Methods/Location.cs index 828d1d7..e887622 100644 --- a/Shared/Models/Stateless/Methods/Location.cs +++ b/Shared/Models/Stateless/Methods/Location.cs @@ -108,15 +108,21 @@ internal abstract class Location return result; } + internal static int GetWholePercentages(int height, Models.Location location, int locationDigits, int width) + { + int result = GetWholePercentages(location.Bottom, height, location.Left, locationDigits, location.Right, location.Top, width, zCount: 1); + return result; + } + internal static int GetConfidencePercent(int faceConfidencePercent, float[] rangeFaceConfidence, double confidence) { int result = (int)(confidence / rangeFaceConfidence[1] * faceConfidencePercent); return result; } - internal static Rectangle? GetWholePercentages(int locationDigits, string wholePercentages) + internal static RectangleF? GetPercentagesRectangle(int locationDigits, string wholePercentages) { - Rectangle? result; + RectangleF? result; int length = (locationDigits - 1) / 4; string[] segments = new string[] { @@ -133,7 +139,10 @@ internal abstract class Location if (!int.TryParse(segments[1], out int xWholePercent) || !int.TryParse(segments[2], out int yWholePercent) || !int.TryParse(segments[3], out int wWholePercent) || !int.TryParse(segments[4], out int hWholePercent)) result = null; else - result = new(xWholePercent, yWholePercent, wWholePercent, hWholePercent); + { + float factor = 100; + result = new(xWholePercent / factor, yWholePercent / factor, wWholePercent / factor, hWholePercent / factor); + } } return result; } @@ -143,14 +152,13 @@ internal abstract class Location Rectangle? result; if (wholePercentages.Length != locationDigits || wholePercentages[0] is not '4' and not '8') throw new NotSupportedException("Old way has been removed!"); - (int width, int height) = OutputResolution.Get(outputResolution); - Rectangle? rectangle = GetWholePercentages(locationDigits, wholePercentages); + (int width, int height) = OutputResolution.Get(outputResolution); + RectangleF? rectangle = GetPercentagesRectangle(locationDigits, wholePercentages); if (rectangle is null) result = null; else { - decimal factor = 100; - result = new((int)(rectangle.Value.X / factor * width), (int)(rectangle.Value.Y / factor * height), (int)(rectangle.Value.Width / factor * width), (int)(rectangle.Value.Height / factor * height)); + result = new((int)(rectangle.Value.X * width), (int)(rectangle.Value.Y * height), (int)(rectangle.Value.Width * width), (int)(rectangle.Value.Height * height)); } if (result is null) throw new NullReferenceException(nameof(result)); @@ -163,25 +171,29 @@ internal abstract class Location return result; } - internal static Rectangle? GetRectangle(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) + internal static RectangleF? GetPercentagesRectangle(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) { - Rectangle? result; + RectangleF? 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)); + result = new(marker.X, marker.Y, marker.W, marker.H); return result; } - private static Models.Location? GetLocation(DatabaseFile databaseFile, Marker marker, Rectangle rectangle) + private static Models.Location? GetLocation(DatabaseFile databaseFile, Marker marker, RectangleF rectangle) { Models.Location? result; - bool verified = Check(rectangle.Bottom, databaseFile.FileHeight, rectangle.Left, rectangle.Right, rectangle.Top, databaseFile.FileWidth, zCount: 1, throwException: false); + int top = (int)Math.Ceiling(rectangle.Top * databaseFile.FileHeight); + int left = (int)Math.Ceiling(rectangle.Left * databaseFile.FileWidth); + int right = (int)Math.Ceiling(rectangle.Right * databaseFile.FileWidth); + int bottom = (int)Math.Ceiling(rectangle.Bottom * databaseFile.FileHeight); + bool verified = Check(bottom, databaseFile.FileHeight, left, right, top, databaseFile.FileWidth, zCount: 1, throwException: false); if (!verified) result = null; else - result = new(rectangle.Bottom, marker.Score / 100, rectangle.Left, rectangle.Right, rectangle.Top); + result = new(bottom, marker.Score / 100, left, right, top); return result; } @@ -200,7 +212,7 @@ internal abstract class Location internal static Models.Location? GetLocation(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) { Models.Location? result; - Rectangle? rectangle = GetRectangle(databaseFile, marker, outputResolution); + RectangleF? rectangle = GetPercentagesRectangle(databaseFile, marker, outputResolution); if (rectangle is null) result = null; else @@ -208,15 +220,43 @@ internal abstract class Location return result; } - internal static List GetLocations(List mappingFromPhotoPrismCollection, List faces, List> containers) + internal static float? GetIntersectPercent(RectangleF rectangleA, float? areaA, RectangleF rectangleB) + { + float? result; + if (rectangleA.Equals(rectangleB)) + result = 1; + else + { + float intersectArea; + RectangleF intersectRectangle; + areaA ??= rectangleA.Width * rectangleA.Height; + float areaB = rectangleB.Width * rectangleB.Height; + bool check = areaA > areaB; + if (check) + intersectRectangle = RectangleF.Intersect(rectangleB, rectangleA); + else + intersectRectangle = RectangleF.Intersect(rectangleA, rectangleB); + intersectArea = intersectRectangle.Width * intersectRectangle.Height; + if (check) + result = intersectArea / areaA; + else + result = intersectArea / areaB; + } + return result; + } + + internal static List GetLocations(List> locationContainers, List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) { List results = new(); bool any; bool matches; - Rectangle dlibRectangle; - Rectangle? prismRectangle; + float? percent; + float prismArea; + int width, height; Models.Location? location; - Rectangle intersectRectangle; + RectangleF? prismRectangle; + int dlibLocationWholePercentages; + RectangleF? dlibPercentagesRectangle; Models.OutputResolution? outputResolution = null; foreach (Models.Face face in faces) { @@ -226,7 +266,7 @@ internal abstract class Location outputResolution ??= face.OutputResolution; } int before = results.Count; - foreach (LocationContainer locationContainer in containers) + foreach (LocationContainer locationContainer in locationContainers) { if (locationContainer.Location is null) continue; @@ -242,23 +282,24 @@ internal abstract class Location foreach (Marker marker in mappingFromPhotoPrism.Markers) { any = false; - prismRectangle = GetRectangle(mappingFromPhotoPrism.DatabaseFile, marker, outputResolution); + prismRectangle = GetPercentagesRectangle(mappingFromPhotoPrism.DatabaseFile, marker, outputResolution); if (prismRectangle is null) break; + prismArea = prismRectangle.Value.Width * prismRectangle.Value.Height; location = GetLocation(mappingFromPhotoPrism.DatabaseFile, marker, prismRectangle.Value); if (location is null) break; - foreach (LocationContainer locationContainer in containers) + foreach (LocationContainer locationContainer in locationContainers) { if (any) continue; - if (locationContainer.Location is null) + if (locationContainer.Rectangle is null) continue; - dlibRectangle = new(locationContainer.Location.Left, locationContainer.Location.Top, locationContainer.Location.Right - locationContainer.Location.Left, locationContainer.Location.Bottom - locationContainer.Location.Top); - intersectRectangle = Rectangle.Intersect(prismRectangle.Value, dlibRectangle); - if (intersectRectangle.Width == 0 && intersectRectangle.Height == 0) + percent = GetIntersectPercent(prismRectangle.Value, prismArea, locationContainer.Rectangle.Value); + if (percent is null || percent < rectangleIntersectMinimum) continue; - any = true; + if (!any) + any = true; break; } foreach (Models.Face face in faces) @@ -267,11 +308,16 @@ internal abstract class Location continue; if (face.Location is null || face.OutputResolution is null) continue; - dlibRectangle = new(face.Location.Left, face.Location.Top, face.Location.Right - face.Location.Left, face.Location.Bottom - face.Location.Top); - intersectRectangle = Rectangle.Intersect(prismRectangle.Value, dlibRectangle); - if (intersectRectangle.Width == 0 && intersectRectangle.Height == 0) + (width, height) = OutputResolution.Get(face.OutputResolution); + dlibLocationWholePercentages = GetWholePercentages(height, face.Location, Stateless.ILocation.Digits, width); + dlibPercentagesRectangle = GetPercentagesRectangle(Stateless.ILocation.Digits, dlibLocationWholePercentages.ToString()); + if (dlibPercentagesRectangle is null) continue; - any = true; + percent = GetIntersectPercent(prismRectangle.Value, prismArea, dlibPercentagesRectangle.Value); + if (percent is null || percent < rectangleIntersectMinimum) + continue; + if (!any) + any = true; break; } if (!any) @@ -283,31 +329,28 @@ internal abstract class Location return results; } - // private static double GP(OutputResolution outputResolution,Location location, ){ - // double result; - // return result; - // } - internal static List FilterByIntersect(Models.Face[] faces, float rectangleIntersectMinimum, int wholePercentages) { List results = new(); - double? percent; - Rectangle checkRectangle; - Rectangle? sourceRectangle; - Rectangle intersectRectangle; + float? percent; + int width, height; + int faceLocationWholePercentages; + RectangleF? facePercentagesRectangle; + RectangleF? sourceRectangle = GetPercentagesRectangle(Stateless.ILocation.Digits, wholePercentages.ToString()); + float? sourceArea = sourceRectangle is null ? null : sourceRectangle.Value.Width * sourceRectangle.Value.Height; foreach (Models.Face face in faces) { + if (sourceRectangle is null || sourceArea is null) + continue; if (face.Location is null || face.OutputResolution is null) continue; - checkRectangle = new(face.Location.Left, face.Location.Top, face.Location.Right - face.Location.Left, face.Location.Bottom - face.Location.Top); - sourceRectangle = GetRectangle(Stateless.ILocation.Digits, face.OutputResolution, wholePercentages.ToString()); - if (sourceRectangle is null) + (width, height) = OutputResolution.Get(face.OutputResolution); + faceLocationWholePercentages = GetWholePercentages(height, face.Location, Stateless.ILocation.Digits, width); + facePercentagesRectangle = GetPercentagesRectangle(Stateless.ILocation.Digits, faceLocationWholePercentages.ToString()); + if (facePercentagesRectangle is null) continue; - intersectRectangle = Rectangle.Intersect(checkRectangle, sourceRectangle.Value); - if (intersectRectangle.Width == 0 || intersectRectangle.Height == 0) - continue; - percent = (double)intersectRectangle.Width * intersectRectangle.Height / (checkRectangle.Width * checkRectangle.Height); - if (percent < rectangleIntersectMinimum) + percent = GetIntersectPercent(sourceRectangle.Value, sourceArea.Value, facePercentagesRectangle.Value); + if (percent is null || percent < rectangleIntersectMinimum) continue; results.Add(face); } diff --git a/Tests/UnitTestCalculations.cs b/Tests/UnitTestCalculations.cs index 69c6380..fefbf7d 100644 --- a/Tests/UnitTestCalculations.cs +++ b/Tests/UnitTestCalculations.cs @@ -4,6 +4,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Phares.Shared; using Serilog; using System.Diagnostics; +using System.Drawing; using System.Reflection; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless.Methods; @@ -307,4 +308,74 @@ public partial class UnitTestCalculations NonThrowTryCatch(); } + [TestMethod] + public void TestMethodIntersect() + { + float? percent; + float? areaA = null; + RectangleF rectangleA; + RectangleF rectangleB; + rectangleA = new(0, 0, 4, 4); + rectangleB = new(0, 0, 4, 4); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == 1); + rectangleA = new(0, 0, 4, 4); + rectangleB = new(0, 0, 2, 2); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == .25); + rectangleA = new(0, 0, 4, 4); + rectangleB = new(0, 0, 4, 2); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == .5); + rectangleA = new(0, 0, 4, 4); + rectangleB = new(2, 2, 4, 4); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == .25); + rectangleA = new(0, 0, 4, 4); + rectangleB = new(2, 0, 4, 4); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == .5); + rectangleA = new(0, 0, 4, 4); + rectangleB = new(2, 4, 4, 4); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == 0); + rectangleB = new(0, 0, 4, 4); + rectangleA = new(0, 0, 4, 4); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == 1); + rectangleB = new(0, 0, 4, 4); + rectangleA = new(0, 0, 2, 2); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == .25); + rectangleB = new(0, 0, 4, 4); + rectangleA = new(0, 0, 4, 2); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == .5); + rectangleB = new(0, 0, 4, 4); + rectangleA = new(2, 2, 4, 4); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == .25); + rectangleB = new(0, 0, 4, 4); + rectangleA = new(2, 0, 4, 4); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == .5); + rectangleB = new(0, 0, 4, 4); + rectangleA = new(2, 4, 4, 4); + percent = ILocation.GetIntersectPercent(rectangleA, areaA, rectangleB); + Assert.IsNotNull(percent); + Assert.IsTrue(percent.Value == 0); + NonThrowTryCatch(); + } + } \ No newline at end of file