MapFaceFileLogic

Author:
This commit is contained in:
Mike Phares 2024-05-26 23:37:26 -07:00
parent 7f8b09e66c
commit dca487deb3
20 changed files with 906 additions and 103 deletions

View File

@ -1,10 +1,13 @@
using ShellProgressBar;
using System.Collections.ObjectModel;
using System.Data;
using System.Text.Json;
using View_by_Distance.FaceRecognitionDotNet;
using View_by_Distance.Map.Models;
using View_by_Distance.Property.Models.Stateless;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Methods;
using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Distance.Models;
@ -136,11 +139,11 @@ public partial class E_Distance : IDistance
return results;
}
private static List<(Face, double?)> GetMatchingFacesByFaceEncoding(Face[] filteredFaces, string? json)
private static List<(Face, double?)> GetMatchingFacesByFaceEncoding(List<Face> faces, string? json)
{
List<(Face, double?)> results = [];
string check;
foreach (Face face in filteredFaces)
foreach (Face face in faces)
{
if (json is null || face.FaceEncoding is null)
continue;
@ -169,7 +172,7 @@ public partial class E_Distance : IDistance
mappedFaceDirectory = Path.GetDirectoryName(file);
if (mappedFaceDirectory is null)
throw new NotSupportedException();
deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
checkFile = Path.Combine(mappedFaceDirectory, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{facesFileNameExtension}");
if (checkFile == file)
continue;
@ -205,7 +208,7 @@ public partial class E_Distance : IDistance
}
}
public void LookForMatchFacesAndPossiblyRename(IDistanceLimits distanceLimits, Shared.Models.Stateless.Methods.IFaceD dFace, FilePath filePath, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Face> faces, ReadOnlyCollection<LocationContainer> locationContainers)
public void LookForMatchFacesAndPossiblyRename(bool overrideForFaceImages, IDistanceLimits distanceLimits, IFaceD dFace, FilePath filePath, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Face> faces, ReadOnlyCollection<LocationContainer> locationContainers)
{
string? json;
string[] matches;
@ -213,9 +216,6 @@ public partial class E_Distance : IDistance
List<Face> intersectFaces;
Shared.Models.FaceEncoding? modelsFaceEncoding;
List<(Face Face, double? Distance)> checkFaces = [];
Face[] filteredFaces = (from l in faces where l.FaceEncoding is not null && l.Location is not null && l.OutputResolution is not null select l).ToArray();
if (filteredFaces.Length != faces.Count)
checkFaces.Clear();
foreach (LocationContainer locationContainer in locationContainers)
{
if (_Renamed.Contains(locationContainer.FilePath.FullName))
@ -236,8 +236,8 @@ public partial class E_Distance : IDistance
MoveUnableToMatch(locationContainer.FilePath);
continue;
}
if (filteredFaces.Length > 0)
checkFaces.AddRange(GetMatchingFacesByFaceEncoding(filteredFaces, json));
if (faces.Count > 0)
checkFaces.AddRange(GetMatchingFacesByFaceEncoding(faces, json));
if (checkFaces.Count == 1)
_Debug.Add(0);
if (checkFaces.Count != 1 && !string.IsNullOrEmpty(json))
@ -246,9 +246,9 @@ public partial class E_Distance : IDistance
modelsFaceEncoding = JsonSerializer.Deserialize<Shared.Models.FaceEncoding>(json);
if (modelsFaceEncoding is null)
throw new NotSupportedException();
if (filteredFaces.Length > 0)
if (faces.Count > 0)
{
intersectFaces = Shared.Models.Stateless.Methods.ILocation.FilterByIntersect(filteredFaces, _RectangleIntersectMinimum, locationContainer.WholePercentages);
intersectFaces = Shared.Models.Stateless.Methods.ILocation.FilterByIntersect(faces, _RectangleIntersectMinimum, locationContainer.WholePercentages);
if (intersectFaces.Count > 0)
checkFaces.AddRange(GetClosestFaceByDistanceIgnoringTolerance(distanceLimits, mappingFromItem, intersectFaces, modelsFaceEncoding));
}
@ -279,11 +279,14 @@ public partial class E_Distance : IDistance
}
continue;
}
if (checkFaces.Count == 1)
if (overrideForFaceImages)
{
json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(locationContainer.ExifDirectory);
if (json is null || json.Contains(nameof(DateTime)))
dFace.ReSaveFace(exifDirectory, locationContainer, checkFaces[0].Face);
if (json is null || !json.Contains(nameof(DateTime)))
{
if (checkFaces.Count == 1)
dFace.ReSaveFace(exifDirectory, locationContainer.FilePath, checkFaces[0].Face, mappedFile: true);
}
}
if (_AllMappedFaceFileNames.Contains(locationContainer.FilePath.Name))
{
@ -332,7 +335,85 @@ public partial class E_Distance : IDistance
File.WriteAllLines(eDistanceContentFileName, results);
}
public static void PreFilterSetFaceDistances(int maxDegreeOfParallelism, Map.Models.Configuration configuration, long ticks, ReadOnlyCollection<Face> distinctValidImageFaces)
public static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMappedWithEncoding(ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mapped)
{
Dictionary<int, ReadOnlyDictionary<int, LocationContainer>> results = [];
string? json;
LocationContainer? locationContainer;
Shared.Models.FaceEncoding? faceEncoding;
FaceRecognitionDotNet.FaceEncoding? encoding;
Dictionary<int, LocationContainer> keyValuePairs;
foreach (KeyValuePair<int, ReadOnlyDictionary<int, LocationContainer>> keyValuePair in mapped)
{
keyValuePairs = [];
foreach (KeyValuePair<int, LocationContainer> keyValue in keyValuePair.Value)
{
json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(keyValue.Value.ExifDirectory);
faceEncoding = json is null ? null : JsonSerializer.Deserialize<Shared.Models.FaceEncoding>(json);
if (faceEncoding is null)
continue;
encoding = FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding);
locationContainer = LocationContainer.Get(keyValue.Value, encoding, keepExifDirectory: false);
keyValuePairs.Add(keyValue.Key, locationContainer);
}
results.Add(keyValuePair.Key, new(keyValuePairs));
}
return new(results);
}
public static List<LocationContainer> GetPreFilterLocationContainer(int maxDegreeOfParallelism, Configuration configuration, string focusDirectory, string focusModel, int? skipPersonWithMoreThen, long ticks, MapLogic mapLogic, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mapped, List<LocationContainer> available)
{
List<LocationContainer> results = [];
string? json;
string? model;
bool? canReMap;
bool? isFocusPerson;
bool? inSkipCollection;
Shared.Models.FaceEncoding? faceEncoding;
FaceRecognitionDotNet.FaceEncoding? encoding;
ReadOnlyDictionary<int, LocationContainer>? keyValuePairs;
ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers;
foreach (LocationContainer locationContainer in available)
{
if (mapped.TryGetValue(locationContainer.Id, out keyValuePairs))
{
if (keyValuePairs.ContainsKey(locationContainer.WholePercentages))
continue;
}
if (locationContainer.ExifDirectory is null || locationContainer.FaceFile is null)
continue;
inSkipCollection = mapLogic.InSkipCollection(locationContainer.Id, locationContainer.WholePercentages);
if (inSkipCollection is not null && inSkipCollection.Value)
continue;
wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(locationContainer.Id);
canReMap = Map.Models.Stateless.Methods.IMapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, locationContainer.WholePercentages);
if (canReMap is not null && !canReMap.Value)
continue;
isFocusPerson = mapLogic.IsFocusPerson(skipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, locationContainer.WholePercentages);
if (isFocusPerson is not null && !isFocusPerson.Value)
continue;
if (!string.IsNullOrEmpty(focusModel))
{
model = Metadata.Models.Stateless.Methods.IMetadata.GetModel(locationContainer.ExifDirectory);
if (string.IsNullOrEmpty(model) || !model.Contains(focusModel))
continue;
}
if (!string.IsNullOrEmpty(focusDirectory))
{
if (!locationContainer.FilePath.DirectoryName.Contains(focusDirectory))
continue;
}
json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.ExifDirectory);
faceEncoding = json is null ? null : JsonSerializer.Deserialize<Shared.Models.FaceEncoding>(json);
if (faceEncoding is null)
continue;
encoding = FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding);
results.Add(LocationContainer.Get(locationContainer, encoding, keepExifDirectory: false));
}
return results;
}
public static void PreFilterSetFaceDistances(int maxDegreeOfParallelism, Configuration configuration, long ticks, ReadOnlyCollection<Face> distinctValidImageFaces)
{
List<Face> faces = [];
foreach (Face face in distinctValidImageFaces)
@ -372,12 +453,12 @@ public partial class E_Distance : IDistance
});
}
private static List<SortingContainer> GetSortingContainers(Map.Models.Configuration mapConfiguration, IDistanceLimits distanceLimits, Face face, FaceDistance faceDistanceEncoding, List<Sorting> sortingCollection)
private static List<SortingContainer> GetSortingContainers(Configuration mapConfiguration, IDistanceLimits distanceLimits, Face face, FaceDistance faceDistanceEncoding, List<Sorting> sortingCollection)
{
List<SortingContainer> results = [];
int days = 0, distance = 0;
SortingContainer sortingContainer;
Sorting[] collection = Shared.Models.Stateless.Methods.ISorting.Sort(sortingCollection);
Sorting[] collection = ISorting.Sort(sortingCollection);
foreach (Sorting sorting in collection)
{
if (face.Mapping?.MappingFromLocation is null || faceDistanceEncoding.WholePercentages is null)
@ -403,11 +484,11 @@ public partial class E_Distance : IDistance
return results;
}
private static List<Sorting> GetSortingCollection(Map.Models.MapLogic mapLogic, IDistanceLimits distanceLimits, ReadOnlyCollection<FaceDistance> faceDistanceEncodings, int i, Face face, FaceDistance faceDistanceEncoding)
private static List<Sorting> GetSortingCollection(MapLogic mapLogic, ReadOnlyCollection<FaceDistance> faceDistanceEncodings, int i, Face face, FaceDistance faceDistanceEncoding)
{
List<Sorting> results;
List<FaceDistance> faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding);
results = mapLogic.GetSortingCollection(distanceLimits, i, face, faceDistanceEncoding, faceDistanceLengths);
results = mapLogic.GetSortingCollection(i, face, faceDistanceEncoding, faceDistanceLengths);
return results;
}
@ -433,7 +514,23 @@ public partial class E_Distance : IDistance
return results;
}
public static FaceDistanceContainer[] FilteredPostLoadFaceDistanceContainers(Map.Models.MapLogic mapLogic, ReadOnlyCollection<FaceDistanceContainer> faceDistanceContainers, long? skipOlderThan, DistanceLimits distanceLimits)
public static List<LocationContainer> GetPostFilterLocationContainer(MapLogic mapLogic, List<LocationContainer> preFiltered, DistanceLimits distanceLimits)
{
List<LocationContainer> results = [];
foreach (LocationContainer locationContainer in preFiltered)
{
if (locationContainer.FaceFile is null)
continue;
if (locationContainer.FaceFile.AreaPermyriad < distanceLimits.FaceAreaPermyriad)
continue;
if (locationContainer.FaceFile.ConfidencePercent < distanceLimits.FaceConfidencePercent)
continue;
results.Add(locationContainer);
}
return results;
}
public static FaceDistanceContainer[] FilteredPostLoadFaceDistanceContainers(MapLogic mapLogic, ReadOnlyCollection<FaceDistanceContainer> faceDistanceContainers, long? skipOlderThan, DistanceLimits distanceLimits)
{
List<FaceDistanceContainer> results = [];
foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers)
@ -462,7 +559,46 @@ public partial class E_Distance : IDistance
return results.ToArray();
}
public static ReadOnlyCollection<SortingContainer> SetFaceMappingSortingCollectionThenGetSortedSortingContainers(int maxDegreeOfParallelism, Map.Models.Configuration mapConfiguration, long ticks, Map.Models.MapLogic mapLogic, IDistanceLimits distanceLimits, ReadOnlyCollection<FaceDistance> faceDistanceEncodings, FaceDistanceContainer[] filteredFaceDistanceContainers)
private static ReadOnlyCollection<LocationContainer> GetReadOnlyLocationContainer(ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedWithEncoding, List<LocationContainer> postFiltered)
{
List<LocationContainer> results = [];
foreach (LocationContainer locationContainer in postFiltered)
results.Add(locationContainer);
foreach (KeyValuePair<int, ReadOnlyDictionary<int, LocationContainer>> keyValuePair in mappedWithEncoding)
{
foreach (KeyValuePair<int, LocationContainer> keyValue in keyValuePair.Value)
results.Add(keyValue.Value);
}
return new(results);
}
public static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(IDlibDotNet dlibDotNet, Configuration mapConfiguration, long ticks, MapLogic mapLogic, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedWithEncoding, List<LocationContainer> preFiltered, DistanceLimits distanceLimits, List<LocationContainer> postFiltered)
{
List<LocationContainer> results = [];
ReadOnlyCollection<LocationContainer> locationContainers;
ReadOnlyCollection<LocationContainer> readOnlyLocationContainers = GetReadOnlyLocationContainer(mappedWithEncoding, postFiltered);
foreach (LocationContainer locationContainer in postFiltered)
{
dlibDotNet.Tick();
locationContainers = FaceRecognition.GetLocationContainers(mapConfiguration.FaceDistancePermyriad, readOnlyLocationContainers, locationContainer);
foreach (LocationContainer item in locationContainers)
{
if (item.LengthPermyriad is null)
continue;
if (item.LengthPermyriad > distanceLimits.FaceDistancePermyriad)
break;
if (!mapConfiguration.SaveSortingWithoutPerson && item.PersonKey is null)
continue;
if (item.Id == locationContainer.Id && item.WholePercentages == locationContainer.WholePercentages)
continue;
results.Add(item);
}
}
LocationContainer[] array = results.OrderBy(l => l.LengthPermyriad).ToArray();
return new(array);
}
public static ReadOnlyCollection<SortingContainer> SetFaceMappingSortingCollectionThenGetSortedSortingContainers(int maxDegreeOfParallelism, Configuration mapConfiguration, long ticks, MapLogic mapLogic, IDistanceLimits distanceLimits, ReadOnlyCollection<FaceDistance> faceDistanceEncodings, FaceDistanceContainer[] filteredFaceDistanceContainers)
{
List<SortingContainer> results = [];
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
@ -475,7 +611,7 @@ public partial class E_Distance : IDistance
progressBar.Tick();
Face face = filteredFaceDistanceContainers[i].Face;
FaceDistance faceDistanceEncoding = filteredFaceDistanceContainers[i].FaceDistance;
List<Sorting> sortingCollection = GetSortingCollection(mapLogic, distanceLimits, faceDistanceEncodings, i, face, faceDistanceEncoding);
List<Sorting> sortingCollection = GetSortingCollection(mapLogic, faceDistanceEncodings, i, face, faceDistanceEncoding);
if (sortingCollection.Count == 0)
return;
List<SortingContainer> sortingContainers = GetSortingContainers(mapConfiguration, distanceLimits, face, faceDistanceEncoding, sortingCollection);
@ -486,9 +622,9 @@ public partial class E_Distance : IDistance
}
});
if (distanceLimits is not null && distanceLimits.RangeDaysDeltaTargetLessThenUpper)
results = Shared.Models.Stateless.Methods.ISortingContainer.Sort(results);
results = ISortingContainer.Sort(results);
else
results = Shared.Models.Stateless.Methods.ISortingContainer.SortUsingDaysDelta(results);
results = ISortingContainer.SortUsingDaysDelta(results);
return new(results);
}

