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 GetSortingContainers(Configuration mapConfiguration, Face face, FaceDistance faceDistanceEncoding, List sortingCollection) { List 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 GetSortingCollection(MapLogic mapLogic, List faceDistanceEncodings, int i, FaceDistance faceDistanceEncoding) { List results; List faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding); results = mapLogic.GetSortingCollection(i, faceDistanceEncoding, faceDistanceLengths); return results; } private static FaceDistanceContainer[] GetFaceDistanceContainers(List distinctFilteredFaces) { FaceDistanceContainer[] results; FaceDistance faceDistance; FaceDistanceContainer faceDistanceContainer; List 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 GetFaceDistanceEncodings(FaceDistanceContainer[] faceDistanceContainers, List missingFaceDistanceContainers) { List 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 distinctFilteredFaces, List missingFaceDistanceContainers) { SortingContainer[] results; List collection = new(); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; FaceDistanceContainer[] faceDistanceContainers = GetFaceDistanceContainers(distinctFilteredFaces); List 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 sortingCollection = GetSortingCollection(mapLogic, faceDistanceEncodings, i, faceDistanceEncoding); if (!sortingCollection.Any()) return; List sortingContainers = GetSortingContainers(mapConfiguration, face, faceDistanceEncoding, sortingCollection); if (sortingContainers.Any()) { lock (collection) collection.AddRange(sortingContainers); } }); if (!collection.Any()) results = Array.Empty(); 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 distinctFilteredFaces) { Mapping[] results; IEnumerable 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 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> GetIdToNormalizedRectangleToFace(Mapping[] mappingCollection) { Dictionary> results = new(); Dictionary? 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; } }