2024-01-27 18:11:24 -07:00

546 lines
29 KiB
C#

using ShellProgressBar;
using System.Collections.ObjectModel;
using System.Text.Json;
using View_by_Distance.FaceRecognitionDotNet;
using View_by_Distance.Property.Models.Stateless;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Methods;
namespace View_by_Distance.Distance.Models;
public partial class E_Distance : IDistance
{
internal record Record(FilePath FilePath, FaceRecognitionDotNet.FaceEncoding FaceRecognitionDotNetFaceEncoding);
private readonly List<string> _Moved;
private readonly List<double?> _Debug;
private readonly List<string> _Renamed;
private readonly int _FaceConfidencePercent;
private readonly bool _DistanceRenameToMatch;
private readonly bool _DistanceMoveUnableToMatch;
private readonly float _RectangleIntersectMinimum;
private readonly List<string> _AllMappedFaceFiles;
private readonly List<string> _AllMappedFaceFileNames;
private readonly double _RangeDistanceToleranceAverage;
private readonly List<string> _DuplicateMappedFaceFiles;
public E_Distance(bool distanceMoveUnableToMatch, bool distanceRenameToMatch, int faceConfidencePercent, float[] rangeDistanceTolerance, float[] rectangleIntersectMinimums)
{
_Debug = [];
_Moved = [];
_Renamed = [];
_AllMappedFaceFiles = [];
_AllMappedFaceFileNames = [];
_DuplicateMappedFaceFiles = [];
_DistanceRenameToMatch = distanceRenameToMatch;
_FaceConfidencePercent = faceConfidencePercent;
_DistanceMoveUnableToMatch = distanceMoveUnableToMatch;
_RectangleIntersectMinimum = rectangleIntersectMinimums.Max();
_RangeDistanceToleranceAverage = rangeDistanceTolerance.Average();
}
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 FaceDistanceContainer[] GetFaceDistanceContainers(MappingFromItem mappingFromItem, List<Face> intersectFaces)
{
FaceDistanceContainer[] results;
int wholePercentages;
int confidencePercent;
FaceDistance faceDistance;
FaceDistanceContainer faceDistanceContainer;
List<FaceDistanceContainer> collection = [];
foreach (Face face in intersectFaces)
{
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
throw new NotSupportedException();
if (face.Mapping?.MappingFromFilterPost is null)
throw new NotSupportedException();
confidencePercent = Shared.Models.Stateless.Methods.ILocation.GetConfidencePercent(_FaceConfidencePercent, face.Location.Confidence);
wholePercentages = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
if (face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding faceEncoding)
faceDistance = new(confidencePercent, mappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, face.Mapping?.MappingFromFilterPost, mappingFromItem.Id, mappingFromItem.IsWrongYear, wholePercentages);
else
{
faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding);
faceDistance = new(confidencePercent, mappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, face.Mapping?.MappingFromFilterPost, mappingFromItem.Id, mappingFromItem.IsWrongYear, wholePercentages);
lock (intersectFaces)
face.SetFaceDistance(faceDistance);
}
faceDistanceContainer = new(face, faceDistance);
collection.Add(faceDistanceContainer);
}
results = collection.ToArray();
return results;
}
private static ReadOnlyCollection<FaceDistance> GetFaceDistanceEncodings(FaceDistanceContainer[] faceDistanceContainers)
{
List<FaceDistance> faceDistanceEncodings = [];
foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers)
{
if (faceDistanceContainer.FaceDistance.Encoding is null)
continue;
faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance);
}
return new(faceDistanceEncodings);
}
private List<(Face Face, double? Length)> GetValues(MappingFromItem mappingFromItem, List<Face> intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding)
{
List<(Face Face, double? Length)> results = [];
Face face;
FaceDistance faceDistanceLength;
FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding = FaceRecognition.LoadFaceEncoding(modelsFaceEncoding.RawEncoding);
FaceDistance faceDistanceEncoding = new(faceRecognitionDotNetFaceEncoding);
FaceDistanceContainer[] faceDistanceContainers = GetFaceDistanceContainers(mappingFromItem, intersectFaces);
int faceDistanceContainersLength = faceDistanceContainers.Length;
if (faceDistanceContainersLength != intersectFaces.Count)
throw new NotSupportedException();
ReadOnlyCollection<FaceDistance> faceDistanceEncodings = GetFaceDistanceEncodings(faceDistanceContainers);
if (faceDistanceEncodings.Count != intersectFaces.Count)
throw new NotSupportedException();
List<FaceDistance> faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding);
if (faceDistanceLengths.Count != faceDistanceContainersLength)
throw new NotSupportedException();
for (int i = 0; i < intersectFaces.Count; i++)
{
face = intersectFaces[i];
faceDistanceLength = faceDistanceLengths[i];
if (faceDistanceLength.Length is null)
throw new NotSupportedException();
results.Add(new(face, faceDistanceLength.Length.Value));
}
return results;
}
private (Face, double?)[] GetClosestFaceByDistanceIgnoringTolerance(MappingFromItem mappingFromItem, List<Face> intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding)
{
(Face, double?)[] results;
List<(Face Face, double? Length)> collection = GetValues(mappingFromItem, intersectFaces, modelsFaceEncoding);
results = (from l in collection where l.Length < _RangeDistanceToleranceAverage orderby l.Length select l).Take(1).ToArray();
if (results.Length > 0)
{
(Face _, double? length) = results.First();
_Debug.Add(length);
}
return results;
}
private static List<(Face, double?)> GetMatchingFacesByFaceEncoding(Face[] filteredFaces, string? json)
{
List<(Face, double?)> results = [];
string check;
foreach (Face face in filteredFaces)
{
if (json is null || face.FaceEncoding is null)
continue;
if (!json.Contains(face.FaceEncoding.RawEncoding[0].ToString()))
continue;
check = JsonSerializer.Serialize(face.FaceEncoding);
if (check != json)
continue;
results.Add(new(face, 0));
}
return results;
}
private static FileInfo? CheckFileThenGetFileInfo(string facesFileNameExtension, FilePath filePath, MappingFromItem mappingFromItem, string file, List<(Face, double?)> checkFaces)
{
FileInfo? result = null;
string checkFile;
string? mappedFaceDirectory;
string deterministicHashCodeKey;
foreach ((Face face, _) in checkFaces)
{
if (checkFaces.Count != 1)
break;
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
throw new NotSupportedException();
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);
checkFile = Path.Combine(mappedFaceDirectory, $"{deterministicHashCodeKey}{mappingFromItem.ImageFileHolder.ExtensionLowered}{facesFileNameExtension}");
if (checkFile == file)
continue;
result = new FileInfo(checkFile);
}
return result;
}
private void AppendMatchingDuplicates(string file, string[] matches)
{
string checkFile;
FileInfo fileInfo = new(file);
List<(long Length, string FullName)> collection = [];
if (fileInfo.Exists)
collection.Add(new(fileInfo.Length, fileInfo.FullName));
lock (_DuplicateMappedFaceFiles)
_DuplicateMappedFaceFiles.Add(file);
foreach (string match in matches)
{
fileInfo = new(match);
if (!fileInfo.Exists)
continue;
collection.Add(new(fileInfo.Length, fileInfo.FullName));
break;
}
collection = collection.OrderBy(l => l.Length).ToList();
for (int i = 0; i < collection.Count - 1; i++)
{
checkFile = $"{collection[i].FullName}.dup";
if (File.Exists(checkFile))
continue;
File.Move(collection[i].FullName, checkFile);
}
}
public void LookForMatchFacesAndPossiblyRename(string facesFileNameExtension, FilePath filePath, MappingFromItem mappingFromItem, List<Face> faces, ReadOnlyCollection<LocationContainer> locationContainers)
{
string? json;
string[] matches;
FileInfo? fileInfo;
List<Face> intersectFaces;
List<(Face, double?)> checkFaces = [];
Shared.Models.FaceEncoding? modelsFaceEncoding;
Face[] filteredFaces = (from l in faces where l.FaceEncoding is not null && l.Location is not null && l.OutputResolution is not null select l).ToArray();
if (filteredFaces.Length != faces.Count)
checkFaces.Clear();
foreach (LocationContainer locationContainer in locationContainers)
{
if (_Renamed.Contains(locationContainer.FilePath.FullName))
continue;
if (locationContainer.FromDistanceContent && _DuplicateMappedFaceFiles.Contains(locationContainer.FilePath.Name))
continue;
checkFaces.Clear();
if (locationContainer.ExifDirectory is null)
{
if (locationContainer.FromDistanceContent)
throw new NullReferenceException(nameof(locationContainer.ExifDirectory));
continue;
}
json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.ExifDirectory);
if (json is null)
{
if (_DistanceMoveUnableToMatch)
MoveUnableToMatch(locationContainer.FilePath);
continue;
}
if (filteredFaces.Length > 0)
checkFaces.AddRange(GetMatchingFacesByFaceEncoding(filteredFaces, json));
if (checkFaces.Count == 1)
_Debug.Add(0);
if (checkFaces.Count != 1 && !string.IsNullOrEmpty(json))
{
checkFaces.Clear();
modelsFaceEncoding = JsonSerializer.Deserialize<Shared.Models.FaceEncoding>(json);
if (modelsFaceEncoding is null)
throw new NotSupportedException();
if (filteredFaces.Length > 0)
{
intersectFaces = Shared.Models.Stateless.Methods.ILocation.FilterByIntersect(filteredFaces, _RectangleIntersectMinimum, locationContainer.WholePercentages);
if (intersectFaces.Count > 0)
checkFaces.AddRange(GetClosestFaceByDistanceIgnoringTolerance(mappingFromItem, intersectFaces, modelsFaceEncoding));
}
}
if (checkFaces.Count == 0)
{
if (_DistanceMoveUnableToMatch)
MoveUnableToMatch(locationContainer.FilePath);
continue;
}
if (checkFaces.Count != 1)
{
if (_DistanceMoveUnableToMatch)
MoveUnableToMatch(locationContainer.FilePath);
continue;
}
fileInfo = CheckFileThenGetFileInfo(facesFileNameExtension, filePath, mappingFromItem, locationContainer.FilePath.FullName, checkFaces);
if (fileInfo is not null)
{
if (_DistanceRenameToMatch && fileInfo is not null)
{
if (fileInfo.Exists)
File.Delete(locationContainer.FilePath.FullName);
else
File.Move(locationContainer.FilePath.FullName, fileInfo.FullName);
File.WriteAllText($"{fileInfo.FullName}.old", $"{fileInfo.FullName}{Environment.NewLine}{locationContainer.FilePath.FullName}");
_Renamed.Add(locationContainer.FilePath.FullName);
}
continue;
}
if (_AllMappedFaceFileNames.Contains(locationContainer.FilePath.Name))
{
lock (_AllMappedFaceFiles)
matches = (from l in _AllMappedFaceFiles where l != locationContainer.FilePath.FullName && Path.GetFileName(l) == locationContainer.FilePath.Name select l).ToArray();
if (locationContainer.FromDistanceContent && matches.Length > 0)
AppendMatchingDuplicates(locationContainer.FilePath.FullName, matches);
}
if (!locationContainer.FromDistanceContent)
continue;
lock (_AllMappedFaceFiles)
_AllMappedFaceFiles.Add(locationContainer.FilePath.FullName);
lock (_AllMappedFaceFileNames)
_AllMappedFaceFileNames.Add(locationContainer.FilePath.Name);
}
}
public void Clear()
{
// double?[] debug = (from l in _Debug where l is null or not 0 select l).ToArray();
// if (debug.Length > 0)
// {
// string debugMessage = $"{_Debug.Count - debug.Length} - {debug.Min()} - {_Debug.Max()}";
// }
// if (_DuplicateMappedFaceFiles.Count > 0)
// _Log.Info($"Renamed {_DuplicateMappedFaceFiles.Count} to *.dup file(s)");
if (_Moved.Count > 0 || _Renamed.Count > 0)
throw new NotImplementedException("Restart!");
_Debug.Clear();
_Moved.Clear();
_Renamed.Clear();
_AllMappedFaceFiles.Clear();
_AllMappedFaceFileNames.Clear();
_DuplicateMappedFaceFiles.Clear();
}
public static void SaveFaceDistances(Property.Models.Configuration configuration, ReadOnlyCollection<SortingContainer> sortingContainers)
{
string eDistanceContentCollectionDirectory = IResult.GetResultsDateGroupDirectory(configuration, nameof(E_Distance), "([])");
if (!Directory.Exists(eDistanceContentCollectionDirectory))
_ = Directory.CreateDirectory(eDistanceContentCollectionDirectory);
#pragma warning disable
string[] results = (from l in sortingContainers select l.ToString()).ToArray();
#pragma warning restore
string eDistanceContentFileName = Path.Combine(eDistanceContentCollectionDirectory, $"{configuration.ResultAllInOne}.tvs");
File.WriteAllLines(eDistanceContentFileName, results);
}
public static void PreFilterSetFaceDistances(int maxDegreeOfParallelism, long ticks, ReadOnlyCollection<Face> distinctFilteredFaces)
{
List<Face> faces = [];
foreach (Face face in distinctFilteredFaces)
{
if (face.Mapping?.MappingFromFilterPre is null)
throw new NotSupportedException();
if (face.Mapping.MappingFromFilterPre.InSkipCollection is not null && face.Mapping.MappingFromFilterPre.InSkipCollection.Value)
continue;
if (face.Mapping.MappingFromFilterPre.IsFocusModel is not null && !face.Mapping.MappingFromFilterPre.IsFocusModel.Value)
continue;
if (face.Mapping.MappingFromFilterPre.IsFocusRelativePath is not null && !face.Mapping.MappingFromFilterPre.IsFocusRelativePath.Value)
continue;
if (face.FaceEncoding is not null && face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding _)
continue;
faces.Add(face);
}
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
string message = $") {faces.Count:000} Load Face Encoding - {totalSeconds} total second(s)";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(faces.Count, message, options);
_ = Parallel.For(0, faces.Count, parallelOptions, (i, state) =>
{
Face face = faces[i];
FaceRecognitionDotNet.FaceEncoding faceEncoding;
if (face.FaceEncoding is null || face.Mapping?.MappingFromLocation is null)
throw new NotSupportedException();
progressBar.Tick();
faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding);
FaceDistance faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, face.Mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, face.Mapping.MappingFromFilterPost, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromLocation.WholePercentages);
lock (face)
face.SetFaceDistance(faceDistance);
});
}
private static List<SortingContainer> GetSortingContainers(Map.Models.Configuration mapConfiguration, IDistanceLimits distanceLimits, Face face, FaceDistance faceDistanceEncoding, List<Sorting> sortingCollection)
{
List<SortingContainer> results = [];
SortingContainer sortingContainer;
int days = 0, distance = 0;
Sorting[] collection = Shared.Models.Stateless.Methods.ISorting.Sort(sortingCollection);
foreach (Sorting sorting in collection)
{
if (face.Mapping?.MappingFromLocation is null || faceDistanceEncoding.WholePercentages is null)
throw new NotSupportedException();
if (!mapConfiguration.SaveSortingWithoutPerson && face.Mapping.MappingFromPerson is null)
continue;
if (sorting.DaysDelta > distanceLimits.RangeDaysDeltaTolerance)
{
days += 1;
continue;
}
if (sorting.DistancePermyriad > distanceLimits.FaceDistancePermyriad)
{
distance += 1;
continue;
}
sortingContainer = new(sorting, face.Mapping);
results.Add(sortingContainer);
if (results.Count >= distanceLimits.SortingMaximumPerFaceShouldBeHigh)
break;
}
distanceLimits.AddCounts(days, distance);
return results;
}
private static List<Sorting> GetSortingCollection(Map.Models.MapLogic mapLogic, ReadOnlyCollection<FaceDistance> faceDistanceEncodings, int i, Face face, FaceDistance faceDistanceEncoding)
{
List<Sorting> results;
List<FaceDistance> faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding);
results = mapLogic.GetSortingCollection(i, face, faceDistanceEncoding, faceDistanceLengths);
return results;
}
public static ReadOnlyCollection<FaceDistanceContainer> GetFaceDistanceContainers(ReadOnlyCollection<Face> distinctFilteredFaces)
{
ReadOnlyCollection<FaceDistanceContainer> results;
FaceDistance faceDistance;
FaceDistanceContainer faceDistanceContainer;
List<FaceDistanceContainer> collection = [];
foreach (Face face in distinctFilteredFaces)
{
if (face.Mapping?.MappingFromLocation is null)
throw new NotSupportedException();
if (face.FaceDistance?.Encoding is not FaceRecognitionDotNet.FaceEncoding faceEncoding)
continue;
faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, face.Mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, face.Mapping.MappingFromFilterPost, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromLocation.WholePercentages);
faceDistanceContainer = new(face, faceDistance);
collection.Add(faceDistanceContainer);
}
results = new(collection.ToArray());
return results;
}
public static FaceDistanceContainer[] FilteredPostLoadFaceDistanceContainers(Map.Models.MapLogic mapLogic, ReadOnlyCollection<FaceDistanceContainer> faceDistanceContainers, long? skipOlderThan, DistanceLimits distanceLimits)
{
List<FaceDistanceContainer> results = [];
foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers)
{
if (faceDistanceContainer.FaceDistance is null || faceDistanceContainer.Face.Mapping?.MappingFromLocation is null)
throw new NotSupportedException();
if (skipOlderThan is not null && faceDistanceContainer.FaceDistance.DateTimeOriginalThenMinimumDateTime.Ticks < skipOlderThan.Value)
continue;
if (faceDistanceContainer.Face.Mapping.MappingFromLocation.ConfidencePercent < distanceLimits.FaceConfidencePercent)
continue;
if (faceDistanceContainer.Face.Mapping.MappingFromLocation.AreaPermyriad < distanceLimits.FaceAreaPermyriad)
continue;
if (faceDistanceContainer.Face.Mapping.MappingFromFilterPre.InSkipCollection is not null && faceDistanceContainer.Face.Mapping.MappingFromFilterPre.InSkipCollection.Value)
// throw new NotSupportedException(nameof(PreFilterSetFaceDistances));
continue;
if (faceDistanceContainer.Face.Mapping.MappingFromFilterPre.IsFocusModel is not null && !faceDistanceContainer.Face.Mapping.MappingFromFilterPre.IsFocusModel.Value)
continue;
if (faceDistanceContainer.Face.Mapping.MappingFromFilterPre.IsFocusRelativePath is not null && !faceDistanceContainer.Face.Mapping.MappingFromFilterPre.IsFocusRelativePath.Value)
continue;
if (faceDistanceContainer.Face.Mapping.MappingFromFilterPost.InSkipCollection is not null && faceDistanceContainer.Face.Mapping.MappingFromFilterPost.InSkipCollection.Value)
continue;
if (faceDistanceContainer.Face.Mapping.MappingFromFilterPost.IsFocusPerson is not null && !faceDistanceContainer.Face.Mapping.MappingFromFilterPost.IsFocusPerson.Value)
continue;
results.Add(faceDistanceContainer);
}
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)
{
List<SortingContainer> results = [];
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
string message = $") {filteredFaceDistanceContainers.Length:000} Get Sorting Containers Then Set Face Mapping Sorting Collection - {totalSeconds} total second(s)";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(filteredFaceDistanceContainers.Length, message, options);
_ = Parallel.For(0, filteredFaceDistanceContainers.Length, parallelOptions, (i, state) =>
{
progressBar.Tick();
Face face = filteredFaceDistanceContainers[i].Face;
FaceDistance faceDistanceEncoding = filteredFaceDistanceContainers[i].FaceDistance;
List<Sorting> sortingCollection = GetSortingCollection(mapLogic, faceDistanceEncodings, i, face, faceDistanceEncoding);
if (sortingCollection.Count == 0)
return;
List<SortingContainer> sortingContainers = GetSortingContainers(mapConfiguration, distanceLimits, face, faceDistanceEncoding, sortingCollection);
if (sortingContainers.Count > 0)
{
lock (results)
results.AddRange(sortingContainers);
}
});
if (distanceLimits is not null && distanceLimits.RangeDaysDeltaTargetLessThenUpper)
results = Shared.Models.Stateless.Methods.ISortingContainer.Sort(results);
else
results = Shared.Models.Stateless.Methods.ISortingContainer.SortUsingDaysDelta(results);
return new(results);
}
private static ReadOnlyCollection<RelationContainer> GetRelationCollections(int faceDistancePermyriad, int locationContainerDistanceTake, float distanceTolerance, List<Record> records)
{
List<RelationContainer> results = [];
string fileName;
FileHolder fileHolder;
int distancePermyriad;
List<string> files = [];
long ticks = DateTime.Now.Ticks;
FaceDistance? faceDistanceEncoding;
List<Relation> mappedRelations;
List<FaceDistance> faceDistanceLengths;
List<FaceDistance> faceDistanceEncodings = [];
foreach (Record record in records)
{
files.Add(record.FilePath.FullName);
faceDistanceEncodings.Add(new(record.FaceRecognitionDotNetFaceEncoding));
}
foreach (Record record in records)
{
mappedRelations = [];
fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(record.FilePath.FullName);
if (files.Count > 1)
{
faceDistanceEncoding = new(record.FaceRecognitionDotNetFaceEncoding);
if (faceDistanceEncoding is null)
throw new NullReferenceException(nameof(faceDistanceEncoding));
faceDistanceLengths = FaceRecognition.FaceDistances(new(faceDistanceEncodings), faceDistanceEncoding);
for (int i = 0; i < faceDistanceLengths.Count; i++)
{
fileName = Path.GetFileName(files[i]);
if (fileName == fileHolder.Name)
continue;
FaceDistance faceDistance = faceDistanceLengths[i];
if (faceDistance.Length is null || faceDistance.Length.Value > distanceTolerance)
continue;
distancePermyriad = (int)(faceDistance.Length.Value * faceDistancePermyriad);
mappedRelations.Add(new(distancePermyriad, files[i]));
}
}
mappedRelations = (from l in mappedRelations orderby l.DistancePermyriad select l).Take(locationContainerDistanceTake).ToList();
results.Add(new(fileHolder, new(mappedRelations)));
}
return new(results);
}
ReadOnlyCollection<RelationContainer> IDistance.GetRelationContainers(int faceDistancePermyriad, int locationContainerDistanceTake, float locationContainerDistanceTolerance, ReadOnlyCollection<LocationContainer> locationContainers)
{
ReadOnlyCollection<RelationContainer> result;
string? json;
List<Record> records = [];
Shared.Models.FaceEncoding? modelsFaceEncoding;
FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding;
foreach (LocationContainer locationContainer in locationContainers)
{
json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.ExifDirectory);
if (json is null)
continue;
modelsFaceEncoding = JsonSerializer.Deserialize<Shared.Models.FaceEncoding>(json);
if (modelsFaceEncoding is null)
throw new NotSupportedException();
faceRecognitionDotNetFaceEncoding = FaceRecognition.LoadFaceEncoding(modelsFaceEncoding.RawEncoding);
records.Add(new(locationContainer.FilePath, faceRecognitionDotNetFaceEncoding));
}
result = GetRelationCollections(faceDistancePermyriad, locationContainerDistanceTake, locationContainerDistanceTolerance, records);
return result;
}
}