View File

@ -6,6 +6,7 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using View_by_Distance.FaceRecognitionDotNet;
using View_by_Distance.Metadata.Models;
using View_by_Distance.Metadata.Models.Stateless.Methods;
using View_by_Distance.Property.Models;
using View_by_Distance.Resize.Models;
using View_by_Distance.Shared.Models;
@ -149,7 +150,7 @@ public class D_Face : IFaceD
#pragma warning disable CA1416
private void SaveFaces(FileHolder resizedFileHolder, MetadataExtractor.GeoLocation? geoLocation, string? maker, string? model, List<(Shared.Models.Face, FileInfo?, string, bool)> collection)
private void SaveFaces(FileHolder resizedFileHolder, ExifDirectory exifDirectory, List<(Shared.Models.Face, FileHolder?, string, bool)> collection)
{
int width;
int height;
@ -162,17 +163,27 @@ public class D_Face : IFaceD
string faceFileJson;
string faceEncodingJson;
PropertyItem? propertyItem;
string? maker = IMetadata.GetMaker(exifDirectory);
string? model = IMetadata.GetModel(exifDirectory);
using Bitmap source = new(resizedFileHolder.FullName);
int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist;
int userComment = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagUserComment;
foreach ((Shared.Models.Face face, FileInfo? fileInfo, string fileName, bool save) in collection)
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315
const int userComment = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagUserComment;
foreach ((Shared.Models.Face face, FileHolder? fileHolder, string fileName, bool save) in collection)
{
if (!save)
continue;
if (fileInfo is null)
if (fileHolder is null)
continue;
if (face.FaceEncoding is null || face?.Location is null || face?.OutputResolution is null)
continue;
if (_OverrideForFaceImages && fileHolder.Exists)
{
IFaceD dFace = this;
FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null);
dFace.ReSaveFace(exifDirectory, filePath, face, mappedFile: false);
continue;
}
location = Shared.Models.Stateless.Methods.ILocation.GetLocation(face.Location, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, source.Height, source.Width, collection.Count);
if (location is null)
continue;
@ -180,7 +191,15 @@ public class D_Face : IFaceD
height = location.Bottom - location.Top;
faceEncodingJson = JsonSerializer.Serialize(face.FaceEncoding);
rectangle = new Rectangle(location.Left, location.Top, width, height);
faceFile = new(face.DateTime, geoLocation?.ToDmsString(), face.FaceParts, face.Location, maker, model, face.OutputResolution);
faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
face.Mapping?.MappingFromLocation?.ConfidencePercent,
face.DateTime,
geoLocation?.ToDmsString(),
face.FaceParts,
face.Location,
maker,
model,
face.OutputResolution);
faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
using (bitmap = new(width, height))
{
@ -190,7 +209,7 @@ public class D_Face : IFaceD
bitmap.SetPropertyItem(propertyItem);
propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(_ConstructorInfo, userComment, type, faceEncodingJson);
bitmap.SetPropertyItem(propertyItem);
bitmap.Save(fileInfo.FullName, _ImageCodecInfo, _EncoderParameters);
bitmap.Save(fileHolder.FullName, _ImageCodecInfo, _EncoderParameters);
}
if (File.Exists(fileName))
File.Delete(fileName);
@ -330,11 +349,12 @@ public class D_Face : IFaceD
return results;
}
public List<(Shared.Models.Face, FileInfo?, string, bool)> SaveFaces(string f, string dResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces)
public List<(Shared.Models.Face, FileHolder?, string, bool)> SaveFaces(string f, string dResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces)
{
List<(Shared.Models.Face, FileInfo?, string, bool Save)> results = [];
List<(Shared.Models.Face, FileHolder?, string, bool Save)> results = [];
bool save;
FileInfo fileInfo;
FileHolder fileHolder;
string deterministicHashCodeKey;
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)];
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
@ -351,42 +371,78 @@ public class D_Face : IFaceD
}
deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
fileInfo = new FileInfo(Path.Combine(directory, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_FileNameExtension}"));
fileHolder = FileHolder.Get(fileInfo);
if (!directoryExists)
save = true;
else if (_OverrideForFaceImages)
save = true;
else if (!fileInfo.Exists)
else if (!fileHolder.Exists)
save = true;
else if (_CheckDFaceAndUpWriteDates && dateTimes.Count > 0 && dateTimes.Max() > fileInfo.LastWriteTime)
save = true;
results.Add(new(face, fileInfo, Path.Combine(directory, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_HiddenFileNameExtension}"), save));
results.Add(new(face, fileHolder, Path.Combine(directory, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_HiddenFileNameExtension}"), save));
}
if (results.Any(l => l.Save))
{
if (!directoryExists)
_ = Directory.CreateDirectory(directory);
string? maker = Metadata.Models.Stateless.Methods.IMetadata.GetMaker(exifDirectory);
string? model = Metadata.Models.Stateless.Methods.IMetadata.GetModel(exifDirectory);
MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(exifDirectory);
SaveFaces(mappingFromItem.ResizedFileHolder, geoLocation, maker, model, results);
SaveFaces(mappingFromItem.ResizedFileHolder, exifDirectory, results);
}
return results;
}
#pragma warning disable CA1416
void IFaceD.ReSaveFace(ExifDirectory exifDirectory, LocationContainer locationContainer, Shared.Models.Face face)
private static (string?, string?) Get(string? json)
{
FileInfo fileInfo = new(locationContainer.FilePath.FullName);
string? model;
string? maker;
FaceFile? faceFile = json is null ? null : JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile);
if (faceFile is null || faceFile.Location is null)
(maker, model) = (null, null);
else
(maker, model) = (faceFile.Maker, faceFile.Model);
return (maker, model);
}
void IFaceD.ReSaveFace(ExifDirectory exifDirectory, FilePath filePath, Shared.Models.Face face, bool mappedFile)
{
FileInfo fileInfo = new(filePath.FullName);
if (fileInfo.Exists)
{
string? json;
short type = 2;
string checkFile = $"{locationContainer.FilePath.FullName}.exif";
int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist;
string? maker = Metadata.Models.Stateless.Methods.IMetadata.GetMaker(exifDirectory);
string? model = Metadata.Models.Stateless.Methods.IMetadata.GetModel(exifDirectory);
MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(exifDirectory);
FaceFile faceFile = new(face.DateTime, geoLocation?.ToDmsString(), face.FaceParts, face.Location, maker, model, face.OutputResolution);
string? model;
string? maker;
string checkFile = $"{filePath.FullName}.exif";
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist;
// const int author = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagWinAuthor; // 40093
if (mappedFile)
{
json = IMetadata.GetOutputResolution(exifDirectory);
if (json is not null && json.Contains(nameof(DateTime)))
return;
(maker, model) = Get(json);
}
else
{
maker = IMetadata.GetMaker(exifDirectory);
model = IMetadata.GetModel(exifDirectory);
ExifDirectory? faceExifDirectory = IMetadata.GetExifDirectory(filePath);
json = IMetadata.GetOutputResolution(faceExifDirectory);
if (json is not null && json.Contains(nameof(DateTime)))
return;
}
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
FaceFile faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
face.Mapping?.MappingFromLocation?.ConfidencePercent,
face.DateTime,
geoLocation?.ToDmsString(),
face.FaceParts,
face.Location,
maker,
model,
face.OutputResolution);
string faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception();
PropertyItem? propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(constructorInfo, artist, type, faceFileJson);

View File

@ -302,12 +302,12 @@ public class D2_FaceParts
#pragma warning disable CA1416
private void SaveFaceLandmarkImage(MappingFromItem mappingFromItem, List<(Shared.Models.Face, FileInfo?, string, bool)> faceCollection, string fileName)
private void SaveFaceLandmarkImage(MappingFromItem mappingFromItem, List<(Shared.Models.Face, FileHolder?, string, bool)> faceCollection, string fileName)
{
Pen pen;
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
using Graphics graphic = Graphics.FromImage(image);
foreach ((Shared.Models.Face face, FileInfo? fileInfo, string _, bool _) in faceCollection)
foreach ((Shared.Models.Face face, FileHolder? _, string _, bool _) in faceCollection)
{
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null || face.FaceParts is null || face.FaceParts.Count == 0)
continue;
@ -327,17 +327,17 @@ public class D2_FaceParts
#pragma warning restore CA1416
private static bool GetNotMapped(string facePartsCollectionDirectory, List<(Shared.Models.Face Face, FileInfo?, string, bool)> faceCollection)
private static bool GetNotMapped(string facePartsCollectionDirectory, List<(Shared.Models.Face Face, FileHolder?, string, bool)> faceCollection)
{
bool results = false;
string checkFile;
foreach ((Shared.Models.Face face, FileInfo? fileInfo, string _, bool _) in faceCollection)
foreach ((Shared.Models.Face face, FileHolder? fileHolder, string _, bool _) in faceCollection)
{
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
continue;
if (fileInfo is not null)
if (fileHolder is not null)
{
checkFile = Path.Combine(facePartsCollectionDirectory, fileInfo.Name);
checkFile = Path.Combine(facePartsCollectionDirectory, fileHolder.Name);
if (face.Mapping?.MappingFromPerson is not null)
{
if (File.Exists(checkFile))
@ -353,14 +353,14 @@ public class D2_FaceParts
if (!results)
results = true;
if (!File.Exists(checkFile))
File.Copy(fileInfo.FullName, checkFile);
File.Copy(fileHolder.FullName, checkFile);
}
}
}
return results;
}
public void CopyFacesAndSaveFaceLandmarkImage(string facePartsCollectionDirectory, MappingFromItem mappingFromItem, List<(Shared.Models.Face Face, FileInfo?, string, bool)> faceCollection)
public void CopyFacesAndSaveFaceLandmarkImage(string facePartsCollectionDirectory, MappingFromItem mappingFromItem, List<(Shared.Models.Face Face, FileHolder?, string, bool)> faceCollection)
{
bool hasNotMapped = GetNotMapped(facePartsCollectionDirectory, faceCollection);
string fileName = Path.Combine(facePartsCollectionDirectory, $"{mappingFromItem.FilePath.Name}{_FileNameExtension}");

View File

@ -412,16 +412,42 @@ public class FaceRecognition : DisposableObject
return null;
}
public static ReadOnlyCollection<LocationContainer> GetLocationContainers(int permyriad, ReadOnlyCollection<LocationContainer> readOnlyLocationContainers, LocationContainer locationContainer)
{
List<LocationContainer> results = [];
int lengthPermyriad;
if (readOnlyLocationContainers.Count != 0)
{
double length;
LocationContainer result;
if (locationContainer.Encoding is not FaceEncoding faceEncodingToCompare)
throw new NullReferenceException(nameof(locationContainer));
faceEncodingToCompare.ThrowIfDisposed();
foreach (LocationContainer item in readOnlyLocationContainers)
{
#pragma warning disable CA1513
if (item.Encoding is not FaceEncoding faceEncoding || faceEncoding.IsDisposed)
throw new ObjectDisposedException($"{nameof(item)} contains disposed object.");
#pragma warning restore CA1513
using (Matrix<double> diff = faceEncoding.Encoding - faceEncodingToCompare.Encoding)
length = DlibDotNet.Dlib.Length(diff);
lengthPermyriad = (int)(length * permyriad);
result = LocationContainer.Get(locationContainer, item, lengthPermyriad, keepExifDirectory: false, keepEncoding: false);
results.Add(result);
}
}
LocationContainer[] array = results.OrderBy(l => l.LengthPermyriad).ToArray();
return new(array);
}
public static List<FaceDistance> FaceDistances(ReadOnlyCollection<FaceDistance> faceDistances, FaceDistance faceDistanceToCompare)
{
List<FaceDistance> results = [];
if (faceDistances is null)
throw new NullReferenceException(nameof(faceDistances));
if (faceDistances.Count != 0)
{
double length;
FaceDistance result;
if (faceDistanceToCompare is null || faceDistanceToCompare.Encoding is not FaceEncoding faceEncodingToCompare)
if (faceDistanceToCompare.Encoding is not FaceEncoding faceEncodingToCompare)
throw new NullReferenceException(nameof(faceDistanceToCompare));
faceEncodingToCompare.ThrowIfDisposed();
foreach (FaceDistance faceDistance in faceDistances)

View File

@ -286,6 +286,34 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
}
}
private void MapFaceFileLogic(long ticks, ReadOnlyCollection<PersonContainer> personContainers, MapLogic mapLogic, string? a2PeopleContentDirectory, string eDistanceContentDirectory, ProgressBarOptions options)
{
foreach (string outputResolution in _Configuration.OutputResolutions)
{
if (_Exceptions.Count != 0)
break;
if (!_Configuration.SaveResizedSubfiles)
continue;
if (!_ArgZeroIsConfigurationRootDirectory)
continue;
if (outputResolution != _Configuration.OutputResolutions[0])
continue;
if (_PropertyRootExistedBefore || a2PeopleContentDirectory is null)
break;
if (!_Configuration.SaveFaceDistancesForOutputResolutions.Contains(outputResolution))
continue;
if (!_Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution))
continue;
List<SaveContainer> saveContainers = GetSaveContainers(ticks, personContainers, a2PeopleContentDirectory, eDistanceContentDirectory, options, mapLogic, outputResolution);
if (saveContainers.Count > 0)
{
int updated = 0;
mapLogic.SaveContainers(updated, saveContainers);
throw new NotSupportedException("Done Restart! :)");
}
}
}
private void Search(long ticks, ReadOnlyCollection<PersonContainer> personContainers, string argZero, string propertyRoot)
{
int t;
@ -381,6 +409,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
SaveDistinctIds(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers);
mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory);
DeleteContinueFiles(personContainers);
if (!runToDoCollectionFirst)
MapFaceFileLogic(ticks, personContainers, mapLogic, a2PeopleContentDirectory, eDistanceContentDirectory, options);
FullDoWork(argZero, propertyRoot, ticks, aResultsFullGroupDirectory, bResultsFullGroupDirectory, fPhotoPrismSingletonDirectory, t, readOnlyContainers, propertyLogic, mapLogic);
ReadOnlyCollection<Item> distinctValidImageItems = Shared.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true);
if (_Configuration.LookForAbandoned)
@ -389,7 +419,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
string d2ResultsFullGroupDirectory;
foreach (string outputResolution in _Configuration.OutputResolutions)
{
_ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
_ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), options);
(cResultsFullGroupDirectory, _, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution);
mapLogic.LookForAbandoned(this, _Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory);
_ProgressBar.Dispose();
@ -805,6 +835,57 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
}
}
private ReadOnlyCollection<FilePath> GetFilePath(long ticks, string dFacesContentDirectory)
{
List<FilePath> results = [];
FilePath filePath;
FileHolder fileHolder;
string[] files = Directory.GetFiles(dFacesContentDirectory, $"*{_Faces.FileNameExtension}", SearchOption.AllDirectories);
long? skipOlderThan = _Configuration.SkipOlderThanDays is null ? null : new DateTime(ticks).AddDays(-_Configuration.SkipOlderThanDays.Value).Ticks;
foreach (string file in files)
{
fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(file);
filePath = FilePath.Get(_Configuration.PropertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
continue;
if (skipOlderThan is not null && (fileHolder.LastWriteTime is null || fileHolder.LastWriteTime.Value.Ticks < skipOlderThan.Value))
continue;
results.Add(filePath);
}
return new(results);
}
private List<SaveContainer> GetSaveContainers(long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, ProgressBarOptions options, MapLogic mapLogic, string outputResolution)
{
List<SaveContainer> results;
long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray();
(string cResultsFullGroupDirectory, string _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution);
string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent);
ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mapped = Map.Models.Stateless.Methods.IMapLogic.GetMapped(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory);
if (mapped.Count == 0 && !_Configuration.SaveSortingWithoutPerson)
throw new NotSupportedException($"Switch {nameof(_Configuration.SaveSortingWithoutPerson)}!");
ReadOnlyCollection<FilePath> filePaths = GetFilePath(ticks, dFacesContentDirectory);
List<LocationContainer> available = Map.Models.Stateless.Methods.IMapLogic.GetAvailable(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Faces, ticks, dFacesContentDirectory, filePaths);
if (!string.IsNullOrEmpty(_Configuration.FocusDirectory) && _Configuration.FocusDirectory.Length != 2)
throw new NotSupportedException($"{nameof(_Configuration.FocusDirectory)} currently only works with output directory! Example 00.");
ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedWithEncoding = E_Distance.GetMappedWithEncoding(mapped);
if (mappedWithEncoding.Count == 0 && !_Configuration.SaveSortingWithoutPerson)
throw new NotSupportedException($"Switch {nameof(_Configuration.SaveSortingWithoutPerson)}!");
List<LocationContainer> preFiltered = E_Distance.GetPreFilterLocationContainer(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, _Configuration.FocusDirectory, _Configuration.FocusModel, _Configuration.SkipPersonWithMoreThen, ticks, mapLogic, jLinkResolvedPersonKeys, mapped, available);
if (preFiltered.Count == 0)
throw new NotSupportedException("Done?");
DistanceLimits distanceLimits = new(_Configuration.FaceAreaPermyriad, _Configuration.FaceConfidencePercent, _Configuration.FaceDistancePermyriad, _Configuration.RangeDaysDeltaTolerance, _Configuration.RangeDistanceTolerance, _Configuration.RangeFaceAreaPermyriadTolerance, _Configuration.RangeFaceConfidence, _Configuration.SortingMaximumPerFaceShouldBeHigh);
List<LocationContainer> postFiltered = E_Distance.GetPostFilterLocationContainer(mapLogic, preFiltered, distanceLimits);
if (postFiltered.Count == 0)
throw new NotSupportedException("Done?");
string message = $") Building Matrix - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
_ProgressBar = new(postFiltered.Count, message, options);
ReadOnlyCollection<LocationContainer> matrix = E_Distance.GetMatrixLocationContainers(this, _MapConfiguration, ticks, mapLogic, mappedWithEncoding, preFiltered, distanceLimits, postFiltered);
_ProgressBar.Dispose();
results = mapLogic.GetSaveContainers(cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, distanceLimits, matrix);
return results;
}
private void MapLogic(long ticks, ReadOnlyCollection<Container> containers, string fPhotoPrismContentDirectory, MapLogic mapLogic, string outputResolution, ReadOnlyDictionary<long, List<int>> personKeyToIds, ReadOnlyCollection<Shared.Models.Face> distinctValidImageFaces, ReadOnlyCollection<Mapping> distinctValidImageMappingCollection)
{
(_, _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution);
@ -1074,7 +1155,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
bool move = _Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution);
faces = _Faces.GetFaces(outputResolution, dResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionToResize, mappingFromPhotoPrismCollection);
result = GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(mapLogic, item, isFocusRelativePath, mappingFromItem, mappingFromPhotoPrismCollection, faces);
List<(Shared.Models.Face, FileInfo?, string, bool Saved)> faceCollection = _Faces.SaveFaces(_FaceParts.FileNameExtension, dResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, mappingFromItem, exifDirectory, faces);
List<(Shared.Models.Face, FileHolder?, string, bool Saved)> faceCollection = _Faces.SaveFaces(_FaceParts.FileNameExtension, dResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, mappingFromItem, exifDirectory, faces);
if (_Configuration.CopyFacesAndSaveFaceLandmarkForOutputResolutions.Contains(outputResolution))
_FaceParts.CopyFacesAndSaveFaceLandmarkImage(facePartsCollectionDirectory, mappingFromItem, faceCollection);
if (move && faceCollection.All(l => !l.Saved))
@ -1085,7 +1166,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
Map.Models.Stateless.Methods.IMapLogic.SetCreationTime(mappingFromItem, locationContainers);
if (_Configuration.LocationContainerDistanceTolerance is null && _Configuration.MoveToDecade)
Map.Models.Stateless.Methods.IMapLogic.MoveToDecade(_Configuration.PropertyConfiguration, mappingFromItem, locationContainers);
_Distance.LookForMatchFacesAndPossiblyRename(_DistanceLimits, _Faces, item.FilePath, mappingFromItem, exifDirectory, faces, locationContainers);
_Distance.LookForMatchFacesAndPossiblyRename(_Configuration.OverrideForFaceImages, _DistanceLimits, _Faces, item.FilePath, mappingFromItem, exifDirectory, faces, locationContainers);
}
}
(bool review, int[] eyesCollection) = Shared.Models.Stateless.Methods.IFace.GetEyeCollection(_Configuration.EyeThreshold, faces);
@ -1250,8 +1331,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
if (filteredFaceDistanceContainers.Length > 0)
{
int updated = sortingContainers.Count == 0 ? 0 : mapLogic.UpdateFromSortingContainers(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, sortingContainers);
List<SaveContainer> saveContainers;
saveContainers = mapLogic.GetSaveContainers(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, distanceLimits, useFiltersCounter, sortingContainers);
List<SaveContainer> saveContainers = mapLogic.GetSaveContainers(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, distanceLimits, useFiltersCounter, sortingContainers);
if (saveContainers.Count > 0)
mapLogic.SaveContainers(updated, saveContainers);
}

