538 lines
28 KiB
C#
538 lines
28 KiB
C#
using ShellProgressBar;
|
|
using System.Text.Json;
|
|
using View_by_Distance.Distance.Models.Stateless;
|
|
using View_by_Distance.FaceRecognitionDotNet;
|
|
using View_by_Distance.Shared.Models;
|
|
|
|
namespace View_by_Distance.Distance.Models;
|
|
|
|
public partial class E_Distance
|
|
{
|
|
|
|
private readonly List<string> _Moved;
|
|
private readonly List<double?> _Debug;
|
|
private readonly List<string> _Renamed;
|
|
private readonly Serilog.ILogger? _Log;
|
|
private readonly int _FaceConfidencePercent;
|
|
private readonly bool _DistanceRenameToMatch;
|
|
private readonly float[] _RangeFaceConfidence;
|
|
private readonly bool _DistanceMoveUnableToMatch;
|
|
private readonly List<string> _AllMappedFaceFiles;
|
|
private readonly List<string> _AllMappedFaceFileNames;
|
|
private readonly List<string> _DuplicateMappedFaceFiles;
|
|
|
|
public E_Distance(bool distanceMoveUnableToMatch, bool distanceRenameToMatch, int faceConfidencePercent, float[] rangeFaceConfidence)
|
|
{
|
|
_Debug = new();
|
|
_Moved = new();
|
|
_Renamed = new();
|
|
_AllMappedFaceFiles = new();
|
|
_AllMappedFaceFileNames = new();
|
|
_DuplicateMappedFaceFiles = new();
|
|
_RangeFaceConfidence = rangeFaceConfidence;
|
|
_Log = Serilog.Log.ForContext<E_Distance>();
|
|
_DistanceRenameToMatch = distanceRenameToMatch;
|
|
_FaceConfidencePercent = faceConfidencePercent;
|
|
_DistanceMoveUnableToMatch = distanceMoveUnableToMatch;
|
|
}
|
|
|
|
private static void MoveUnableToMatch(string file)
|
|
{
|
|
string checkFile = string.Concat(file, ".unk");
|
|
if (File.Exists(file) && !File.Exists(checkFile))
|
|
File.Move(file, checkFile);
|
|
}
|
|
|
|
private FaceDistanceContainer[] GetFaceDistanceContainers(MappingFromItem mappingFromItem, List<Face> intersectFaces)
|
|
{
|
|
FaceDistanceContainer[] results;
|
|
int confidencePercent;
|
|
int normalizedRectangle;
|
|
FaceDistance faceDistance;
|
|
FaceDistanceContainer faceDistanceContainer;
|
|
List<FaceDistanceContainer> collection = new();
|
|
foreach (Face face in intersectFaces)
|
|
{
|
|
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
|
|
throw new NotSupportedException();
|
|
confidencePercent = Shared.Models.Stateless.Methods.ILocation.GetConfidencePercent(_FaceConfidencePercent, _RangeFaceConfidence, face.Location.Confidence);
|
|
normalizedRectangle = Shared.Models.Stateless.Methods.ILocation.GetNormalizedRectangle(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, mappingFromItem.Id, mappingFromItem.IsWrongYear, normalizedRectangle);
|
|
else
|
|
{
|
|
faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding);
|
|
faceDistance = new(confidencePercent, mappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, mappingFromItem.Id, mappingFromItem.IsWrongYear, normalizedRectangle);
|
|
lock (intersectFaces)
|
|
face.SetFaceDistance(faceDistance);
|
|
}
|
|
faceDistanceContainer = new(face, faceDistance);
|
|
collection.Add(faceDistanceContainer);
|
|
}
|
|
results = collection.ToArray();
|
|
return results;
|
|
}
|
|
|
|
private static List<FaceDistance> GetFaceDistanceEncodings(FaceDistanceContainer[] faceDistanceContainers)
|
|
{
|
|
List<FaceDistance> faceDistanceEncodings = new();
|
|
foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers)
|
|
{
|
|
if (faceDistanceContainer.FaceDistance.Encoding is null)
|
|
continue;
|
|
faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance);
|
|
}
|
|
return faceDistanceEncodings;
|
|
}
|
|
|
|
private List<(Face Face, double? Length)> GetValues(MappingFromItem mappingFromItem, List<Face> intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding)
|
|
{
|
|
List<(Face Face, double? Length)> results = new();
|
|
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();
|
|
List<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 orderby l.Length select l).Take(1).ToArray();
|
|
if (results.Any())
|
|
{
|
|
(Face _, double? length) = results.First();
|
|
_Debug.Add(length);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static List<(Face, double?)> GetMatchingFacesByFaceEncoding(Face[] filteredFaces, string? json)
|
|
{
|
|
List<(Face, double?)> results = new();
|
|
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, 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(mappingFromItem.Id, 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 = new();
|
|
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 = string.Concat(collection[i].FullName, ".dup");
|
|
if (File.Exists(checkFile))
|
|
continue;
|
|
File.Move(collection[i].FullName, checkFile);
|
|
}
|
|
}
|
|
|
|
public void LookForMatchFacesAndPossiblyRename(string facesFileNameExtension, string eDistanceContentDirectory, MappingFromItem mappingFromItem, List<Face> faces, List<LocationContainer<MetadataExtractor.Directory>> collection)
|
|
{
|
|
string? json;
|
|
string fileName;
|
|
string[] matches;
|
|
FileInfo? fileInfo;
|
|
List<Face> intersectFaces;
|
|
List<(Face, double?)> checkFaces = new();
|
|
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<MetadataExtractor.Directory>? locationContainer in collection)
|
|
{
|
|
if (_Renamed.Contains(locationContainer.File))
|
|
continue;
|
|
fileName = Path.GetFileName(locationContainer.File);
|
|
if (locationContainer.FromDistanceContent && _DuplicateMappedFaceFiles.Contains(fileName))
|
|
continue;
|
|
checkFaces.Clear();
|
|
if (!locationContainer.Directories.Any())
|
|
{
|
|
if (locationContainer.FromDistanceContent)
|
|
throw new NullReferenceException(nameof(locationContainer.Directories));
|
|
continue;
|
|
}
|
|
json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.Directories);
|
|
if (json is null)
|
|
{
|
|
if (_DistanceMoveUnableToMatch)
|
|
MoveUnableToMatch(locationContainer.File);
|
|
continue;
|
|
}
|
|
if (filteredFaces.Any())
|
|
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.Any())
|
|
{
|
|
intersectFaces = Shared.Models.Stateless.Methods.ILocation.FilterByIntersect(filteredFaces, locationContainer.NormalizedRectangle);
|
|
if (intersectFaces.Any())
|
|
checkFaces.AddRange(GetClosestFaceByDistanceIgnoringTolerance(mappingFromItem, intersectFaces, modelsFaceEncoding));
|
|
}
|
|
}
|
|
if (!checkFaces.Any())
|
|
{
|
|
if (_DistanceMoveUnableToMatch)
|
|
MoveUnableToMatch(locationContainer.File);
|
|
continue;
|
|
}
|
|
if (checkFaces.Count != 1)
|
|
{
|
|
if (_DistanceMoveUnableToMatch)
|
|
MoveUnableToMatch(locationContainer.File);
|
|
continue;
|
|
}
|
|
fileInfo = CheckFileThenGetFileInfo(facesFileNameExtension, mappingFromItem, locationContainer.File, checkFaces);
|
|
if (fileInfo is not null)
|
|
{
|
|
if (_DistanceRenameToMatch && fileInfo is not null)
|
|
{
|
|
if (fileInfo.Exists)
|
|
File.Delete(locationContainer.File);
|
|
else
|
|
File.Move(locationContainer.File, fileInfo.FullName);
|
|
File.WriteAllText($"{fileInfo.FullName}.old", $"{fileInfo.FullName}{Environment.NewLine}{locationContainer.File}");
|
|
_Renamed.Add(locationContainer.File);
|
|
}
|
|
continue;
|
|
}
|
|
if (_AllMappedFaceFileNames.Contains(fileName))
|
|
{
|
|
lock (_AllMappedFaceFiles)
|
|
matches = (from l in _AllMappedFaceFiles where l != locationContainer.File && Path.GetFileName(l) == fileName select l).ToArray();
|
|
if (locationContainer.FromDistanceContent && matches.Any())
|
|
AppendMatchingDuplicates(locationContainer.File, matches);
|
|
}
|
|
if (!locationContainer.FromDistanceContent)
|
|
continue;
|
|
lock (_AllMappedFaceFiles)
|
|
_AllMappedFaceFiles.Add(locationContainer.File);
|
|
lock (_AllMappedFaceFileNames)
|
|
_AllMappedFaceFileNames.Add(fileName);
|
|
}
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
if (_Log is null)
|
|
throw new NullReferenceException(nameof(_Log));
|
|
double?[] debug = (from l in _Debug where l is null or not 0 select l).ToArray();
|
|
if (debug.Any())
|
|
{
|
|
string debugMessage = $"{_Debug.Count - debug.Length} - {debug.Min()} - {_Debug.Max()}";
|
|
_Log.Info(debugMessage);
|
|
}
|
|
if (_DuplicateMappedFaceFiles.Any())
|
|
_Log.Info($"Renamed {_DuplicateMappedFaceFiles.Count} to *.dup file(s)");
|
|
if (_Moved.Any() || _Renamed.Any())
|
|
throw new NotImplementedException("Restart!");
|
|
_Debug.Clear();
|
|
_Moved.Clear();
|
|
_Renamed.Clear();
|
|
_AllMappedFaceFiles.Clear();
|
|
_AllMappedFaceFileNames.Clear();
|
|
_DuplicateMappedFaceFiles.Clear();
|
|
}
|
|
|
|
public List<FaceDistanceContainer> GetMissingFaceDistanceContainer(int maxDegreeOfParallelism, long ticks, string dFacesCollectionDirectory, Dictionary<int, Dictionary<int, PersonContainer[]>> missingIdThenNormalizedRectangleToPersonContainers)
|
|
{
|
|
List<FaceDistanceContainer> results = new();
|
|
string[] files;
|
|
List<Face>? faces;
|
|
int confidencePercent;
|
|
int normalizedRectangle;
|
|
bool? isWrongYear = null;
|
|
FaceDistance faceDistance;
|
|
List<(int id, string json)> collection = new();
|
|
FaceDistanceContainer faceDistanceContainer;
|
|
foreach (KeyValuePair<int, Dictionary<int, PersonContainer[]>> keyValuePair in missingIdThenNormalizedRectangleToPersonContainers)
|
|
{
|
|
files = Directory.GetFiles(dFacesCollectionDirectory, $"{keyValuePair.Key}*.json", SearchOption.TopDirectoryOnly);
|
|
if (files.Length != 1)
|
|
continue;
|
|
collection.Add(new(keyValuePair.Key, Shared.Models.Stateless.Methods.IFace.GetJson(files[0])));
|
|
}
|
|
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
|
|
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
|
|
string message = $") {collection.Count:000} Setting missing distance containers - {totalSeconds} total second(s)";
|
|
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
|
|
using ProgressBar progressBar = new(collection.Count, message, options);
|
|
_ = Parallel.For(0, collection.Count, parallelOptions, (i, state) =>
|
|
{
|
|
progressBar.Tick();
|
|
int id = collection[i].id;
|
|
string json = collection[i].json;
|
|
faces = JsonSerializer.Deserialize<List<Face>>(json);
|
|
if (faces is null)
|
|
throw new NullReferenceException(nameof(faces));
|
|
foreach (Face face in faces)
|
|
{
|
|
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
|
|
continue;
|
|
confidencePercent = Shared.Models.Stateless.Methods.ILocation.GetConfidencePercent(_FaceConfidencePercent, _RangeFaceConfidence, face.Location.Confidence);
|
|
normalizedRectangle = Shared.Models.Stateless.Methods.ILocation.GetNormalizedRectangle(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, face.DateTime, faceEncoding, id, isWrongYear, normalizedRectangle);
|
|
else
|
|
{
|
|
faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding);
|
|
faceDistance = new(confidencePercent, face.DateTime, faceEncoding, id, isWrongYear, normalizedRectangle);
|
|
face.SetFaceDistance(faceDistance);
|
|
}
|
|
faceDistanceContainer = new(face, faceDistance);
|
|
lock (results)
|
|
results.Add(faceDistanceContainer);
|
|
}
|
|
});
|
|
return results;
|
|
}
|
|
|
|
public static void SaveFaceDistances(Property.Models.Configuration configuration, SortingContainer[] sortingContainers)
|
|
{
|
|
string eDistanceContentCollectionDirectory = Property.Models.Stateless.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 SetFaceDistances(int maxDegreeOfParallelism, long ticks, List<Face> distinctFilteredFaces)
|
|
{
|
|
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
|
|
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
|
|
string message = $") {distinctFilteredFaces.Count:000} Load Face Encoding - {totalSeconds} total second(s)";
|
|
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
|
|
using ProgressBar progressBar = new(distinctFilteredFaces.Count, message, options);
|
|
_ = Parallel.For(0, distinctFilteredFaces.Count, parallelOptions, (i, state) =>
|
|
{
|
|
Face face = distinctFilteredFaces[i];
|
|
if (face.FaceEncoding is null || face.Mapping?.MappingFromLocation is null)
|
|
throw new NotSupportedException();
|
|
if (face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding faceEncoding)
|
|
return;
|
|
progressBar.Tick();
|
|
faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding);
|
|
FaceDistance faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, face.Mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromLocation.NormalizedRectangle);
|
|
lock (face)
|
|
face.SetFaceDistance(faceDistance);
|
|
});
|
|
}
|
|
|
|
private static List<SortingContainer> GetSortingContainers(Map.Models.Configuration mapConfiguration, Shared.Models.Methods.IDistanceLimits distanceLimits, Face face, FaceDistance faceDistanceEncoding, List<Sorting> sortingCollection)
|
|
{
|
|
List<SortingContainer> results = new();
|
|
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.NormalizedRectangle 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(face.Mapping, sorting);
|
|
results.Add(sortingContainer);
|
|
if (results.Count >= distanceLimits.SortingMaximumPerFaceShouldBeHigh)
|
|
break;
|
|
}
|
|
distanceLimits.AddCounts(days, distance);
|
|
return results;
|
|
}
|
|
|
|
private static List<Sorting> GetSortingCollection(Map.Models.MapLogic mapLogic, List<FaceDistance> faceDistanceEncodings, int i, FaceDistance faceDistanceEncoding)
|
|
{
|
|
List<Sorting> results;
|
|
List<FaceDistance> faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding);
|
|
results = mapLogic.GetSortingCollection(i, faceDistanceEncoding, faceDistanceLengths);
|
|
return results;
|
|
}
|
|
|
|
public static FaceDistanceContainer[] GetFaceDistanceContainers(List<Face> distinctFilteredFaces)
|
|
{
|
|
FaceDistanceContainer[] results;
|
|
FaceDistance faceDistance;
|
|
FaceDistanceContainer faceDistanceContainer;
|
|
List<FaceDistanceContainer> collection = new();
|
|
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.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromLocation.NormalizedRectangle);
|
|
faceDistanceContainer = new(face, faceDistance);
|
|
collection.Add(faceDistanceContainer);
|
|
}
|
|
results = collection.ToArray();
|
|
return results;
|
|
}
|
|
|
|
public static List<FaceDistance> GetFaceDistanceEncodings(FaceDistanceContainer[] faceDistanceContainers, List<FaceDistanceContainer> missingFaceDistanceContainers)
|
|
{
|
|
List<FaceDistance> faceDistanceEncodings = new();
|
|
foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers)
|
|
{
|
|
if (faceDistanceContainer.FaceDistance.Encoding is null)
|
|
continue;
|
|
faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance);
|
|
}
|
|
foreach (FaceDistanceContainer faceDistanceContainer in missingFaceDistanceContainers)
|
|
{
|
|
if (faceDistanceContainer.FaceDistance.Encoding is null)
|
|
continue;
|
|
faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance);
|
|
}
|
|
return faceDistanceEncodings;
|
|
}
|
|
|
|
public static FaceDistanceContainer[] FilteredFaceDistanceContainers(Map.Models.MapLogic mapLogic, FaceDistanceContainer[] faceDistanceContainers, long? skipOlderThan, DistanceLimits distanceLimits)
|
|
{
|
|
List<FaceDistanceContainer> results = new();
|
|
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.MappingFromFilter.IsFocusModel is not null && !faceDistanceContainer.Face.Mapping.MappingFromFilter.IsFocusModel.Value)
|
|
continue;
|
|
if (faceDistanceContainer.Face.Mapping.MappingFromFilter.IsFocusRelativePath is not null && !faceDistanceContainer.Face.Mapping.MappingFromFilter.IsFocusRelativePath.Value)
|
|
continue;
|
|
if (faceDistanceContainer.Face.Mapping.MappingFromFilter.IsIgnoreRelativePath is not null && faceDistanceContainer.Face.Mapping.MappingFromFilter.IsIgnoreRelativePath.Value)
|
|
continue;
|
|
if (mapLogic.Used(faceDistanceContainer.FaceDistance))
|
|
continue;
|
|
results.Add(faceDistanceContainer);
|
|
}
|
|
return results.ToArray();
|
|
}
|
|
|
|
public static SortingContainer[] SetFaceMappingSortingCollectionThenGetSortingContainers(int maxDegreeOfParallelism, Map.Models.Configuration mapConfiguration, long ticks, Map.Models.MapLogic mapLogic, Shared.Models.Methods.IDistanceLimits distanceLimits, List<FaceDistance> faceDistanceEncodings, FaceDistanceContainer[] filteredFaceDistanceContainers)
|
|
{
|
|
SortingContainer[] results;
|
|
List<SortingContainer> collection = new();
|
|
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();
|
|
FaceDistance faceDistanceEncoding = filteredFaceDistanceContainers[i].FaceDistance;
|
|
Face face = filteredFaceDistanceContainers[i].Face;
|
|
List<Sorting> sortingCollection = GetSortingCollection(mapLogic, faceDistanceEncodings, i, faceDistanceEncoding);
|
|
if (!sortingCollection.Any())
|
|
return;
|
|
List<SortingContainer> sortingContainers = GetSortingContainers(mapConfiguration, distanceLimits, face, faceDistanceEncoding, sortingCollection);
|
|
if (sortingContainers.Any())
|
|
{
|
|
lock (collection)
|
|
collection.AddRange(sortingContainers);
|
|
}
|
|
});
|
|
if (!collection.Any())
|
|
results = Array.Empty<SortingContainer>();
|
|
else
|
|
{
|
|
if (distanceLimits.RangeDaysDeltaTargetLessThenUpper)
|
|
results = Shared.Models.Stateless.Methods.ISortingContainer.Sort(collection);
|
|
else
|
|
results = Shared.Models.Stateless.Methods.ISortingContainer.SortUsingDaysDelta(collection);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
} |