Split out classes
This commit is contained in:
52
Distance/Distance.csproj
Normal file
52
Distance/Distance.csproj
Normal file
@ -0,0 +1,52 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>library</OutputType>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PackageId>Phares.View.by.Distance.Distance</PackageId>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<Version>6.0.100.1</Version>
|
||||
<Authors>Mike Phares</Authors>
|
||||
<Company>Phares</Company>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
|
||||
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
|
||||
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsWindows)'=='true'">
|
||||
<DefineConstants>Windows</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsOSX)'=='true'">
|
||||
<DefineConstants>OSX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsLinux)'=='true'">
|
||||
<DefineConstants>Linux</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
|
||||
<SupportedPlatform Include="browser" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Humanizer.Core" Version="2.13.14" />
|
||||
<PackageReference Include="MetadataExtractor" Version="2.7.1" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="ShellProgressBar" Version="5.1.0" />
|
||||
<PackageReference Include="WindowsShortcutFactory" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />
|
||||
<ProjectReference Include="..\Property\Property.csproj" />
|
||||
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
|
||||
<ProjectReference Include="..\Map\Map.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
10
Distance/Models/Stateless/SerilogExtensionMethods.cs
Normal file
10
Distance/Models/Stateless/SerilogExtensionMethods.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace View_by_Distance.Distance.Models.Stateless;
|
||||
|
||||
internal static class SerilogExtensionMethods
|
||||
{
|
||||
|
||||
internal static void Warn(this Serilog.ILogger log, string messageTemplate) => log.Warning(messageTemplate);
|
||||
|
||||
internal static void Info(this Serilog.ILogger log, string messageTemplate) => log.Information(messageTemplate);
|
||||
|
||||
}
|
240
Distance/Models/_E_Distance.cs
Normal file
240
Distance/Models/_E_Distance.cs
Normal file
@ -0,0 +1,240 @@
|
||||
using ShellProgressBar;
|
||||
using System.Text.Json;
|
||||
using View_by_Distance.FaceRecognitionDotNet;
|
||||
using View_by_Distance.Map.Models;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
|
||||
namespace View_by_Distance.Distance.Models;
|
||||
|
||||
public class E_Distance : Shared.Models.Methods.IFaceDistance
|
||||
{
|
||||
|
||||
public static void SaveFaceDistances(Property.Models.Configuration propertyConfiguration, string eResultsFullGroupDirectory, SortingContainer[] sortingContainers)
|
||||
{
|
||||
string eDistanceContentCollectionDirectory = Path.Combine(eResultsFullGroupDirectory, "([])");
|
||||
if (!Directory.Exists(eDistanceContentCollectionDirectory))
|
||||
_ = Directory.CreateDirectory(eDistanceContentCollectionDirectory);
|
||||
#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
|
||||
string eDistanceContentFileName = Path.Combine(eDistanceContentCollectionDirectory, $"{propertyConfiguration.ResultAllInOne}.tvs");
|
||||
File.WriteAllLines(eDistanceContentFileName, results);
|
||||
}
|
||||
|
||||
private static List<Sorting> GetSortingCollection(Configuration configuration, MapLogic mapLogic, List<FaceDistance> faceDistanceEncodings, int faceDistanceContainersLength, int i, FaceDistance faceDistanceEncoding)
|
||||
{
|
||||
List<Sorting> results;
|
||||
List<FaceDistance> 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 < configuration.FaceDistanceTolerance select true).Any();
|
||||
results = mapLogic.GetSortingCollection(i, faceDistanceEncoding, faceDistanceLengths, anyLowerThanTolerance);
|
||||
return results;
|
||||
}
|
||||
|
||||
private static List<SortingContainer> GetSortingContainers(Configuration configuration, 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 is null || faceDistanceEncoding.NormalizedPixelPercentage is null)
|
||||
throw new NotSupportedException();
|
||||
if (face.Mapping.MappingFromLocation.Confidence < configuration.FaceDistanceMinimumConfidence || sorting.DistancePermyriad > configuration.FaceDistancePermyriad || sorting.DaysDelta > configuration.SortingDaysDeltaTolerance)
|
||||
continue;
|
||||
sortingContainer = new(face, sorting);
|
||||
results.Add(sortingContainer);
|
||||
if (results.Count >= configuration.SortingMaximumPerFaceShouldBeHigh)
|
||||
break;
|
||||
}
|
||||
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 static FaceDistanceContainer[] GetFaceDistanceContainers(List<Face> selectedFilteredFaces)
|
||||
{
|
||||
FaceDistanceContainer[] results;
|
||||
FaceDistance faceDistance;
|
||||
FaceDistanceContainer faceDistanceContainer;
|
||||
List<FaceDistanceContainer> collection = new();
|
||||
foreach (Face face in selectedFilteredFaces)
|
||||
{
|
||||
if (face.Mapping is null)
|
||||
throw new NotSupportedException();
|
||||
if (face.FaceDistance?.Encoding is not FaceRecognitionDotNet.FaceEncoding faceEncoding)
|
||||
continue;
|
||||
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;
|
||||
}
|
||||
|
||||
public static SortingContainer[] SetFaceMappingSortingCollectionThenGetSortingContainers(int maxDegreeOfParallelism, Configuration configuration, long ticks, MapLogic mapLogic, List<Face> selectedFilteredFaces)
|
||||
{
|
||||
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(selectedFilteredFaces);
|
||||
List<FaceDistance> faceDistanceEncodings = GetFaceDistanceEncodings(faceDistanceContainers);
|
||||
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 };
|
||||
selectedFilteredFaces.Clear();
|
||||
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<Sorting> sortingCollection = GetSortingCollection(configuration, mapLogic, faceDistanceEncodings, faceDistanceContainers.Length, i, faceDistanceEncoding);
|
||||
List<SortingContainer> sortingContainers = GetSortingContainers(configuration, face, faceDistanceEncoding, sortingCollection);
|
||||
lock (collection)
|
||||
collection.AddRange(sortingContainers);
|
||||
lock (face)
|
||||
face.ReleaseFaceDistance();
|
||||
});
|
||||
results = Shared.Models.Stateless.Methods.ISortingContainer.Sort(collection);
|
||||
return results;
|
||||
}
|
||||
|
||||
public static List<Face> GetSelectedFilteredFaces(List<Face> distinctFilteredFaces)
|
||||
{
|
||||
List<Face> results;
|
||||
Face[] orderedFilteredFaces = (from l in distinctFilteredFaces orderby l.Mapping is not null, l.Mapping?.MappingFromItem.MinimumDateTime descending select l).ToArray();
|
||||
results = orderedFilteredFaces.ToList();
|
||||
return results;
|
||||
}
|
||||
|
||||
public static void SetFaceDistances(int maxDegreeOfParallelism, long ticks, List<Face> selectedFilteredFaces)
|
||||
{
|
||||
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
|
||||
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
|
||||
string message = $") {selectedFilteredFaces.Count:000} Load Face Encoding - {totalSeconds} total second(s)";
|
||||
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
|
||||
using ProgressBar progressBar = new(selectedFilteredFaces.Count, message, options);
|
||||
_ = Parallel.For(0, selectedFilteredFaces.Count, parallelOptions, (i, state) =>
|
||||
{
|
||||
progressBar.Tick();
|
||||
FaceDistance faceDistance;
|
||||
Face face = selectedFilteredFaces[i];
|
||||
FaceRecognitionDotNet.FaceEncoding faceEncoding;
|
||||
if (face.FaceEncoding is null || face.Mapping is null)
|
||||
throw new NotSupportedException();
|
||||
faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding);
|
||||
faceDistance = new(face.Mapping.MappingFromLocation.Confidence, faceEncoding, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromItem.MinimumDateTime, face.Mapping.MappingFromLocation.NormalizedPixelPercentage);
|
||||
lock (face)
|
||||
face.FaceDistanceAdd(faceDistance);
|
||||
});
|
||||
}
|
||||
|
||||
List<Face> Shared.Models.Methods.IFaceDistance.GetMatchingFaces(double faceDistanceTolerance, string checkFile, List<Face> faces)
|
||||
{
|
||||
List<Face> results = new();
|
||||
Face face;
|
||||
FaceDistance faceDistanceLength;
|
||||
string json = File.ReadAllText(checkFile);
|
||||
List<(Face Face, double Length)> collection = new();
|
||||
Shared.Models.FaceEncoding? modelsFaceEncoding = JsonSerializer.Deserialize<Shared.Models.FaceEncoding>(json);
|
||||
if (modelsFaceEncoding is null)
|
||||
throw new NotSupportedException();
|
||||
FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding = FaceRecognition.LoadFaceEncoding(modelsFaceEncoding.RawEncoding);
|
||||
FaceDistance faceDistanceEncoding = new(faceRecognitionDotNetFaceEncoding);
|
||||
FaceDistanceContainer[] faceDistanceContainers = GetFaceDistanceContainers(faces);
|
||||
int faceDistanceContainersLength = faceDistanceContainers.Length;
|
||||
if (faceDistanceContainersLength != faces.Count)
|
||||
throw new NotSupportedException();
|
||||
List<FaceDistance> faceDistanceEncodings = GetFaceDistanceEncodings(faceDistanceContainers);
|
||||
if (faceDistanceEncodings.Count != faces.Count)
|
||||
throw new NotSupportedException();
|
||||
List<FaceDistance> faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding);
|
||||
if (faceDistanceLengths.Count != faceDistanceContainersLength)
|
||||
throw new NotSupportedException();
|
||||
for (int i = 0; i < faces.Count; i++)
|
||||
{
|
||||
face = faces[i];
|
||||
faceDistanceLength = faceDistanceLengths[i];
|
||||
if (face.Mapping is null || faceDistanceLength.Length is null)
|
||||
throw new NotSupportedException();
|
||||
if (faceDistanceLength.Length.Value > faceDistanceTolerance)
|
||||
continue;
|
||||
collection.Add(new(face, faceDistanceLength.Length.Value));
|
||||
}
|
||||
if (collection.Any())
|
||||
{
|
||||
collection = (from l in collection orderby l.Length select l).ToList();
|
||||
results.Add(collection[0].Face);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
void Shared.Models.Methods.IFaceDistance.SavePossiblyNewPersonContainers(IPropertyConfiguration propertyConfiguration, string personBirthdayFormat, string facesFileNameExtension, string a2PeopleSingletonDirectory, Dictionary<long, PersonContainer> personKeyToPersonContainer, List<(string[], PersonContainer)> possiblyNewPersonDisplayDirectoryNamesAndPersonContainer)
|
||||
{
|
||||
char @char;
|
||||
string json;
|
||||
string[] files;
|
||||
string checkFile;
|
||||
string[] segments;
|
||||
const int zero = 0;
|
||||
string personKeyFormatted;
|
||||
string personDisplayDirectory;
|
||||
PersonBirthday personBirthday;
|
||||
string personDisplayDirectoryName;
|
||||
string checkPersonDisplayDirectory;
|
||||
string checkPersonKeyFormattedDirectory;
|
||||
char[] chars = Shared.Models.Stateless.Methods.IAge.GetChars();
|
||||
JsonSerializerOptions jsonSerializerOptions = new() { WriteIndented = true };
|
||||
foreach ((string[] personDisplayDirectoryNames, PersonContainer personContainer) in possiblyNewPersonDisplayDirectoryNamesAndPersonContainer)
|
||||
{
|
||||
if (personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any())
|
||||
continue;
|
||||
personBirthday = personContainer.Birthdays[zero];
|
||||
personDisplayDirectoryName = personDisplayDirectoryNames[^1];
|
||||
personDisplayDirectory = Path.Combine(personDisplayDirectoryNames);
|
||||
personKeyFormatted = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthdayFormat, personBirthday);
|
||||
segments = personDisplayDirectoryName.Split(chars);
|
||||
if (segments.Length != 2)
|
||||
@char = '_';
|
||||
else
|
||||
@char = personDisplayDirectoryName[segments[zero].Length];
|
||||
checkPersonDisplayDirectory = Path.Combine(a2PeopleSingletonDirectory, @char.ToString(), personDisplayDirectoryName);
|
||||
checkPersonKeyFormattedDirectory = Path.Combine(checkPersonDisplayDirectory, personKeyFormatted);
|
||||
if (Directory.Exists(checkPersonKeyFormattedDirectory))
|
||||
continue;
|
||||
_ = Directory.CreateDirectory(checkPersonKeyFormattedDirectory);
|
||||
checkFile = Path.Combine(checkPersonKeyFormattedDirectory, $"{personKeyFormatted}.json");
|
||||
json = JsonSerializer.Serialize(personContainer.Person, jsonSerializerOptions);
|
||||
_ = Shared.Models.Stateless.Methods.IPath.WriteAllText(checkFile, json, updateDateWhenMatches: false, compareBeforeWrite: true);
|
||||
if (!Directory.Exists(personDisplayDirectory))
|
||||
continue;
|
||||
files = Directory.GetFiles(personDisplayDirectory, $"*{facesFileNameExtension}", SearchOption.TopDirectoryOnly);
|
||||
foreach (string file in files)
|
||||
{
|
||||
checkFile = Path.Combine(checkPersonDisplayDirectory, Path.GetFileName(file));
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
File.Copy(files[0], checkFile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user