1
Map/.vscode/format-report.json vendored Normal file
View File

@ -0,0 +1 @@
[]

42
Map/.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,42 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Format",
"command": "dotnet",
"type": "process",
"args": [
"format",
"--report",
".vscode",
"--verbosity",
"detailed",
"--severity",
"warn"
],
"problemMatcher": "$msCompile"
},
{
"label": "Format-Whitespaces",
"command": "dotnet",
"type": "process",
"args": [
"format",
"whitespace"
],
"problemMatcher": "$msCompile"
},
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Map.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@ -460,6 +460,31 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic
return result;
}
private (long?, string?) GetDirectory(Configuration configuration, string by, string segmentB)
{
long? ticks = null;
const int zero = 0;
string? directory = null;
string personKeyFormatted;
PersonBirthday personBirthday;
PersonContainer personContainer;
for (int i = _NotMappedPersonContainers.Count - 1; i > 0; i--)
{
if (configuration.SaveIndividually)
break;
personContainer = _NotMappedPersonContainers[i];
if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0)
continue;
personBirthday = personContainer.Birthdays[zero];
ticks = personBirthday.Value.Ticks;
personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personBirthday);
directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, segmentB);
_NotMappedPersonContainers.RemoveAt(i);
break;
}
return (ticks, directory);
}
private (long?, string?) GetDirectory(Configuration configuration, bool saveIndividually, int padLeft, string? segmentC, string by, MappingFromItem mappingFromItem)
{
long? ticks = null;
@ -512,7 +537,30 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic
return result;
}
private Record Get(Configuration configuration, bool saveIndividually, string by, Mapping question, int padLeft)
private Record Get(Configuration configuration, string by, long? personKey, string? displayDirectoryName, string segmentB)
{
long? ticks;
string? directory;
string? debugDirectory;
string? personDirectory;
if (personKey is null || string.IsNullOrEmpty(displayDirectoryName))
{
debugDirectory = null;
(ticks, directory) = GetDirectory(configuration, by, segmentB);
personDirectory = directory is null ? null : Path.Combine(directory, $"X+{ticks}");
}
else
{
ticks = null;
string personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personKey.Value);
debugDirectory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, displayDirectoryName);
directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, segmentB);
personDirectory = Path.Combine(directory, displayDirectoryName, "lnk");
}
return new(debugDirectory, directory, ticks, personDirectory);
}
private Record Get(Configuration configuration, bool saveIndividually, int padLeft, string by, Mapping question)
{
long? ticks;
string? directory;
@ -594,7 +642,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic
if (!PreAndPostContinue(_Configuration, mapping, question))
continue;
}
record = Get(_Configuration, saveIndividually, by, mapping, padLeft);
record = Get(_Configuration, saveIndividually, padLeft, by, mapping);
if (string.IsNullOrEmpty(record.Directory) || string.IsNullOrEmpty(record.PersonDirectory))
continue;
directory = record.Directory;
@ -673,7 +721,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic
Stateless.MapLogic.SaveMappingShortcuts(mappingDirectory);
}
public List<Sorting> GetSortingCollection(Shared.Models.Methods.IDistanceLimits distanceLimits, int i, Face face, FaceDistance faceDistanceEncoding, List<FaceDistance> faceDistanceLengths)
public List<Sorting> GetSortingCollection(int i, Face face, FaceDistance faceDistanceEncoding, List<FaceDistance> faceDistanceLengths)
{
if (_Configuration is null)
throw new NullReferenceException(nameof(_Configuration));
@ -772,6 +820,94 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic
return result;
}
private string? GetDisplayDirectoryName(string? displayDirectoryName, LocationContainer locationContainer)
{
string? result = displayDirectoryName;
ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers = GetWholePercentagesToPersonContainers(locationContainer.Id);
if (wholePercentagesToPersonContainers is not null)
{
foreach (KeyValuePair<int, ReadOnlyCollection<PersonContainer>> keyValuePair in wholePercentagesToPersonContainers)
{
if (keyValuePair.Key != locationContainer.WholePercentages)
continue;
if (keyValuePair.Value.Count != 1)
continue;
result = keyValuePair.Value[0].DisplayDirectoryName;
}
}
return result;
}
public List<SaveContainer> GetSaveContainers(string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, Shared.Models.Methods.IDistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> matrix)
{
if (_Configuration is null)
throw new NullReferenceException(nameof(_Configuration));
List<SaveContainer> results = [];
string by;
Record record;
string segmentB;
bool isBySorting;
string checkFile;
string? directory;
string shortcutFile;
string facesDirectory;
List<string> added = [];
bool isCounterPersonYear;
string facePartsDirectory;
FileHolder? faceFileHolder;
SaveContainer? saveContainer;
string? displayDirectoryName;
FileHolder? resizedFileHolder;
int? useFiltersCounter = null;
string resizeContentDirectory;
FileHolder? facePartsFileHolder;
FileHolder? hiddenFaceFileHolder;
bool sortingContainersAny = matrix.Count > 0;
string cContentDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent);
string forceSingleImageHumanized = nameof(Shared.Models.Stateless.IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent);
foreach (LocationContainer locationContainer in matrix)
{
if (_Configuration.SaveIndividually)
break;
if (locationContainer.LengthPermyriad is null || locationContainer.LengthSource is null)
continue;
if (added.Contains(locationContainer.LengthSource.Name))
continue;
segmentB = locationContainer.LengthPermyriad.Value.ToString().PadLeft(2, '0')[..2];
isCounterPersonYear = locationContainer.PersonKey is not null && IPersonBirthday.IsCounterPersonYear(locationContainer.PersonKey.Value);
displayDirectoryName = isCounterPersonYear ? locationContainer.DisplayDirectoryName : GetDisplayDirectoryName(locationContainer.DisplayDirectoryName, locationContainer);
(by, _, isBySorting) = Stateless.MapLogic.Get(useFiltersCounter, _Configuration.SaveIndividually, sortingContainersAny, forceSingleImageHumanized, locationContainer.LengthPermyriad, locationContainer.PersonKey, displayDirectoryName);
record = Get(_Configuration, by, locationContainer.PersonKey, displayDirectoryName, segmentB);
if (string.IsNullOrEmpty(record.Directory) || string.IsNullOrEmpty(record.PersonDirectory))
continue;
directory = record.Directory;
if (!string.IsNullOrEmpty(record.DebugDirectory))
results.Add(new(record.DebugDirectory));
if (locationContainer.PersonKey is null)
{
if (!_Configuration.SaveSortingWithoutPerson)
throw new NotSupportedException();
if (record.Ticks is null)
continue;
}
results.Add(new(record.PersonDirectory));
facesDirectory = locationContainer.LengthSource.DirectoryName;
faceFileHolder = IFileHolder.Get(locationContainer.LengthSource.FullName);
checkFile = Path.Combine(directory, $"{locationContainer.LengthSource.Name}");
shortcutFile = Path.Combine(record.PersonDirectory, $"{locationContainer.LengthSource.Name}.lnk");
resizeContentDirectory = Stateless.MapLogic.GetResizeContentDirectory(_PropertyConfiguration, cContentDirectory, locationContainer.LengthSource);
facePartsDirectory = Stateless.MapLogic.GetFacePartsDirectoryX(_PropertyConfiguration, d2FacePartsContentDirectory, locationContainer.LengthSource);
hiddenFaceFileHolder = IFileHolder.Get(Path.Combine(facesDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{_Configuration.FacesHiddenFileNameExtension}"));
facePartsFileHolder = IFileHolder.Get(Path.Combine(facePartsDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{_Configuration.FacePartsFileNameExtension}"));
resizedFileHolder = IFileHolder.Get(Path.Combine(resizeContentDirectory, $"{locationContainer.LengthSource.FileNameFirstSegment}{Path.GetExtension(locationContainer.LengthSource.NameWithoutExtension)}"));
saveContainer = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, resizedFileHolder, shortcutFile);
results.Add(saveContainer);
added.Add(locationContainer.LengthSource.Name);
}
return results;
}
public List<SaveContainer> GetSaveContainers(string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyDictionary<int, Mapping>> idToWholePercentagesToMapping, Shared.Models.Methods.IDistanceLimits distanceLimits, int? useFiltersCounter, ReadOnlyCollection<SortingContainer> sortingContainers)
{
if (_Configuration is null)
@ -810,7 +946,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic
throw new NotSupportedException();
if (question.MappingFromLocation is null)
continue;
record = Get(_Configuration, _Configuration.SaveIndividually, by, question, padLeft);
record = Get(_Configuration, _Configuration.SaveIndividually, padLeft, by, question);
if (string.IsNullOrEmpty(record.Directory) || string.IsNullOrEmpty(record.PersonDirectory))
continue;
directory = record.Directory;
@ -1320,10 +1456,13 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic
return result;
}
public bool InSkipCollection(int id, MappingFromLocation mappingFromLocation) =>
_SkipCollection.TryGetValue(id, out List<int>? wholePercentagesCollection) && wholePercentagesCollection.Contains(mappingFromLocation.WholePercentages);
public bool InSkipCollection(int id, int wholePercentages) =>
_SkipCollection.TryGetValue(id, out List<int>? wholePercentagesCollection) && wholePercentagesCollection.Contains(wholePercentages);
public bool? IsFocusPerson(int? skipPersonWithMoreThen, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation)
public bool InSkipCollection(int id, MappingFromLocation mappingFromLocation) =>
InSkipCollection(id, mappingFromLocation.WholePercentages);
public bool? IsFocusPerson(int? skipPersonWithMoreThen, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, int wholePercentages)
{
bool? result;
ReadOnlyCollection<PersonContainer>? personContainers;
@ -1331,7 +1470,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic
result = null;
else if (wholePercentagesToPersonContainers is null)
result = null;
else if (!wholePercentagesToPersonContainers.TryGetValue(mappingFromLocation.WholePercentages, out personContainers))
else if (!wholePercentagesToPersonContainers.TryGetValue(wholePercentages, out personContainers))
result = null;
else
{
@ -1355,6 +1494,9 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic
return result;
}
public bool? IsFocusPerson(int? skipPersonWithMoreThen, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) =>
IsFocusPerson(skipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation.WholePercentages);
public void LookForAbandoned(IDlibDotNet dlibDotNet, Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection<Container> readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory)
{
string[] directories;

View File

@ -387,7 +387,7 @@ internal abstract class DistanceLogic
}
personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly);
if (personNameDirectories.Length > 1)
throw new NotSupportedException();
throw new NotSupportedException("Try deleting *.lnk files!");
foreach (string personNameDirectory in personNameDirectories)
{
directoryNumber++;

View File

@ -0,0 +1,212 @@
using ShellProgressBar;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Text.Json;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Stateless.Methods;
using static View_by_Distance.Map.Models.Stateless.MapLogic;
namespace View_by_Distance.Map.Models.Stateless;
internal abstract class FaceFileLogic
{
private static void MappedParallelFor(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, ReadOnlyDictionary<int, List<(string, int)>> skipCollection, List<LocationContainer> locationContainers, MappedFile mappedFile)
{
int? id;
string checkFile;
DateOnly dateOnly;
FilePath filePath;
string[] fileMatches;
FileHolder fileHolder;
int? wholePercentages;
const string lnk = ".lnk";
ExifDirectory? exifDirectory;
string personDisplayDirectoryName;
const bool fromDistanceContent = true;
List<(string File, int WholePercentages)>? wholePercentagesCollection;
if (!mappedFile.FilePath.Name.EndsWith(lnk))
{
if (mappedFile.FilePath.Id is null)
return;
id = mappedFile.FilePath.Id;
wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, mappedFile.FilePath);
}
else
{
fileHolder = IFileHolder.Get(mappedFile.FilePath.FullName[..^4]);
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
return;
id = filePath.Id;
wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath);
}
if (wholePercentages is null)
return;
if (configuration.LinkedAlpha is null && string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory) && skipCollection.TryGetValue(id.Value, out wholePercentagesCollection))
{
fileMatches = (from l in wholePercentagesCollection where l.WholePercentages == wholePercentages select l.File).ToArray();
foreach (string fileMatch in fileMatches)
{
if (string.IsNullOrEmpty(fileMatch) || !File.Exists(fileMatch))
continue;
checkFile = $"{fileMatch}.dup";
if (File.Exists(checkFile))
continue;
File.Move(fileMatch, checkFile);
continue;
}
}
dateOnly = DateOnly.FromDateTime(new DateTime(mappedFile.FilePath.CreationTicks));
if (mappedFile.FilePath.Name.EndsWith(lnk) || !File.Exists(mappedFile.FilePath.FullName))
exifDirectory = null;
else
exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(mappedFile.FilePath);
RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value);
personDisplayDirectoryName = mappedFile.PersonDisplayDirectoryName is null ? configuration.MappingDefaultName : mappedFile.PersonDisplayDirectoryName;
lock (locationContainers)
locationContainers.Add(new(dateOnly,
exifDirectory,
mappedFile.DirectoryNumber,
personDisplayDirectoryName,
null,
null,
mappedFile.FilePath,
fromDistanceContent,
id.Value,
null,
null,
mappedFile.PersonKey,
rectangle,
wholePercentages.Value));
}
private static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetReadOnly(Dictionary<int, Dictionary<int, LocationContainer>> keyValuePairs)
{
Dictionary<int, ReadOnlyDictionary<int, LocationContainer>> results = [];
foreach (KeyValuePair<int, Dictionary<int, LocationContainer>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
return new(results);
}
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMapped(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory)
{
Dictionary<int, Dictionary<int, LocationContainer>> results = [];
List<LocationContainer> locationContainers = [];
Dictionary<int, LocationContainer>? keyValuePairs;
Dictionary<int, List<(string, int)>> skipCollection = [];
Dictionary<int, List<(string, int)>> skipNotSkipCollection = [];
ReadOnlyCollection<string> readOnlyPersonKeyFormattedCollection;
ReadOnlyDictionary<string, string> readOnlyPersonKeyFormattedToNewestPersonKeyFormatted;
SetSkipCollections(configuration, personContainers, a2PeopleSingletonDirectory, skipCollection, skipNotSkipCollection);
{
List<string> personKeyFormattedCollection = [];
Dictionary<string, string> personKeyFormattedToNewestPersonKeyFormatted = [];
SetPersonCollections(configuration, personContainers, personKeyFormattedToNewestPersonKeyFormatted, personKeyFormattedCollection);
readOnlyPersonKeyFormattedCollection = new(personKeyFormattedCollection);
readOnlyPersonKeyFormattedToNewestPersonKeyFormatted = new(personKeyFormattedToNewestPersonKeyFormatted);
}
List<Record> records = DistanceLogic.DeleteEmptyDirectoriesAndGetCollection(propertyConfiguration, configuration, ticks, eDistanceContentDirectory, readOnlyPersonKeyFormattedToNewestPersonKeyFormatted, readOnlyPersonKeyFormattedCollection);
List<MappedFile> mappedFiles = GetMappedFiles(propertyConfiguration, configuration, personContainers, records);
if (mappedFiles.Count > 0)
{
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)";
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
ReadOnlyDictionary<int, List<(string, int)>> readOnlySkipNotSkipCollection = new(skipCollection);
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(mappedFiles.Count, message, options);
_ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) =>
{
progressBar.Tick();
MappedParallelFor(propertyConfiguration, configuration, readOnlySkipNotSkipCollection, locationContainers, mappedFiles[i]);
});
}
foreach (LocationContainer locationContainer in locationContainers)
{
if (!results.TryGetValue(locationContainer.Id, out keyValuePairs))
{
results.Add(locationContainer.Id, []);
if (!results.TryGetValue(locationContainer.Id, out keyValuePairs))
throw new Exception();
}
if (keyValuePairs.ContainsKey(locationContainer.WholePercentages))
continue;
keyValuePairs.Add(locationContainer.WholePercentages, locationContainer);
}
return GetReadOnly(results);
}
private static void MoveUnableToMatch(FilePath filePath)
{
string checkFile = $"{filePath.FullName}.unk";
if (File.Exists(filePath.FullName) && !File.Exists(checkFile))
File.Move(filePath.FullName, checkFile);
}
private static void AvailableParallelFor(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, IFaceD dFace, List<LocationContainer> locationContainers, FilePath filePath)
{
string? json;
const bool fromDistanceContent = false;
if (filePath.Id is null)
return;
DateOnly dateOnly = DateOnly.FromDateTime(new DateTime(filePath.CreationTicks));
int? wholePercentages = IMapping.GetWholePercentages(dFace.FileNameExtension, filePath);
if (wholePercentages is null)
{
if (configuration.DistanceMoveUnableToMatch)
MoveUnableToMatch(filePath);
return;
}
ExifDirectory exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePath);
json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(exifDirectory);
if (json is null || !json.Contains(nameof(DateTime)))
{
if (configuration.DistanceMoveUnableToMatch)
MoveUnableToMatch(filePath);
return;
}
FaceFile? faceFile = JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile);
if (faceFile is null || faceFile.Location is null)
{
if (configuration.DistanceMoveUnableToMatch)
MoveUnableToMatch(filePath);
return;
}
RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value);
if (rectangle is null)
return;
lock (locationContainers)
locationContainers.Add(new(dateOnly,
exifDirectory,
null,
null,
null,
faceFile,
filePath,
fromDistanceContent,
filePath.Id.Value,
null,
null,
null,
rectangle,
wholePercentages.Value));
}
internal static List<LocationContainer> GetAvailable(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, IFaceD dFace, long ticks, string dFacesContentDirectory, ReadOnlyCollection<FilePath> filePaths)
{
List<LocationContainer> results = [];
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
string message = $") Building Available Face Files Collection - {totalSeconds} total second(s)";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(filePaths.Count, message, options);
_ = Parallel.For(0, filePaths.Count, parallelOptions, (i, state) =>
{
progressBar.Tick();
AvailableParallelFor(propertyConfiguration, configuration, dFace, results, filePaths[i]);
});
return results;
}
}

