527 lines
27 KiB
C#
527 lines
27 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Reflection;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using View_by_Distance.FaceRecognitionDotNet;
|
|
using View_by_Distance.Metadata.Models;
|
|
using View_by_Distance.Metadata.Models.Stateless.Methods;
|
|
using View_by_Distance.Property.Models;
|
|
using View_by_Distance.Resize.Models;
|
|
using View_by_Distance.Shared.Models;
|
|
using View_by_Distance.Shared.Models.Properties;
|
|
using View_by_Distance.Shared.Models.Stateless;
|
|
using View_by_Distance.Shared.Models.Stateless.Methods;
|
|
|
|
namespace View_by_Distance.Face.Models;
|
|
|
|
/// <summary>
|
|
// List<D_Faces>
|
|
/// </summary>
|
|
public class D_Face : IFaceD
|
|
{
|
|
|
|
protected readonly string _FileNameExtension;
|
|
public string FileNameExtension => _FileNameExtension;
|
|
|
|
protected readonly string _HiddenFileNameExtension;
|
|
public string HiddenFileNameExtension => _HiddenFileNameExtension;
|
|
|
|
private readonly Model _Model;
|
|
private readonly string _ArgZero;
|
|
private readonly bool _OverrideForFaceImages;
|
|
private readonly bool _LoadPhotoPrismLocations;
|
|
private readonly ImageCodecInfo _ImageCodecInfo;
|
|
private readonly ModelParameter _ModelParameter;
|
|
private readonly PredictorModel _PredictorModel;
|
|
private readonly bool _CheckDFaceAndUpWriteDates;
|
|
private readonly bool _PropertiesChangedForFaces;
|
|
private readonly ConstructorInfo _ConstructorInfo;
|
|
private readonly float _RectangleIntersectMinimum;
|
|
private readonly int _FaceDistanceHiddenImageFactor;
|
|
private readonly EncoderParameters _EncoderParameters;
|
|
private readonly ImageCodecInfo _HiddenImageCodecInfo;
|
|
private readonly bool _ForceFaceLastWriteTimeToCreationTime;
|
|
private readonly EncoderParameters _HiddenEncoderParameters;
|
|
private readonly IPropertyConfiguration _PropertyConfiguration;
|
|
private readonly JsonSerializerOptions _WriteIndentedAndWhenWritingNull;
|
|
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultContentFileGroups;
|
|
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultCollectionFileGroups;
|
|
|
|
public D_Face(
|
|
string argZero,
|
|
IPropertyConfiguration propertyConfiguration,
|
|
bool checkDFaceAndUpWriteDates,
|
|
EncoderParameters encoderParameters,
|
|
int faceDistanceHiddenImageFactor,
|
|
string filenameExtension,
|
|
bool forceFaceLastWriteTimeToCreationTime,
|
|
EncoderParameters hiddenEncoderParameters,
|
|
string hiddenFileNameExtension,
|
|
ImageCodecInfo hiddenImageCodecInfo,
|
|
ImageCodecInfo imageCodecInfo,
|
|
bool loadPhotoPrismLocations,
|
|
string modelDirectory,
|
|
string modelName,
|
|
bool overrideForFaceImages,
|
|
string predictorModelName,
|
|
bool propertiesChangedForFaces,
|
|
float[] rectangleIntersectMinimums)
|
|
{
|
|
_ArgZero = argZero;
|
|
_ImageCodecInfo = imageCodecInfo;
|
|
_EncoderParameters = encoderParameters;
|
|
_FileNameExtension = filenameExtension;
|
|
_HiddenImageCodecInfo = hiddenImageCodecInfo;
|
|
_OverrideForFaceImages = overrideForFaceImages;
|
|
_PropertyConfiguration = propertyConfiguration;
|
|
_HiddenEncoderParameters = hiddenEncoderParameters;
|
|
_HiddenFileNameExtension = hiddenFileNameExtension;
|
|
_LoadPhotoPrismLocations = loadPhotoPrismLocations;
|
|
_CheckDFaceAndUpWriteDates = checkDFaceAndUpWriteDates;
|
|
_PropertiesChangedForFaces = propertiesChangedForFaces;
|
|
_RectangleIntersectMinimum = rectangleIntersectMinimums.Min();
|
|
_FaceDistanceHiddenImageFactor = faceDistanceHiddenImageFactor;
|
|
_ForceFaceLastWriteTimeToCreationTime = forceFaceLastWriteTimeToCreationTime;
|
|
_ResultContentFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
|
|
_ResultCollectionFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
|
|
(Model model, PredictorModel predictorModel, ModelParameter modelParameter) = GetModel(modelDirectory, modelName, predictorModelName);
|
|
_Model = model;
|
|
_PredictorModel = predictorModel;
|
|
_ModelParameter = modelParameter;
|
|
ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception();
|
|
_ConstructorInfo = constructorInfo;
|
|
_WriteIndentedAndWhenWritingNull = new JsonSerializerOptions { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
|
|
}
|
|
|
|
void IFaceD.ReSaveFace(ExifDirectory exifDirectory, FilePath filePath, Shared.Models.Face face, bool mappedFile)
|
|
{
|
|
FileInfo fileInfo = new(filePath.FullName);
|
|
if (fileInfo.Exists)
|
|
{
|
|
string? json;
|
|
short type = 2;
|
|
string? model;
|
|
string? maker;
|
|
string checkFile = $"{filePath.FullName}.exif";
|
|
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist;
|
|
// const int author = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagWinAuthor; // 40093
|
|
if (mappedFile)
|
|
{
|
|
json = IMetadata.GetOutputResolution(exifDirectory);
|
|
if (json is not null && json.Contains(nameof(DateTime)))
|
|
return;
|
|
(maker, model) = Get(json);
|
|
}
|
|
else
|
|
{
|
|
maker = IMetadata.GetMaker(exifDirectory);
|
|
model = IMetadata.GetModel(exifDirectory);
|
|
ExifDirectory? faceExifDirectory = IMetadata.GetExifDirectory(filePath);
|
|
json = IMetadata.GetOutputResolution(faceExifDirectory);
|
|
if (json is not null && json.Contains(nameof(DateTime)))
|
|
return;
|
|
}
|
|
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
|
|
FaceFile faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
|
|
face.Mapping?.MappingFromLocation?.ConfidencePercent,
|
|
geoLocation?.ToDmsString(),
|
|
face.DateTime,
|
|
null,
|
|
face.FaceParts,
|
|
face.Location,
|
|
maker,
|
|
null,
|
|
model,
|
|
face.OutputResolution);
|
|
string faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
|
|
ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception();
|
|
PropertyItem? propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(constructorInfo, artist, type, faceFileJson);
|
|
#pragma warning disable CA1416
|
|
Bitmap bitmap = new(fileInfo.FullName);
|
|
bitmap.SetPropertyItem(propertyItem);
|
|
bitmap.Save(checkFile);
|
|
bitmap.Dispose();
|
|
#pragma warning restore CA1416
|
|
File.SetLastWriteTime(checkFile, fileInfo.LastWriteTime);
|
|
File.Delete(fileInfo.FullName);
|
|
File.Move(checkFile, fileInfo.FullName);
|
|
}
|
|
}
|
|
|
|
private static (string?, string?) Get(string? json)
|
|
{
|
|
string? model;
|
|
string? maker;
|
|
FaceFile? faceFile = json is null ? null : JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile);
|
|
if (faceFile is null || faceFile.Location is null)
|
|
(maker, model) = (null, null);
|
|
else
|
|
(maker, model) = (faceFile.Maker, faceFile.Model);
|
|
return (maker, model);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
|
|
return result;
|
|
}
|
|
|
|
public void Update(string dResultsFullGroupDirectory)
|
|
{
|
|
ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, dResultsFullGroupDirectory, [_PropertyConfiguration.ResultCollection, _PropertyConfiguration.ResultContent]);
|
|
foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePair in keyValuePairs)
|
|
{
|
|
if (keyValuePair.Key == _PropertyConfiguration.ResultContent)
|
|
_ResultContentFileGroups[0] = keyValuePair.Value;
|
|
else if (keyValuePair.Key == _PropertyConfiguration.ResultCollection)
|
|
_ResultCollectionFileGroups[0] = keyValuePair.Value;
|
|
else
|
|
throw new Exception();
|
|
}
|
|
}
|
|
|
|
public List<(Shared.Models.Face, FileHolder?, string, bool)> SaveFaces(FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces)
|
|
{
|
|
List<(Shared.Models.Face, FileHolder?, string, bool Save)> results = [];
|
|
bool save;
|
|
FileInfo fileInfo;
|
|
FileHolder fileHolder;
|
|
string deterministicHashCodeKey;
|
|
string fileName = mappingFromItem.FilePath.NameWithoutExtension;
|
|
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)];
|
|
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
|
|
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath);
|
|
string directory = _ResultContentFileGroups[0][cei.Enum][cei.Index];
|
|
DirectoryInfo directoryInfo = new(Path.Combine(directory, fileName));
|
|
MoveIf(fileName, cei, directory, directoryInfo);
|
|
foreach (Shared.Models.Face face in faces)
|
|
{
|
|
save = false;
|
|
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
|
|
{
|
|
results.Add(new(face, null, string.Empty, save));
|
|
continue;
|
|
}
|
|
deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
|
|
fileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_FileNameExtension}"));
|
|
fileHolder = FileHolder.Get(fileInfo);
|
|
if (!directoryInfo.Exists)
|
|
save = true;
|
|
else if (_OverrideForFaceImages)
|
|
save = true;
|
|
else if (!fileHolder.Exists)
|
|
save = true;
|
|
else if (_CheckDFaceAndUpWriteDates && dateTimes.Count > 0 && dateTimes.Max() > fileInfo.LastWriteTime)
|
|
save = true;
|
|
results.Add(new(face, fileHolder, Path.Combine(directoryInfo.FullName, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_HiddenFileNameExtension}"), save));
|
|
}
|
|
if (results.Any(l => l.Save))
|
|
{
|
|
if (!directoryInfo.Exists)
|
|
_ = Directory.CreateDirectory(directoryInfo.FullName);
|
|
SaveFaces(mappingFromItem.ResizedFileHolder, exifDirectory, results);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo)
|
|
{
|
|
string[] segments = directory.Split(cei.Combined);
|
|
string? checkDirectory = segments.Length == 1 ?
|
|
Path.Combine(segments[0], $"{cei.Combined[2..]}") :
|
|
segments.Length == 2 ?
|
|
$"{segments[0]}{cei.Combined[2..]}{segments[1]}" :
|
|
null;
|
|
if (checkDirectory is not null && Directory.Exists(checkDirectory))
|
|
{
|
|
string checkFile = Path.Combine(checkDirectory, fileName);
|
|
if (File.Exists(checkFile))
|
|
{
|
|
File.Move(checkFile, fileInfo.FullName);
|
|
fileInfo.Refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, DirectoryInfo directoryInfo)
|
|
{
|
|
string[] segments = directory.Split(cei.Combined);
|
|
string? checkDirectory = segments.Length == 1 ?
|
|
Path.Combine(segments[0], $"{cei.Combined[2..]}") :
|
|
segments.Length == 2 ?
|
|
$"{segments[0]}{cei.Combined[2..]}{segments[1]}" :
|
|
null;
|
|
if (checkDirectory is not null && Directory.Exists(checkDirectory))
|
|
{
|
|
string checkFile = Path.Combine(checkDirectory, fileName);
|
|
if (Directory.Exists(checkFile))
|
|
{
|
|
Directory.Move(checkFile, directoryInfo.FullName);
|
|
directoryInfo.Refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
public List<Shared.Models.Face> GetFaces(string outputResolution, string cResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize, List<MappingFromPhotoPrism>? mappingFromPhotoPrismCollection)
|
|
{
|
|
List<Shared.Models.Face>? results;
|
|
string? json;
|
|
List<Location> locations;
|
|
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)];
|
|
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
|
|
string fileName = $"{mappingFromItem.FilePath.NameWithoutExtension}{mappingFromItem.FilePath.ExtensionLowered}.json";
|
|
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath);
|
|
string directory = _ResultCollectionFileGroups[0][cei.Enum][cei.Index];
|
|
FileInfo fileInfo = new(Path.Combine(directory, fileName));
|
|
MoveIf(fileName, cei, directory, fileInfo);
|
|
if (_ForceFaceLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
|
|
{
|
|
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
|
|
fileInfo.Refresh();
|
|
}
|
|
if (_ForceFaceLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime)
|
|
{
|
|
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
|
|
fileInfo.Refresh();
|
|
}
|
|
if (_PropertiesChangedForFaces)
|
|
results = null;
|
|
else if (!fileInfo.Exists)
|
|
results = null;
|
|
else if (_CheckDFaceAndUpWriteDates && dateTimes.Count > 0 && dateTimes.Max() > fileInfo.LastWriteTime)
|
|
results = null;
|
|
else
|
|
{
|
|
json = Shared.Models.Stateless.Methods.IFace.GetJson(fileInfo.FullName);
|
|
try
|
|
{
|
|
results = JsonSerializer.Deserialize<List<Shared.Models.Face>>(json);
|
|
if (results is null)
|
|
throw new NullReferenceException(nameof(results));
|
|
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), fileInfo.LastWriteTime));
|
|
}
|
|
catch (Exception)
|
|
{
|
|
results = null;
|
|
parseExceptions.Add(nameof(D_Face));
|
|
}
|
|
}
|
|
if (!_LoadPhotoPrismLocations || mappingFromPhotoPrismCollection is null || results is null)
|
|
locations = [];
|
|
else
|
|
locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(results, mappingFromPhotoPrismCollection, _RectangleIntersectMinimum);
|
|
if (results is null || locations.Count > 0)
|
|
{
|
|
results = GetFaces(outputResolution, cResultsFullGroupDirectory, property, mappingFromItem, outputResolutionToResize, locations);
|
|
if (results.Count == 0)
|
|
File.Move(mappingFromItem.ResizedFileHolder.FullName, $"{mappingFromItem.ResizedFileHolder.FullName}.err");
|
|
else
|
|
{
|
|
bool updateDateWhenMatches = dateTimes.Count > 0 && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime;
|
|
DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max();
|
|
json = JsonSerializer.Serialize(results, _WriteIndentedAndWhenWritingNull);
|
|
if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime))
|
|
{
|
|
if (!_ForceFaceLastWriteTimeToCreationTime)
|
|
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), DateTime.Now));
|
|
else
|
|
{
|
|
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
|
|
fileInfo.Refresh();
|
|
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), fileInfo.CreationTime));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private List<Shared.Models.Face> GetFaces(string outputResolution, string cResultsFullGroupDirectory, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize, List<Location> locations)
|
|
{
|
|
if (_PropertyConfiguration.NumberOfJitters is null)
|
|
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters));
|
|
if (_PropertyConfiguration.NumberOfTimesToUpsample is null)
|
|
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample));
|
|
List<Shared.Models.Face> results = [];
|
|
FaceRecognitionDotNet.Image? unknownImage;
|
|
try
|
|
{
|
|
if (mappingFromItem.ResizedFileHolder.ExtensionLowered != ".tif")
|
|
unknownImage = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName);
|
|
else
|
|
{
|
|
int outputQuality = 100;
|
|
string extension = ".png";
|
|
string file = Path.Combine(cResultsFullGroupDirectory, $"{mappingFromItem.ResizedFileHolder.Name}{extension}");
|
|
(ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(extension, outputQuality);
|
|
#pragma warning disable CA1416
|
|
System.Drawing.Image image = System.Drawing.Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
|
|
image.Save(Path.Combine(cResultsFullGroupDirectory, $"{mappingFromItem.ResizedFileHolder.Name}{filenameExtension}"), imageCodecInfo, encoderParameters);
|
|
image.Dispose();
|
|
#pragma warning restore CA1416
|
|
unknownImage = FaceRecognition.LoadImageFile(file);
|
|
File.Delete(file);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{ unknownImage = null; }
|
|
if (unknownImage is not null)
|
|
{
|
|
(int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation) = Resize.Models.Stateless.Methods.IResize.Get(outputResolution, outputResolutionToResize);
|
|
List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary<FacePart, FacePoint[]>? FaceParts)> collection;
|
|
FaceRecognition faceRecognition = new(_PropertyConfiguration.NumberOfJitters.Value, _PropertyConfiguration.NumberOfTimesToUpsample.Value, _Model, _ModelParameter, _PredictorModel);
|
|
collection = faceRecognition.GetCollection(unknownImage, locations, includeFaceEncoding: true, includeFaceParts: true);
|
|
if (collection.Count == 0)
|
|
results.Add(new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location: null));
|
|
else
|
|
{
|
|
double[] rawEncoding;
|
|
Shared.Models.Face face;
|
|
Shared.Models.FaceEncoding convertedFaceEncoding;
|
|
foreach ((Location location, FaceRecognitionDotNet.FaceEncoding? faceEncoding, Dictionary<FacePart, FacePoint[]>? faceParts) in collection)
|
|
{
|
|
face = new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location);
|
|
if (faceEncoding is not null)
|
|
{
|
|
rawEncoding = faceEncoding.GetRawEncoding();
|
|
convertedFaceEncoding = new(rawEncoding, faceEncoding.Size);
|
|
face.SetFaceEncoding(convertedFaceEncoding);
|
|
}
|
|
if (faceParts is not null)
|
|
face.SetFaceParts(faceParts);
|
|
results.Add(face);
|
|
}
|
|
}
|
|
unknownImage.Dispose();
|
|
faceRecognition.Dispose();
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static (Model model, PredictorModel predictorModel, ModelParameter modelParameter) GetModel(string modelDirectory, string modelName, string predictorModelName)
|
|
{
|
|
(Model, PredictorModel, ModelParameter) result;
|
|
Array array;
|
|
Model? model = null;
|
|
array = Enum.GetValues<Model>();
|
|
PredictorModel? predictorModel = null;
|
|
foreach (Model check in array)
|
|
{
|
|
if (modelName.Contains(check.ToString()))
|
|
{
|
|
model = check;
|
|
break;
|
|
}
|
|
}
|
|
if (model is null)
|
|
throw new Exception("Destination directory must have Model name!");
|
|
model = model.Value;
|
|
array = Enum.GetValues<PredictorModel>();
|
|
foreach (PredictorModel check in array)
|
|
{
|
|
if (predictorModelName.Contains(check.ToString()))
|
|
{
|
|
predictorModel = check;
|
|
break;
|
|
}
|
|
}
|
|
if (predictorModel is null)
|
|
throw new Exception("Destination directory must have Predictor Model name!");
|
|
predictorModel = predictorModel.Value;
|
|
ModelParameter modelParameter = new()
|
|
{
|
|
CnnFaceDetectorModel = File.ReadAllBytes(Path.Combine(modelDirectory, "mmod_human_face_detector.dat")),
|
|
FaceRecognitionModel = File.ReadAllBytes(Path.Combine(modelDirectory, "dlib_face_recognition_resnet_model_v1.dat")),
|
|
PosePredictor5FaceLandmarksModel = File.ReadAllBytes(Path.Combine(modelDirectory, "shape_predictor_5_face_landmarks.dat")),
|
|
PosePredictor68FaceLandmarksModel = File.ReadAllBytes(Path.Combine(modelDirectory, "shape_predictor_68_face_landmarks.dat"))
|
|
};
|
|
result = new(model.Value, predictorModel.Value, modelParameter);
|
|
return result;
|
|
}
|
|
|
|
private void SaveFaces(FileHolder resizedFileHolder, ExifDirectory exifDirectory, List<(Shared.Models.Face, FileHolder?, string, bool)> collection)
|
|
{
|
|
int width;
|
|
int height;
|
|
Bitmap bitmap;
|
|
short type = 2;
|
|
FaceFile faceFile;
|
|
Graphics graphics;
|
|
Location? location;
|
|
Rectangle rectangle;
|
|
string faceFileJson;
|
|
string faceEncodingJson;
|
|
PropertyItem? propertyItem;
|
|
string? maker = IMetadata.GetMaker(exifDirectory);
|
|
string? model = IMetadata.GetModel(exifDirectory);
|
|
#pragma warning disable CA1416
|
|
using Bitmap source = new(resizedFileHolder.FullName);
|
|
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
|
|
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315
|
|
const int userComment = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagUserComment;
|
|
foreach ((Shared.Models.Face face, FileHolder? fileHolder, string fileName, bool save) in collection)
|
|
{
|
|
if (!save)
|
|
continue;
|
|
if (fileHolder is null)
|
|
continue;
|
|
if (face.FaceEncoding is null || face?.Location is null || face?.OutputResolution is null)
|
|
continue;
|
|
if (_OverrideForFaceImages && fileHolder.Exists)
|
|
{
|
|
IFaceD dFace = this;
|
|
FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null);
|
|
dFace.ReSaveFace(exifDirectory, filePath, face, mappedFile: false);
|
|
continue;
|
|
}
|
|
location = Shared.Models.Stateless.Methods.ILocation.GetLocation(face.Location, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, source.Height, source.Width, collection.Count);
|
|
if (location is null)
|
|
continue;
|
|
width = location.Right - location.Left;
|
|
height = location.Bottom - location.Top;
|
|
faceEncodingJson = JsonSerializer.Serialize(face.FaceEncoding);
|
|
rectangle = new Rectangle(location.Left, location.Top, width, height);
|
|
faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
|
|
face.Mapping?.MappingFromLocation?.ConfidencePercent,
|
|
geoLocation?.ToDmsString(),
|
|
face.DateTime,
|
|
null,
|
|
face.FaceParts,
|
|
face.Location,
|
|
maker,
|
|
null,
|
|
model,
|
|
face.OutputResolution);
|
|
faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
|
|
using (bitmap = new(width, height))
|
|
{
|
|
using (graphics = Graphics.FromImage(bitmap))
|
|
graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
|
|
propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson);
|
|
bitmap.SetPropertyItem(propertyItem);
|
|
propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(_ConstructorInfo, userComment, type, faceEncodingJson);
|
|
bitmap.SetPropertyItem(propertyItem);
|
|
bitmap.Save(fileHolder.FullName, _ImageCodecInfo, _EncoderParameters);
|
|
}
|
|
if (File.Exists(fileName))
|
|
File.Delete(fileName);
|
|
location = Shared.Models.Stateless.Methods.ILocation.GetLocation(_FaceDistanceHiddenImageFactor, face.Location, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, source.Height, source.Width, collection.Count);
|
|
if (location is null)
|
|
continue;
|
|
width = location.Right - location.Left;
|
|
height = location.Bottom - location.Top;
|
|
rectangle = new Rectangle(location.Left, location.Top, width, height);
|
|
using (bitmap = new(width, height))
|
|
{
|
|
using (graphics = Graphics.FromImage(bitmap))
|
|
graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
|
|
bitmap.Save(fileName, _HiddenImageCodecInfo, _HiddenEncoderParameters);
|
|
}
|
|
#pragma warning restore CA1416
|
|
File.SetAttributes(fileName, FileAttributes.Hidden);
|
|
}
|
|
}
|
|
|
|
} |