254 lines
13 KiB
C#
254 lines
13 KiB
C#
using ShellProgressBar;
|
|
using View_by_Distance.FaceRecognitionDotNet;
|
|
using View_by_Distance.Map.Models;
|
|
using View_by_Distance.Shared.Models;
|
|
|
|
namespace View_by_Distance.Distance.Models;
|
|
|
|
public class MapLogicSupport : Shared.Models.Methods.IMapLogicSupport
|
|
{
|
|
|
|
private int _Area;
|
|
private int _Days;
|
|
private int _Distance;
|
|
private int _Confidence;
|
|
|
|
private readonly double _FaceAreaPermille;
|
|
private readonly double _RangeDaysDeltaTolerance;
|
|
private readonly double _FaceConfidencePercent;
|
|
private readonly double _FaceDistancePermyriad;
|
|
private readonly int _SortingMaximumPerFaceShouldBeHigh;
|
|
private readonly bool _RangeDaysDeltaTargetLessThenUpper;
|
|
|
|
public MapLogicSupport(int faceConfidencePercent,
|
|
int faceDistancePermyriad,
|
|
int[] rangeDaysDeltaTolerance,
|
|
double[] rangeDistanceTolerance,
|
|
int[] rangeFaceAreaPermilleTolerance,
|
|
double[] rangeFaceConfidence,
|
|
int sortingMaximumPerFaceShouldBeHigh,
|
|
int? useFiltersCounter = null)
|
|
{
|
|
_SortingMaximumPerFaceShouldBeHigh = sortingMaximumPerFaceShouldBeHigh;
|
|
_RangeDaysDeltaTargetLessThenUpper = rangeDaysDeltaTolerance[1] > rangeDaysDeltaTolerance[2];
|
|
if (useFiltersCounter is null)
|
|
{
|
|
_FaceAreaPermille = rangeFaceAreaPermilleTolerance[1];
|
|
_RangeDaysDeltaTolerance = rangeDaysDeltaTolerance[1];
|
|
_FaceConfidencePercent = faceConfidencePercent * rangeFaceConfidence[1];
|
|
_FaceDistancePermyriad = faceDistancePermyriad * rangeDistanceTolerance[1];
|
|
}
|
|
else
|
|
{
|
|
_RangeDaysDeltaTolerance = ((rangeDaysDeltaTolerance[2] - rangeDaysDeltaTolerance[0]) * 0.01 * useFiltersCounter.Value) + rangeDaysDeltaTolerance[1];
|
|
_FaceConfidencePercent = faceConfidencePercent * ((rangeFaceConfidence[2] - rangeFaceConfidence[0]) * 0.01 * useFiltersCounter.Value) + rangeFaceConfidence[1];
|
|
_FaceAreaPermille = ((rangeFaceAreaPermilleTolerance[2] - rangeFaceAreaPermilleTolerance[0]) * 0.01 * useFiltersCounter.Value) + rangeFaceAreaPermilleTolerance[1];
|
|
_FaceDistancePermyriad = faceDistancePermyriad * ((rangeDistanceTolerance[2] - rangeDistanceTolerance[0]) * 0.01 * useFiltersCounter.Value) + rangeDistanceTolerance[1];
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
private List<SortingContainer> GetSortingContainers(Configuration mapConfiguration, Face face, FaceDistance faceDistanceEncoding, List<Sorting> sortingCollection)
|
|
{
|
|
List<SortingContainer> results = new();
|
|
SortingContainer sortingContainer;
|
|
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 > _RangeDaysDeltaTolerance)
|
|
{
|
|
_Days += 1;
|
|
continue;
|
|
}
|
|
if (sorting.DistancePermyriad > _FaceDistancePermyriad)
|
|
{
|
|
_Distance += 1;
|
|
continue;
|
|
}
|
|
if (face.Mapping.MappingFromLocation.ConfidencePercent < _FaceConfidencePercent)
|
|
{
|
|
_Confidence += 1;
|
|
continue;
|
|
}
|
|
if (face.Mapping.MappingFromLocation.AreaPermille < _FaceAreaPermille)
|
|
{
|
|
_Area += 1;
|
|
continue;
|
|
}
|
|
sortingContainer = new(face.Mapping, sorting);
|
|
results.Add(sortingContainer);
|
|
if (results.Count >= _SortingMaximumPerFaceShouldBeHigh)
|
|
break;
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static List<Sorting> GetSortingCollection(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;
|
|
}
|
|
|
|
private 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, faceEncoding, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromItem.MinimumDateTime, face.Mapping.MappingFromLocation.NormalizedRectangle);
|
|
faceDistanceContainer = new(face, faceDistance);
|
|
collection.Add(faceDistanceContainer);
|
|
}
|
|
results = collection.ToArray();
|
|
return results;
|
|
}
|
|
|
|
private 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 SortingContainer[] SetFaceMappingSortingCollectionThenGetSortingContainers(int maxDegreeOfParallelism, Configuration mapConfiguration, long ticks, MapLogic mapLogic, List<Face> distinctFilteredFaces, List<FaceDistanceContainer> missingFaceDistanceContainers)
|
|
{
|
|
SortingContainer[] results;
|
|
List<SortingContainer> collection = new();
|
|
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
|
|
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
|
|
FaceDistanceContainer[] faceDistanceContainers = GetFaceDistanceContainers(distinctFilteredFaces);
|
|
List<FaceDistance> faceDistanceEncodings = GetFaceDistanceEncodings(faceDistanceContainers, missingFaceDistanceContainers);
|
|
string message = $") {faceDistanceContainers.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(faceDistanceContainers.Length, message, options);
|
|
_ = Parallel.For(0, faceDistanceContainers.Length, parallelOptions, (i, state) =>
|
|
{
|
|
progressBar.Tick();
|
|
FaceDistance faceDistanceEncoding = faceDistanceContainers[i].FaceDistance;
|
|
if (mapLogic.Used(faceDistanceEncoding))
|
|
return;
|
|
Face face = faceDistanceContainers[i].Face;
|
|
if (face.Mapping is null)
|
|
throw new NotSupportedException();
|
|
List<Sorting> sortingCollection = GetSortingCollection(mapLogic, faceDistanceEncodings, i, faceDistanceEncoding);
|
|
if (!sortingCollection.Any())
|
|
return;
|
|
List<SortingContainer> sortingContainers = GetSortingContainers(mapConfiguration, face, faceDistanceEncoding, sortingCollection);
|
|
if (sortingContainers.Any())
|
|
{
|
|
lock (collection)
|
|
collection.AddRange(sortingContainers);
|
|
}
|
|
});
|
|
if (!collection.Any())
|
|
results = Array.Empty<SortingContainer>();
|
|
else
|
|
{
|
|
if (_RangeDaysDeltaTargetLessThenUpper)
|
|
results = Shared.Models.Stateless.Methods.ISortingContainer.Sort(collection);
|
|
else
|
|
results = Shared.Models.Stateless.Methods.ISortingContainer.SortUsingDaysDelta(collection);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public static Mapping[] GetSelectedMappingCollection(List<Face> distinctFilteredFaces)
|
|
{
|
|
Mapping[] results;
|
|
IEnumerable<Mapping> collection = from l in distinctFilteredFaces orderby l.Mapping?.MappingFromItem.Id select l.Mapping;
|
|
results = (from l in collection where l is not null select l).ToArray();
|
|
return 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, faceEncoding, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromItem.MinimumDateTime, face.Mapping.MappingFromLocation.NormalizedRectangle);
|
|
lock (face)
|
|
face.SetFaceDistance(faceDistance);
|
|
});
|
|
}
|
|
|
|
public static Dictionary<int, Dictionary<int, Mapping>> GetIdToNormalizedRectangleToFace(Mapping[] mappingCollection)
|
|
{
|
|
Dictionary<int, Dictionary<int, Mapping>> results = new();
|
|
Dictionary<int, Mapping>? keyValuePairs;
|
|
foreach (Mapping mapping in mappingCollection)
|
|
{
|
|
if (mapping.MappingFromLocation is null)
|
|
continue;
|
|
if (!results.TryGetValue(mapping.MappingFromItem.Id, out keyValuePairs))
|
|
{
|
|
results.Add(mapping.MappingFromItem.Id, new());
|
|
if (!results.TryGetValue(mapping.MappingFromItem.Id, out keyValuePairs))
|
|
throw new Exception();
|
|
}
|
|
if (keyValuePairs.ContainsKey(mapping.MappingFromLocation.NormalizedRectangle))
|
|
continue;
|
|
keyValuePairs.Add(mapping.MappingFromLocation.NormalizedRectangle, mapping);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
string Shared.Models.Methods.IMapLogicSupport.GetCounts()
|
|
{
|
|
string result;
|
|
List<(int Value, string Name)> results = new()
|
|
{
|
|
new(_Area, nameof(_Area)),
|
|
new(_Confidence, nameof(_Confidence)),
|
|
new(_Days, nameof(_Days)),
|
|
new(_Distance, nameof(_Distance))
|
|
};
|
|
result = string.Join(' ', from l in results orderby l.Value descending select $"{l.Name}_{l.Value};");
|
|
return result;
|
|
}
|
|
|
|
} |