View File

@ -438,7 +438,7 @@ internal abstract class MapLogic
return result;
}
private static List<MappedFile> GetMappedFiles(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, ReadOnlyCollection<PersonContainer> personContainers, List<Record> records)
internal static List<MappedFile> GetMappedFiles(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, ReadOnlyCollection<PersonContainer> personContainers, List<Record> records)
{
List<MappedFile> results = [];
long personKey;
@ -540,10 +540,13 @@ internal abstract class MapLogic
exifDirectory,
mappedFile.DirectoryNumber,
personDisplayDirectoryName,
null,
null,
mappedFile.FilePath,
fromDistanceContent,
id.Value,
null,
null,
mappedFile.PersonKey,
rectangle,
wholePercentages.Value));
@ -561,6 +564,8 @@ internal abstract class MapLogic
Dictionary<string, (FilePath, int)> distinct = [];
foreach (LocationContainer locationContainer in locationContainers)
{
if (locationContainer.PersonKey is null)
continue;
key = string.Concat(locationContainer.PersonKey, locationContainer.Id);
if (distinct.TryGetValue(key, out item))
{
@ -576,7 +581,7 @@ internal abstract class MapLogic
}
delete.Add(item.FilePath);
delete.Add(locationContainer.FilePath);
duplicates.Add(new(locationContainer.PersonKey, locationContainer.Id, locationContainer.FilePath, percent));
duplicates.Add(new(locationContainer.PersonKey.Value, locationContainer.Id, locationContainer.FilePath, percent));
continue;
}
distinct.Add(key, new(locationContainer.FilePath, locationContainer.WholePercentages));
@ -990,6 +995,22 @@ internal abstract class MapLogic
return result;
}
internal static string GetFacePartsDirectoryX(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string d2FacePartsContentDirectory, FilePath filePath)
{
string result;
(string directoryName, _) = IPath.GetDirectoryNameAndIndex(propertyConfiguration, filePath);
result = Path.Combine(d2FacePartsContentDirectory, directoryName, filePath.NameWithoutExtension);
return result;
}
internal static string GetResizeContentDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string cContentDirectory, FilePath filePath)
{
string result;
(string directoryName, _) = IPath.GetDirectoryNameAndIndex(propertyConfiguration, filePath);
result = Path.Combine(cContentDirectory, directoryName);
return result;
}
internal static SaveContainer GetDebugSaveContainer(SortingContainer sortingContainer, string directory, Mapping keyMapping)
{
SaveContainer result;
@ -1171,29 +1192,29 @@ internal abstract class MapLogic
return results;
}
internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, Mapping mapping)
internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, int? by, string? displayDirectoryName)
{
string by;
string byValue;
bool isByMapping;
bool isBySorting;
if (mapping.By is null)
if (by is null)
{
isByMapping = false;
isBySorting = !sortingContainersAny;
by = $"{nameof(Shared.Models.Stateless.IMapLogic.Mapping)}Null";
byValue = $"{nameof(Shared.Models.Stateless.IMapLogic.Mapping)}Null";
}
else
{
isByMapping = mapping.By == Shared.Models.Stateless.IMapLogic.Mapping;
isBySorting = mapping.By == Shared.Models.Stateless.IMapLogic.Sorting;
bool isDefaultName = mapping.MappingFromPerson is not null && IPerson.IsDefaultName(mapping.MappingFromPerson.DisplayDirectoryName);
if (isBySorting && mapping.MappingFromPerson is null)
by = saveIndividually ? nameof(Shared.Models.Stateless.IMapLogic.Individually) : $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)} Without Person{(distancePermyriad < 2000 ? "-A" : "-Z")}";
isByMapping = by == Shared.Models.Stateless.IMapLogic.Mapping;
isBySorting = by == Shared.Models.Stateless.IMapLogic.Sorting;
bool isDefaultName = displayDirectoryName is not null && IPerson.IsDefaultName(displayDirectoryName);
if (isBySorting && displayDirectoryName is null)
byValue = saveIndividually ? nameof(Shared.Models.Stateless.IMapLogic.Individually) : $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)} Without Person{(distancePermyriad < 2000 ? "-A" : "-Z")}";
else if (isBySorting && useFiltersCounter.HasValue)
by = $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)}{(!isDefaultName ? "-A" : "-Z")} Modified Filters - {useFiltersCounter.Value}";
byValue = $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)}{(!isDefaultName ? "-A" : "-Z")} Modified Filters - {useFiltersCounter.Value}";
else
{
by = $"{mapping.By.Value switch
byValue = $"{by.Value switch
{
Shared.Models.Stateless.IMapLogic.Mapping => nameof(Shared.Models.Stateless.IMapLogic.Mapping),
Shared.Models.Stateless.IMapLogic.Sorting => saveIndividually ? nameof(Shared.Models.Stateless.IMapLogic.Individually) : nameof(Shared.Models.Stateless.IMapLogic.Sorting),
@ -1202,9 +1223,15 @@ internal abstract class MapLogic
}}{(!isDefaultName ? "-A" : "-Z")}";
}
}
return new(by, isByMapping, isBySorting);
return new(byValue, isByMapping, isBySorting);
}
internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, long? personKey, string? displayDirectoryName) =>
Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, personKey is null ? null : Shared.Models.Stateless.IMapLogic.Mapping, displayDirectoryName);
internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, Mapping mapping) =>
Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, mapping.By, mapping.MappingFromPerson?.DisplayDirectoryName);
internal static void CheckCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string? rootDirectoryParent)
{
string json;
@ -1272,7 +1299,7 @@ internal abstract class MapLogic
return new(results);
}
internal static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation)
internal static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, int wholePercentages)
{
bool? result;
ReadOnlyCollection<PersonContainer>? personContainers;
@ -1280,7 +1307,7 @@ internal abstract class MapLogic
result = null;
else
{
if (!wholePercentagesToPersonContainers.TryGetValue(mappingFromLocation.WholePercentages, out personContainers))
if (!wholePercentagesToPersonContainers.TryGetValue(wholePercentages, out personContainers))
result = null;
else
{

View File

@ -1,5 +1,6 @@
using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Map.Models.Stateless.Methods;
@ -49,11 +50,26 @@ public interface IMapLogic
bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) =>
CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation);
static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) =>
MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation);
MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation.WholePercentages);
bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, int wholePercentages) =>
CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, wholePercentages);
static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, int wholePercentages) =>
MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, wholePercentages);
string TestStatic_GetDecade(MappingFromItem mappingFromItem) =>
GetDecade(mappingFromItem);
static string GetDecade(MappingFromItem mappingFromItem) =>
DecadeLogic.GetDecade(mappingFromItem, null);
ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> TestStatic_GetMappedFiles(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) =>
GetMapped(maxDegreeOfParallelism, propertyConfiguration, configuration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory);
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMapped(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) =>
FaceFileLogic.GetMapped(maxDegreeOfParallelism, propertyConfiguration, configuration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory);
List<LocationContainer> TestStatic_GetAvailable(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, IFaceD dFace, long ticks, string dFacesContentDirectory, ReadOnlyCollection<FilePath> filePaths) =>
GetAvailable(maxDegreeOfParallelism, propertyConfiguration, configuration, dFace, ticks, dFacesContentDirectory, filePaths);
static List<LocationContainer> GetAvailable(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, IFaceD dFace, long ticks, string dFacesContentDirectory, ReadOnlyCollection<FilePath> filePaths) =>
FaceFileLogic.GetAvailable(maxDegreeOfParallelism, propertyConfiguration, configuration, dFace, ticks, dFacesContentDirectory, filePaths);
}

