FaceFile for D2
This commit is contained in:
@ -2,9 +2,12 @@ using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using View_by_Distance.Face.Models;
|
||||
using View_by_Distance.Metadata.Models;
|
||||
using View_by_Distance.Metadata.Models.Stateless.Methods;
|
||||
using View_by_Distance.Property.Models;
|
||||
using View_by_Distance.Property.Models.Stateless;
|
||||
using View_by_Distance.Resize.Models;
|
||||
@ -25,9 +28,9 @@ public class D2_FaceParts
|
||||
|
||||
private readonly ImageCodecInfo _ImageCodecInfo;
|
||||
private readonly bool _CheckDFaceAndUpWriteDates;
|
||||
private readonly ConstructorInfo _ConstructorInfo;
|
||||
private readonly bool _OverrideForFaceLandmarkImages;
|
||||
private readonly EncoderParameters _EncoderParameters;
|
||||
private readonly List<string> _AngleBracketCollection;
|
||||
private readonly Dictionary<string, string[]> _FileGroups;
|
||||
private readonly IPropertyConfiguration _PropertyConfiguration;
|
||||
|
||||
@ -37,10 +40,11 @@ public class D2_FaceParts
|
||||
_ImageCodecInfo = imageCodecInfo;
|
||||
_EncoderParameters = encoderParameters;
|
||||
_FileNameExtension = filenameExtension;
|
||||
_AngleBracketCollection = [];
|
||||
_PropertyConfiguration = propertyConfiguration;
|
||||
_CheckDFaceAndUpWriteDates = checkDFaceAndUpWriteDates;
|
||||
_OverrideForFaceLandmarkImages = overrideForFaceLandmarkImages;
|
||||
ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception();
|
||||
_ConstructorInfo = constructorInfo;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
@ -57,41 +61,6 @@ public class D2_FaceParts
|
||||
_FileGroups.Add(keyValuePair.Key, keyValuePair.Value);
|
||||
}
|
||||
|
||||
public void SetAngleBracketCollection(IPropertyConfiguration propertyConfiguration, string d2ResultsFullGroupDirectory, string sourceDirectory)
|
||||
{
|
||||
_AngleBracketCollection.Clear();
|
||||
_AngleBracketCollection.AddRange(IResult.GetDirectoryInfoCollection(propertyConfiguration,
|
||||
sourceDirectory,
|
||||
d2ResultsFullGroupDirectory,
|
||||
contentDescription: "n gif file(s) for each face found",
|
||||
singletonDescription: string.Empty,
|
||||
collectionDescription: string.Empty,
|
||||
converted: true));
|
||||
}
|
||||
|
||||
public string GetFacePartsDirectory(IPropertyConfiguration propertyConfiguration, string dResultsFullGroupDirectory, Item item, bool includeNameWithoutExtension)
|
||||
{
|
||||
string result;
|
||||
bool angleBracketCollectionAny = _AngleBracketCollection.Count != 0;
|
||||
if (!angleBracketCollectionAny)
|
||||
{
|
||||
if (item.FilePath.DirectoryName is null)
|
||||
throw new NullReferenceException(nameof(item.FilePath.DirectoryName));
|
||||
SetAngleBracketCollection(propertyConfiguration, dResultsFullGroupDirectory, item.FilePath.DirectoryName);
|
||||
}
|
||||
if (includeNameWithoutExtension)
|
||||
result = Path.Combine(_AngleBracketCollection[0].Replace("<>", _PropertyConfiguration.ResultContent), item.FilePath.NameWithoutExtension);
|
||||
else
|
||||
{
|
||||
result = _AngleBracketCollection[0].Replace("<>", $"[{_PropertyConfiguration.ResultContent}]");
|
||||
if (!Directory.Exists(result))
|
||||
_ = Directory.CreateDirectory(result);
|
||||
}
|
||||
if (!angleBracketCollectionAny)
|
||||
_AngleBracketCollection.Clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void GetPointBounds(PointF[] points, out float xMinimum, out float xMaximum, out float yMinimum, out float yMaximum)
|
||||
{
|
||||
xMinimum = points[0].X;
|
||||
@ -183,73 +152,234 @@ public class D2_FaceParts
|
||||
return result;
|
||||
}
|
||||
|
||||
private void SaveFaceParts(int pointSize, FileHolder resizedFileHolder, bool saveRotated, List<(Shared.Models.Face, string, string)> collection)
|
||||
private void SaveRotated(MappingFromItem mappingFromItem, List<(Shared.Models.Face, string, string)> collection)
|
||||
{
|
||||
double? α;
|
||||
Bitmap rotated;
|
||||
foreach ((Shared.Models.Face face, string _, string rotatedFileName) in collection)
|
||||
{
|
||||
if (face.FaceParts is null)
|
||||
continue;
|
||||
(_, α) = Shared.Models.Stateless.Methods.IFace.GetEyeα(face.FaceParts);
|
||||
if (α is null)
|
||||
continue;
|
||||
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
|
||||
rotated = RotateBitmap(image, (float)α.Value);
|
||||
if (rotated is not null)
|
||||
{
|
||||
rotated.Save(rotatedFileName, _ImageCodecInfo, _EncoderParameters);
|
||||
rotated.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetSeasonABDirectory(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem)
|
||||
{
|
||||
string result;
|
||||
string minimumDateYear = mappingFromItem.MinimumDateTime.ToString("yyyy");
|
||||
DateTime dateTime = mappingFromItem.DateTimeOriginal is null ? mappingFromItem.MinimumDateTime : mappingFromItem.DateTimeOriginal.Value;
|
||||
(int _, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeasonAB(dateTime.DayOfYear);
|
||||
string year = mappingFromItem.DateTimeOriginal is null ? $"{minimumDateYear[1..]}{minimumDateYear[0]}" : mappingFromItem.DateTimeOriginal.Value.ToString("yyyy");
|
||||
result = Path.Combine(d2ResultsFullGroupDirectory, $"[{_PropertyConfiguration.ResultContent}]", $"{year} {seasonName}");
|
||||
if (!Directory.Exists(result))
|
||||
_ = Directory.CreateDirectory(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void SaveImage(MappingFromItem mappingFromItem, string directory, Image image, List<FaceFile> faceFiles)
|
||||
{
|
||||
short type = 2;
|
||||
string faceFileJson;
|
||||
PropertyItem? propertyItem;
|
||||
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315
|
||||
string fileName = Path.Combine(directory, $"{mappingFromItem.FilePath.Name}{_FileNameExtension}");
|
||||
try
|
||||
{
|
||||
foreach (int propertyId in image.PropertyIdList)
|
||||
{
|
||||
if (propertyId == MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation)
|
||||
continue;
|
||||
image.RemovePropertyItem(propertyId);
|
||||
}
|
||||
faceFileJson = JsonSerializer.Serialize(faceFiles.ToArray(), FaceFileCollectionGenerationContext.Default.FaceFileArray);
|
||||
propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson);
|
||||
image.SetPropertyItem(propertyItem);
|
||||
image.Save(fileName, _ImageCodecInfo, _EncoderParameters);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is not null && !string.IsNullOrEmpty(fileName) && File.Exists(fileName))
|
||||
File.Delete(fileName);
|
||||
faceFileJson = JsonSerializer.Serialize(faceFiles.ToArray(), FaceFileCollectionGenerationContext.Default.FaceFileArray);
|
||||
if (!string.IsNullOrEmpty(faceFileJson))
|
||||
File.WriteAllText($"{fileName}.json", faceFileJson);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveAllFaceParts(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces)
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
double? α;
|
||||
int width;
|
||||
int height;
|
||||
Bitmap rotated;
|
||||
foreach ((Shared.Models.Face face, string fileName, string rotatedFileName) in collection)
|
||||
Pen pen;
|
||||
int pointSize;
|
||||
FaceFile faceFile;
|
||||
bool? isDefaultName;
|
||||
List<long> personKeys = [];
|
||||
List<FaceFile> faceFiles = [];
|
||||
StringBuilder stringBuilder = new();
|
||||
MappingFromPerson? mappingFromPerson;
|
||||
string? maker = IMetadata.GetMaker(exifDirectory);
|
||||
string? model = IMetadata.GetModel(exifDirectory);
|
||||
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
|
||||
string directory = GetSeasonABDirectory(d2ResultsFullGroupDirectory, mappingFromItem);
|
||||
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
|
||||
using Graphics graphics = Graphics.FromImage(image);
|
||||
foreach (Shared.Models.Face face in faces)
|
||||
{
|
||||
if (face.FaceEncoding is null)
|
||||
if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0)
|
||||
continue;
|
||||
mappingFromPerson = face.Mapping?.MappingFromPerson;
|
||||
pen = mappingFromPerson is null ? Pens.Red : Pens.GreenYellow;
|
||||
isDefaultName = mappingFromPerson is null ? null : Shared.Models.Stateless.Methods.IPerson.IsDefaultName(mappingFromPerson);
|
||||
if (mappingFromPerson is not null && isDefaultName is not null && !isDefaultName.Value)
|
||||
personKeys.Add(mappingFromPerson.PersonKey);
|
||||
faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
|
||||
face.Mapping?.MappingFromLocation?.ConfidencePercent,
|
||||
geoLocation?.ToDmsString(),
|
||||
face.DateTime,
|
||||
face.FaceEncoding,
|
||||
face.FaceParts,
|
||||
face.Location,
|
||||
maker,
|
||||
mappingFromPerson,
|
||||
model,
|
||||
face.OutputResolution);
|
||||
faceFiles.Add(faceFile);
|
||||
pointSize = GetPointSize(face.FaceParts, defaultPointSize: 2);
|
||||
foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts)
|
||||
{
|
||||
foreach (FacePoint facePoint in facePoints)
|
||||
graphics.DrawEllipse(pen, facePoint.X - pointSize, facePoint.Y - pointSize, pointSize * 2, pointSize * 2);
|
||||
if (facePart == FacePart.Chin)
|
||||
continue;
|
||||
if (facePoints.Length < 3)
|
||||
continue;
|
||||
x = (int)(from l in facePoints select l.X).Average();
|
||||
y = (int)(from l in facePoints select l.Y).Average();
|
||||
graphics.DrawEllipse(Pens.Purple, x - pointSize, y - pointSize, pointSize * 2, pointSize * 2);
|
||||
}
|
||||
}
|
||||
_ = graphics.Save();
|
||||
SaveImage(mappingFromItem, directory, image, faceFiles);
|
||||
}
|
||||
|
||||
private void SaveImage(string fileName, Image image, FaceFile faceFile)
|
||||
{
|
||||
short type = 2;
|
||||
string faceFileJson;
|
||||
PropertyItem? propertyItem;
|
||||
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315
|
||||
try
|
||||
{
|
||||
foreach (int propertyId in image.PropertyIdList)
|
||||
{
|
||||
if (propertyId == MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation)
|
||||
continue;
|
||||
image.RemovePropertyItem(propertyId);
|
||||
}
|
||||
faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
|
||||
propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson);
|
||||
image.SetPropertyItem(propertyItem);
|
||||
image.Save(fileName, _ImageCodecInfo, _EncoderParameters);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is not null && !string.IsNullOrEmpty(fileName) && File.Exists(fileName))
|
||||
File.Delete(fileName);
|
||||
faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
|
||||
if (!string.IsNullOrEmpty(faceFileJson))
|
||||
File.WriteAllText($"{fileName}.json", faceFileJson);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveFaceParts(MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<(Shared.Models.Face, string, string)> collection)
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
Pen pen;
|
||||
int pointSize;
|
||||
FaceFile faceFile;
|
||||
MappingFromPerson? mappingFromPerson;
|
||||
string? maker = IMetadata.GetMaker(exifDirectory);
|
||||
string? model = IMetadata.GetModel(exifDirectory);
|
||||
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
|
||||
foreach ((Shared.Models.Face face, string fileName, string _) in collection)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (Image image = Image.FromFile(resizedFileHolder.FullName))
|
||||
if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0)
|
||||
continue;
|
||||
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
|
||||
mappingFromPerson = face.Mapping?.MappingFromPerson;
|
||||
pen = mappingFromPerson is null ? Pens.Red : Pens.GreenYellow;
|
||||
faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
|
||||
face.Mapping?.MappingFromLocation?.ConfidencePercent,
|
||||
geoLocation?.ToDmsString(),
|
||||
face.DateTime,
|
||||
face.FaceEncoding,
|
||||
face.FaceParts,
|
||||
face.Location,
|
||||
maker,
|
||||
mappingFromPerson,
|
||||
model,
|
||||
face.OutputResolution);
|
||||
using Graphics graphics = Graphics.FromImage(image);
|
||||
pointSize = GetPointSize(face.FaceParts, defaultPointSize: 2);
|
||||
foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts)
|
||||
{
|
||||
using Graphics graphic = Graphics.FromImage(image);
|
||||
if (face.FaceParts is null || face.FaceParts.Count == 0)
|
||||
{
|
||||
if (face.Location is null)
|
||||
continue;
|
||||
width = face.Location.Right - face.Location.Left;
|
||||
height = face.Location.Bottom - face.Location.Top;
|
||||
graphic.DrawEllipse(Pens.Red, face.Location.Left, face.Location.Top, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts)
|
||||
{
|
||||
foreach (FacePoint facePoint in facePoints)
|
||||
graphic.DrawEllipse(Pens.GreenYellow, facePoint.X - pointSize, facePoint.Y - pointSize, pointSize * 2, pointSize * 2);
|
||||
if (facePart == FacePart.Chin)
|
||||
continue;
|
||||
if (facePoints.Length < 3)
|
||||
continue;
|
||||
x = (int)(from l in facePoints select l.X).Average();
|
||||
y = (int)(from l in facePoints select l.Y).Average();
|
||||
graphic.DrawEllipse(Pens.Purple, x - pointSize, y - pointSize, pointSize * 2, pointSize * 2);
|
||||
}
|
||||
}
|
||||
image.Save(fileName, _ImageCodecInfo, _EncoderParameters);
|
||||
}
|
||||
if (saveRotated && face.FaceParts is not null)
|
||||
{
|
||||
(_, α) = Shared.Models.Stateless.Methods.IFace.GetEyeα(face.FaceParts);
|
||||
if (α is null)
|
||||
foreach (FacePoint facePoint in facePoints)
|
||||
graphics.DrawEllipse(pen, facePoint.X - pointSize, facePoint.Y - pointSize, pointSize * 2, pointSize * 2);
|
||||
if (facePart == FacePart.Chin)
|
||||
continue;
|
||||
using Image image = Image.FromFile(resizedFileHolder.FullName);
|
||||
rotated = RotateBitmap(image, (float)α.Value);
|
||||
if (rotated is not null)
|
||||
{
|
||||
rotated.Save(rotatedFileName, _ImageCodecInfo, _EncoderParameters);
|
||||
rotated.Dispose();
|
||||
}
|
||||
if (facePoints.Length < 3)
|
||||
continue;
|
||||
x = (int)(from l in facePoints select l.X).Average();
|
||||
y = (int)(from l in facePoints select l.Y).Average();
|
||||
graphics.DrawEllipse(Pens.Purple, x - pointSize, y - pointSize, pointSize * 2, pointSize * 2);
|
||||
}
|
||||
_ = graphics.Save();
|
||||
SaveImage(fileName, image, faceFile);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (File.Exists(fileName))
|
||||
File.Delete(fileName);
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
|
||||
private int GetPointSize(Dictionary<FacePart, FacePoint[]> faceParts, int defaultPointSize)
|
||||
{
|
||||
int result;
|
||||
FacePoint[]? facePoints;
|
||||
if (faceParts.TryGetValue(FacePart.LeftEye, out facePoints))
|
||||
result = (int)Math.Ceiling((facePoints.Max(l => l.X) - facePoints.Min(l => l.X)) * .05);
|
||||
else
|
||||
{
|
||||
if (faceParts.TryGetValue(FacePart.RightEye, out facePoints))
|
||||
result = (int)Math.Ceiling((facePoints.Max(l => l.X) - facePoints.Min(l => l.X)) * .05);
|
||||
else
|
||||
result = defaultPointSize;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma warning restore CA1416
|
||||
|
||||
public void SaveFaceLandmarkImages(Configuration configuration, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, MappingFromItem mappingFromItem, List<Shared.Models.Face> faces, bool saveRotated)
|
||||
public void SaveFaceLandmarkImages(Configuration configuration, string d2ResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces, bool saveRotated)
|
||||
{
|
||||
FileInfo fileInfo;
|
||||
bool check = false;
|
||||
const int pointSize = 2;
|
||||
FileInfo rotatedFileInfo;
|
||||
DateTime? dateTime = null;
|
||||
long ticks = DateTime.Now.Ticks;
|
||||
@ -296,37 +426,12 @@ public class D2_FaceParts
|
||||
{
|
||||
if (!directoryExists)
|
||||
_ = Directory.CreateDirectory(directory);
|
||||
SaveFaceParts(pointSize, mappingFromItem.ResizedFileHolder, saveRotated, collection);
|
||||
SaveFaceParts(mappingFromItem, exifDirectory, collection);
|
||||
if (saveRotated)
|
||||
SaveRotated(mappingFromItem, collection);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416
|
||||
|
||||
private void SaveFaceLandmarkImage(MappingFromItem mappingFromItem, List<(Shared.Models.Face, FileHolder?, string, bool)> faceCollection, string fileName)
|
||||
{
|
||||
Pen pen;
|
||||
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
|
||||
using Graphics graphic = Graphics.FromImage(image);
|
||||
foreach ((Shared.Models.Face face, FileHolder? _, string _, bool _) in faceCollection)
|
||||
{
|
||||
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null || face.FaceParts is null || face.FaceParts.Count == 0)
|
||||
continue;
|
||||
pen = face.Mapping?.MappingFromPerson is null ? Pens.Red : Pens.GreenYellow;
|
||||
try
|
||||
{
|
||||
foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts)
|
||||
{
|
||||
for (int i = 0; i < facePoints.Length - 1; i++)
|
||||
graphic.DrawLine(pen, new Point(facePoints[i].X, facePoints[i].Y), new Point(facePoints[i + 1].X, facePoints[i + 1].Y));
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
image.Save(fileName, _ImageCodecInfo, _EncoderParameters);
|
||||
}
|
||||
|
||||
#pragma warning restore CA1416
|
||||
|
||||
private static bool GetNotMapped(string facePartsCollectionDirectory, List<(Shared.Models.Face Face, FileHolder?, string, bool)> faceCollection)
|
||||
{
|
||||
bool results = false;
|
||||
@ -360,19 +465,18 @@ public class D2_FaceParts
|
||||
return results;
|
||||
}
|
||||
|
||||
public void CopyFacesAndSaveFaceLandmarkImage(string facePartsCollectionDirectory, MappingFromItem mappingFromItem, List<(Shared.Models.Face Face, FileHolder?, string, bool)> faceCollection)
|
||||
public void SaveFaceLandmarkImages(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces)
|
||||
{
|
||||
bool hasNotMapped = GetNotMapped(facePartsCollectionDirectory, faceCollection);
|
||||
string fileName = Path.Combine(facePartsCollectionDirectory, $"{mappingFromItem.FilePath.Name}{_FileNameExtension}");
|
||||
bool save = faceCollection.Any(l => l.Face.FaceEncoding is not null && l.Face.Location is not null && l.Face.OutputResolution is not null && l.Face.FaceParts is not null && l.Face.FaceParts.Count != 0);
|
||||
FileInfo fileInfo = new(fileName);
|
||||
if (save && (!fileInfo.Exists || new TimeSpan(DateTime.Now.Ticks - fileInfo.LastWriteTime.Ticks).TotalDays > 10))
|
||||
bool any = false;
|
||||
foreach (Shared.Models.Face face in faces)
|
||||
{
|
||||
SaveFaceLandmarkImage(mappingFromItem, faceCollection, fileName);
|
||||
fileInfo.Refresh();
|
||||
if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0)
|
||||
continue;
|
||||
if (!any)
|
||||
any = true;
|
||||
}
|
||||
if (!hasNotMapped && !fileInfo.Attributes.HasFlag(FileAttributes.Hidden) && (fileInfo.Exists || save))
|
||||
File.SetAttributes(fileName, FileAttributes.Hidden);
|
||||
if (any)
|
||||
SaveAllFaceParts(d2ResultsFullGroupDirectory, mappingFromItem, exifDirectory, faces);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user