using ShellProgressBar; using View_by_Distance.FaceRecognitionDotNet; using View_by_Distance.Map.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless; namespace View_by_Distance.Instance.Models; internal class E_Distance { private static void SaveFaceDistances(string eDistanceContentFileName, SortingContainer[] sortingContainers) { #pragma warning disable string[] results = (from l in sortingContainers select string.Concat(l.Sorting.WithinRange, '\t', l.Sorting.DistancePermyriad, '\t', l.Sorting.DaysDelta, '\t', l.Sorting.Id, '\t', l.Sorting.NormalizedPixelPercentage, '\t', l.Sorting.Older, '\t', l.Face.Mapping.MappingFromItem.Id, '\t', l.Face.Mapping.MappingFromLocation.NormalizedPixelPercentage)).ToArray(); #pragma warning restore File.WriteAllLines(eDistanceContentFileName, results); } private static List GetSortingCollection(MapLogic mapLogic, List faceDistanceEncodings, int faceDistanceContainersLength, int i, FaceDistance faceDistanceEncoding) { List results; List faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding); if (faceDistanceLengths.Count != faceDistanceContainersLength) throw new NotSupportedException(); bool anyLowerThanTolerance = (from l in faceDistanceLengths where l.Length is not null && l.Length.Value != 0 && l.Length.Value < IFaceDistance.Tolerance select true).Any(); results = mapLogic.GetSortingCollection(i, faceDistanceEncoding, faceDistanceLengths, anyLowerThanTolerance); return results; } private static List GetSortingContainers(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 is null || faceDistanceEncoding.NormalizedPixelPercentage is null) throw new NotSupportedException(); if (face.Mapping.MappingFromLocation.Confidence < IFaceDistance.MinimumConfidence || sorting.DistancePermyriad > IFaceDistance.Permyriad || sorting.DaysDelta > ISorting.DaysDeltaTolerance) continue; sortingContainer = new(face, sorting); results.Add(sortingContainer); if (results.Count >= ISorting.MaximumPerFaceShouldBeHigh) break; } return results; } private static SortingContainer[] GetSortingContainersThenSetFaceMappingSortingCollection(int maxDegreeOfParallelism, long ticks, MapLogic mapLogic, string outputResolution, FaceDistanceContainer[] faceDistanceContainers) { SortingContainer[] results; List collection = new(); List faceDistanceEncodings = new(); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; string message = $") {faceDistanceContainers.Length:000} faceDistanceContainer(s) - {totalSeconds} total second(s) - {outputResolution}"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers) { if (faceDistanceContainer.FaceDistance.Encoding is null) continue; faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance); } using ProgressBar progressBar = new(faceDistanceContainers.Length, message, options); _ = Parallel.For(0, faceDistanceContainers.Length, parallelOptions, (i, state) => { progressBar.Tick(); Face face = faceDistanceContainers[i].Face; if (face.Mapping is null) throw new NotSupportedException(); FaceDistance faceDistanceEncoding = faceDistanceContainers[i].FaceDistance; List sortingCollection = GetSortingCollection(mapLogic, faceDistanceEncodings, faceDistanceContainers.Length, i, faceDistanceEncoding); List sortingContainers = GetSortingContainers(face, faceDistanceEncoding, sortingCollection); lock (collection) collection.AddRange(sortingContainers); lock (face) face.ReleaseFaceDistance(); }); results = Shared.Models.Stateless.Methods.ISortingContainer.Sort(collection); return results; } private static FaceDistanceContainer[] GetFaceDistanceContainers(Face[] firstFilteredFaces) { FaceDistanceContainer[] results; FaceDistance faceDistance; FaceDistanceContainer faceDistanceContainer; List collection = new(); foreach (Face face in firstFilteredFaces) { if (face.Mapping is null) throw new NotSupportedException(); if (face.FaceDistance?.Encoding is not FaceRecognitionDotNet.FaceEncoding faceEncoding) throw new NotSupportedException(); faceDistance = new(face.Mapping.MappingFromLocation.Confidence, faceEncoding, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromItem.MinimumDateTime, face.Mapping.MappingFromLocation.NormalizedPixelPercentage); faceDistanceContainer = new(face, faceDistance); collection.Add(faceDistanceContainer); } results = (from l in collection orderby l.FaceDistance.Encoding is not null select l).ToArray(); if (results.Any() && results[0].FaceDistance.Encoding is null) throw new Exception("Sorting failed!"); return results; } private static void SetFaceDistances(int maxDegreeOfParallelism, long ticks, string outputResolution, Face[] selectedFilteredFaces) { int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; string message = $") {selectedFilteredFaces.Length:000} Load Face Encoding - {totalSeconds} total second(s) - {outputResolution}"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = false }; using ProgressBar progressBar = new(selectedFilteredFaces.Length, message, options); _ = Parallel.For(0, selectedFilteredFaces.Length, parallelOptions, (i, state) => { FaceDistance faceDistance; FaceRecognitionDotNet.FaceEncoding faceEncoding; Face face = selectedFilteredFaces[i]; if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) throw new NotSupportedException(); if (face.Mapping is null) throw new NotSupportedException(); faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); faceDistance = new(face.Location.Confidence, faceEncoding, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromItem.MinimumDateTime, face.Location.NormalizedPixelPercentage.Value); lock (face) face.FaceDistanceAdd(faceDistance); }); } internal static SortingContainer[] SetPersonTicksAndSetFaceDistancesAndSetFaceMappingSortingCollectionThenGetSortingContainers(int maxDegreeOfParallelism, long ticks, MapLogic mapLogic, string outputResolution, string eDistanceContentFileName, List distinctFilteredFaces) { SortingContainer[] results; Face[] orderedFilteredFaces = (from l in distinctFilteredFaces orderby l.Mapping is not null, l.Mapping?.MappingFromItem.MinimumDateTime descending select l).ToArray(); mapLogic.SetPersonTicks(outputResolution, orderedFilteredFaces); Face[] selectedFilteredFaces = orderedFilteredFaces.Skip(ISorting.FacesToSkipAfterSortBeforeLoad).Take(ISorting.FacesToTakeAfterSortBeforeLoad).ToArray(); SetFaceDistances(maxDegreeOfParallelism, ticks, outputResolution, selectedFilteredFaces); FaceDistanceContainer[] faceDistanceContainers = GetFaceDistanceContainers(selectedFilteredFaces); results = GetSortingContainersThenSetFaceMappingSortingCollection(maxDegreeOfParallelism, ticks, mapLogic, outputResolution, faceDistanceContainers); SaveFaceDistances(eDistanceContentFileName, results); return results; } }