View File

@ -17,14 +17,16 @@ internal abstract class RelationLogic
Dictionary<long, Dictionary<int, List<LocationContainer>>> personKeyTo = [];
foreach (LocationContainer locationContainer in locationContainers)
{
if (locationContainer.PersonKey is null)
continue;
if (!locationContainer.FromDistanceContent)
continue;
if (!locationContainer.FilePath.FullName.Contains(configuration.LocationContainerDirectoryPattern))
continue;
if (!personKeyTo.TryGetValue(locationContainer.PersonKey, out yearTo))
if (!personKeyTo.TryGetValue(locationContainer.PersonKey.Value, out yearTo))
{
personKeyTo.Add(locationContainer.PersonKey, []);
if (!personKeyTo.TryGetValue(locationContainer.PersonKey, out yearTo))
personKeyTo.Add(locationContainer.PersonKey.Value, []);
if (!personKeyTo.TryGetValue(locationContainer.PersonKey.Value, out yearTo))
throw new Exception();
}
if (!yearTo.TryGetValue(locationContainer.CreationDateOnly.Year, out collection))
@ -45,6 +47,7 @@ internal abstract class RelationLogic
int lastIndex;
List<int> years = [];
List<int> indices = [];
LocationContainer locationContainer;
List<(int Index, int Year)> sort = [];
List<LocationContainer> collection = [];
KeyValuePair<int, List<LocationContainer>> keyValue;
@ -76,7 +79,10 @@ internal abstract class RelationLogic
key = $"{years.Min()}-{keyValue.Key}";
if (collection.Count == 0)
continue;
results.Add(new(key, collection[0].PersonKey, new(collection)));
locationContainer = collection[0];
if (locationContainer.PersonKey is null)
continue;
results.Add(new(key, locationContainer.PersonKey.Value, new(collection)));
collection = [];
years.Clear();
}

View File

@ -35,6 +35,17 @@ internal static class Face
result = pngDirectory.TextualData[artist.Length..];
break;
}
if (result is null)
{
const string author = "Author:";
foreach (PngDirectory pngDirectory in pngDirectories)
{
if (pngDirectory.TextualData is null || !pngDirectory.TextualData.StartsWith(author))
continue;
result = pngDirectory.TextualData[author.Length..];
break;
}
}
}
return result;
}

View File

@ -112,7 +112,7 @@ public class OffsetDateTimeOriginal
string checkFile;
PropertyItem? propertyItem;
string? ticksDirectory = null;
int dateTimeOriginal = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal;
const int dateTimeOriginal = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal; // 36867
for (int i = 0; i < int.MaxValue; i++)
{
ticksDirectory = Path.Combine(sourceDirectory, ticks.ToString());

View File

@ -2,7 +2,9 @@ using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record FaceFile(DateTime DateTime,
public record FaceFile(int? AreaPermyriad,
int? ConfidencePercent,
DateTime DateTime,
string? DMS,
Dictionary<Stateless.FacePart, FacePoint[]>? FaceParts,
Location? Location,

View File

@ -5,11 +5,57 @@ namespace View_by_Distance.Shared.Models;
public record LocationContainer(DateOnly CreationDateOnly,
ExifDirectory? ExifDirectory,
int? DirectoryNumber,
string DisplayDirectoryName,
string? DisplayDirectoryName,
object? Encoding,
FaceFile? FaceFile,
FilePath FilePath,
bool FromDistanceContent,
int Id,
Location? Location,
long PersonKey,
int? LengthPermyriad,
FilePath? LengthSource,
long? PersonKey,
RectangleF? Rectangle,
int WholePercentages);
int WholePercentages)
{
public static LocationContainer Get(LocationContainer locationContainer, object? encoding, bool keepExifDirectory)
{
LocationContainer result;
result = new(locationContainer.CreationDateOnly,
keepExifDirectory ? locationContainer.ExifDirectory : null,
locationContainer.DirectoryNumber,
locationContainer.DisplayDirectoryName,
encoding,
locationContainer.FaceFile,
locationContainer.FilePath,
locationContainer.FromDistanceContent,
locationContainer.Id,
locationContainer.LengthPermyriad,
locationContainer.LengthSource,
locationContainer.PersonKey,
locationContainer.Rectangle,
locationContainer.WholePercentages);
return result;
}
public static LocationContainer Get(LocationContainer source, LocationContainer locationContainer, int lengthPermyriad, bool keepExifDirectory, bool keepEncoding)
{
LocationContainer result;
result = new(locationContainer.CreationDateOnly,
keepExifDirectory ? locationContainer.ExifDirectory : null,
locationContainer.DirectoryNumber,
locationContainer.DisplayDirectoryName,
keepEncoding ? locationContainer.Encoding : null,
locationContainer.FaceFile,
locationContainer.FilePath,
locationContainer.FromDistanceContent,
locationContainer.Id,
lengthPermyriad,
source.FilePath,
locationContainer.PersonKey,
locationContainer.Rectangle,
locationContainer.WholePercentages);
return result;
}
}

View File

@ -4,6 +4,6 @@ public interface IFaceD
{
public string FileNameExtension { get; }
void ReSaveFace(ExifDirectory exifDirectory, LocationContainer locationContainer, Models.Face face);
void ReSaveFace(ExifDirectory exifDirectory, FilePath filePath, Models.Face face, bool mappedFile);
}

View File

@ -10,9 +10,9 @@ public interface ILocation
static Models.Location? GetLocation(int height, Rectangle rectangle, int width) =>
Location.GetLocation(height, rectangle, width);
List<Models.Face> TestStatic_FilterByIntersect(Models.Face[] faces, float rectangleIntersectMinimum, int wholePercentages) =>
List<Models.Face> TestStatic_FilterByIntersect(List<Models.Face> faces, float rectangleIntersectMinimum, int wholePercentages) =>
FilterByIntersect(faces, rectangleIntersectMinimum, wholePercentages);
static List<Models.Face> FilterByIntersect(Models.Face[] faces, float rectangleIntersectMinimum, int wholePercentages) =>
static List<Models.Face> FilterByIntersect(List<Models.Face> faces, float rectangleIntersectMinimum, int wholePercentages) =>
Location.FilterByIntersect(faces, rectangleIntersectMinimum, wholePercentages);
RectangleF? TestStatic_GetPercentagesRectangle(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) =>

View File

@ -310,7 +310,7 @@ internal abstract class Location
return results;
}
internal static List<Models.Face> FilterByIntersect(Models.Face[] faces, float rectangleIntersectMinimum, int wholePercentages)
internal static List<Models.Face> FilterByIntersect(List<Models.Face> faces, float rectangleIntersectMinimum, int wholePercentages)
{
List<Models.Face> results = [];
float? percent;