427 lines
21 KiB
C#
427 lines
21 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.Diagnostics;
|
|
using System.Text.Json;
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
using Phares.Metadata.Models.Stateless;
|
|
using Phares.Shared.Models;
|
|
|
|
namespace File_Folder_Helper.ADO2025.PI6;
|
|
|
|
internal static partial class Helper20250720 {
|
|
|
|
internal static void WriteFaceData(ILogger<Worker> logger, List<string> args) {
|
|
logger.LogInformation(args[0]);
|
|
logger.LogInformation(args[1]);
|
|
logger.LogInformation(args[2]);
|
|
logger.LogInformation(args[3]);
|
|
logger.LogInformation(args[4]);
|
|
logger.LogInformation(args[5]);
|
|
logger.LogInformation(args[6]);
|
|
logger.LogInformation(args[6]);
|
|
logger.LogInformation(args[7]);
|
|
logger.LogInformation(args[8]);
|
|
string searchPattern = args[5];
|
|
string searchPatternXMP = args[7];
|
|
string outputDirectoryName = args[8];
|
|
string jsonFile = Path.GetFullPath(args[3]);
|
|
string digiKamFile = Path.GetFullPath(args[6]);
|
|
FileInfo faceFileInfo = new(Path.GetFullPath(args[4]));
|
|
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
|
|
string pathRoot = Path.GetPathRoot(faceFileInfo.FullName) ?? throw new Exception();
|
|
string? checkDirectory = Path.GetDirectoryName(digiKamFile.Replace("{}", outputDirectoryName));
|
|
string originalFile = Path.Combine(sourceDirectory, args[2]);
|
|
string pathRootXMP = digiKamFile[..5];
|
|
if (!File.Exists(jsonFile)) {
|
|
logger.LogError("json file doesn't exist! <{jsonFile}>", jsonFile);
|
|
} else if (!File.Exists(digiKamFile)) {
|
|
logger.LogError("digiKam file doesn't exist! <{digiKamFile}>", digiKamFile);
|
|
} else if (!File.Exists(originalFile)) {
|
|
logger.LogError("Original file doesn't exist! <{checkFile}>", originalFile);
|
|
} else if (!Directory.Exists(checkDirectory)) {
|
|
logger.LogError("checkDirectory doesn't exist! <{checkDirectory}>", checkDirectory);
|
|
} else if (!faceFileInfo.Exists) {
|
|
logger.LogError("Face file doesn't exist! <{faceFileInfo}>", faceFileInfo.FullName);
|
|
} else {
|
|
string json = File.ReadAllText(jsonFile);
|
|
ResultSettings? resultSettings = JsonSerializer.Deserialize(json, ResultSettingsSourceGenerationContext.Default.ResultSettings);
|
|
MetadataSettings? metadataSettings = JsonSerializer.Deserialize(json, MetadataSettingsSourceGenerationContext.Default.MetadataSettings);
|
|
if (resultSettings is null) {
|
|
logger.LogError(nameof(ResultSettings));
|
|
} else if (metadataSettings is null) {
|
|
logger.LogError(nameof(MetadataSettings));
|
|
} else {
|
|
WriteFaceData(logger, outputDirectoryName, originalFile, faceFileInfo, digiKamFile, resultSettings, metadataSettings);
|
|
ReadOnlyDictionary<long, ReadOnlyCollection<string>> keyValuePairsXMP = GetKeyValuePairs(searchPatternXMP, pathRootXMP);
|
|
ReadOnlyDictionary<long, ReadOnlyCollection<ExifDirectory>> keyValuePairs = GetKeyValuePairs(searchPattern, pathRoot, resultSettings, metadataSettings);
|
|
if (keyValuePairs.Count == 0) {
|
|
logger.LogError("Didn't find any valid file(s)!");
|
|
} else {
|
|
WriteFaceData(logger, outputDirectoryName, keyValuePairs, keyValuePairsXMP);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, string originalFile, FileInfo faceFileInfo, string digiKamFile, ResultSettings resultSettings, MetadataSettings metadataSettings) {
|
|
ExifDirectory? exifDirectory = IMetadata.GetExifDirectory(resultSettings, metadataSettings, faceFileInfo);
|
|
if (exifDirectory is null) {
|
|
logger.LogError("exifDirectory is null!");
|
|
} else {
|
|
long id;
|
|
string fileNameWithoutExtension;
|
|
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(faceFileInfo.FullName);
|
|
if (!long.TryParse(fileNameWithoutExtension.Split('.')[0], out id)) {
|
|
id = -1;
|
|
}
|
|
long? fileNameFirstSegment = id == -1 ? null : id;
|
|
ReadOnlyCollection<string> digiKamFiles = new([digiKamFile]);
|
|
ReadOnlyCollection<ExifDirectory> exifDirectories = new([exifDirectory]);
|
|
WriteFaceData(logger, outputDirectoryName, originalFile, fileNameFirstSegment, exifDirectories, digiKamFiles);
|
|
}
|
|
}
|
|
|
|
private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, string? originalFile, long? fileNameFirstSegment, ReadOnlyCollection<ExifDirectory> exifDirectories, ReadOnlyCollection<string> digiKamFiles) {
|
|
ReadOnlyDictionary<string, FaceFile> keyValuePairs = GetKeyValuePairs(logger, exifDirectories);
|
|
if (keyValuePairs.Count > 0) {
|
|
WriteFaceData(logger, outputDirectoryName, originalFile, fileNameFirstSegment, digiKamFiles, keyValuePairs);
|
|
}
|
|
}
|
|
|
|
private static int? GetMatchingLine(string digiKamLine, ReadOnlyCollection<string> lines) {
|
|
int? result = null;
|
|
for (int i = 0; i < lines.Count; i++) {
|
|
if (lines[i] == digiKamLine) {
|
|
result = i;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static void WriteFaceData(string outputDirectoryName, string? originalFile, ReadOnlyDictionary<string, FaceFile> keyValuePairs, string digiKamFile, List<string> trimmed, int rdfLine) {
|
|
if (keyValuePairs.Count == 0) {
|
|
throw new Exception();
|
|
}
|
|
double h;
|
|
double w;
|
|
double mpTop;
|
|
double width;
|
|
double height;
|
|
double mpLeft;
|
|
double mwgTop;
|
|
double mwgLeft;
|
|
string personKey;
|
|
FaceFile faceFile;
|
|
string descriptionLine = "</rdf:Description>";
|
|
faceFile = keyValuePairs.ElementAt(0).Value;
|
|
List<string> regionLines = [
|
|
"<MP:RegionInfo rdf:parseType=\"Resource\">",
|
|
"<MPRI:Regions>",
|
|
"<rdf:Bag>"
|
|
];
|
|
List<string> regionsLinesB = [
|
|
"<mwg-rs:Regions rdf:parseType=\"Resource\">",
|
|
"<mwg-rs:AppliedToDimensions",
|
|
$"stDim:w=\"{faceFile.OutputResolution.Width}\"",
|
|
$"stDim:h=\"{faceFile.OutputResolution.Height}\"",
|
|
"stDim:unit=\"pixel\"/>",
|
|
"<mwg-rs:RegionList>",
|
|
"<rdf:Bag>"
|
|
];
|
|
List<string> digiKamLines = ["<digiKam:TagsList>", "<rdf:Seq>"];
|
|
List<string> microsoftPhotoLines = ["<MicrosoftPhoto:LastKeywordXMP>", "<rdf:Bag>"];
|
|
List<string> hierarchicalSubjectLines = ["<lr:hierarchicalSubject>", "<rdf:Bag>"];
|
|
List<string> catalogSetLines = ["<mediapro:CatalogSets>", "<rdf:Bag>"];
|
|
List<string> subjectLines = ["<dc:subject>", "<rdf:Bag>"];
|
|
foreach (KeyValuePair<string, FaceFile> keyValuePair in keyValuePairs) {
|
|
personKey = keyValuePair.Key;
|
|
faceFile = keyValuePair.Value;
|
|
width = faceFile.Location.Right - faceFile.Location.Left;
|
|
height = faceFile.Location.Bottom - faceFile.Location.Top;
|
|
if (!string.IsNullOrEmpty(originalFile) && File.Exists(originalFile)) {
|
|
Extract(file: originalFile,
|
|
width: width,
|
|
height: height,
|
|
left: faceFile.Location.Left,
|
|
top: faceFile.Location.Top,
|
|
suffix: $"-{keyValuePair.Key}-whole-percentages.jpg");
|
|
}
|
|
w = width / faceFile.OutputResolution.Width;
|
|
h = height / faceFile.OutputResolution.Height;
|
|
if (w == 0 || h == 0)
|
|
throw new NotImplementedException();
|
|
mpLeft = (double)faceFile.Location.Left / faceFile.OutputResolution.Width;
|
|
mpTop = (double)faceFile.Location.Top / faceFile.OutputResolution.Height;
|
|
mwgLeft = (faceFile.Location.Left + (width * .5)) / faceFile.OutputResolution.Width;
|
|
mwgTop = (faceFile.Location.Top + (height * .5)) / faceFile.OutputResolution.Height;
|
|
// <MP:RegionInfo rdf:parseType="Resource">
|
|
// <MPRI:Regions>
|
|
// <rdf:Bag>
|
|
// <rdf:li
|
|
// MPReg:PersonDisplayName="{personKey}"
|
|
// MPReg:Rectangle="{mpLeft:0.000000}, {mpTop:0.000000}, {w:0.000000}, {h:0.000000}"/>
|
|
// </rdf:Bag>
|
|
// </MPRI:Regions>
|
|
// </MP:RegionInfo>
|
|
regionLines.Add("<rdf:li");
|
|
regionLines.Add($"MPReg:PersonDisplayName=\"{personKey}\"");
|
|
regionLines.Add($"MPReg:Rectangle=\"{mpLeft:0.000000}, {mpTop:0.000000}, {w:0.000000}, {h:0.000000}\"/>");
|
|
// <mwg-rs:Regions rdf:parseType="Resource">
|
|
// <mwg-rs:AppliedToDimensions
|
|
// stDim:w="{faceFile.OutputResolution.Width}"
|
|
// stDim:h="{faceFile.OutputResolution.Height}"
|
|
// stDim:unit="pixel"/>
|
|
// <mwg-rs:RegionList>
|
|
// <rdf:Bag>
|
|
// <rdf:li>
|
|
// <rdf:Description
|
|
// mwg-rs:Name="{personKey}"
|
|
// mwg-rs:Type="Face">
|
|
// <mwg-rs:Area
|
|
// stArea:x="{mwgLeft:0.000000}"
|
|
// stArea:y="{mwgTop:0.000000}"
|
|
// stArea:w="{w:0.000000}"
|
|
// stArea:h="{h:0.000000}"
|
|
// stArea:unit="normalized"/>
|
|
// </rdf:Description>
|
|
// </rdf:li>
|
|
// </rdf:Bag>
|
|
// </mwg-rs:RegionList>
|
|
// </mwg-rs:Regions>
|
|
regionsLinesB.Add("<rdf:li>");
|
|
regionsLinesB.Add("<rdf:Description");
|
|
regionsLinesB.Add($"mwg-rs:Name=\"{personKey}\"");
|
|
regionsLinesB.Add("mwg-rs:Type=\"Face\">");
|
|
regionsLinesB.Add("<mwg-rs:Area");
|
|
regionsLinesB.Add($"stArea:x=\"{mwgLeft:0.000000}\"");
|
|
regionsLinesB.Add($"stArea:y=\"{mwgTop:0.000000}\"");
|
|
regionsLinesB.Add($"stArea:w=\"{w:0.000000}\"");
|
|
regionsLinesB.Add($"stArea:h=\"{h:0.000000}\"");
|
|
regionsLinesB.Add("stArea:unit=\"normalized\"/>");
|
|
regionsLinesB.Add("</rdf:Description>");
|
|
regionsLinesB.Add("</rdf:li>");
|
|
// <digiKam:TagsList>
|
|
// <rdf:Seq>
|
|
// <rdf:li>People/{personKey}</rdf:li>
|
|
// </rdf:Seq>
|
|
// </digiKam:TagsList>
|
|
digiKamLines.Add($"<rdf:li>People/{personKey}</rdf:li>");
|
|
// <MicrosoftPhoto:LastKeywordXMP>
|
|
// <rdf:Bag>
|
|
// <rdf:li>People/{personKey}</rdf:li>
|
|
// </rdf:Bag>
|
|
// </MicrosoftPhoto:LastKeywordXMP>
|
|
microsoftPhotoLines.Add($"<rdf:li>People/{personKey}</rdf:li>");
|
|
// <lr:hierarchicalSubject>
|
|
// <rdf:Bag>
|
|
// <rdf:li>People|{personKey}</rdf:li>
|
|
// </rdf:Bag>
|
|
// </lr:hierarchicalSubject>
|
|
hierarchicalSubjectLines.Add($"<rdf:li>People|{personKey}</rdf:li>");
|
|
// <mediapro:CatalogSets>
|
|
// <rdf:Bag>
|
|
// <rdf:li>People|{personKey}</rdf:li>
|
|
// </rdf:Bag>
|
|
// </mediapro:CatalogSets>
|
|
catalogSetLines.Add($"<rdf:li>People|{personKey}</rdf:li>");
|
|
// <dc:subject>
|
|
// <rdf:Bag>
|
|
// <rdf:li>{personKey}</rdf:li>
|
|
// </rdf:Bag>
|
|
// </dc:subject>
|
|
subjectLines.Add($"<rdf:li>{personKey}</rdf:li>");
|
|
}
|
|
regionLines.AddRange(["</rdf:Bag>","</MPRI:Regions>","</MP:RegionInfo>"]);
|
|
regionsLinesB.AddRange(["</rdf:Bag>", "</mwg-rs:RegionList>","</mwg-rs:Regions>"]);
|
|
digiKamLines.AddRange(["</rdf:Seq>", "</digiKam:TagsList>"]);
|
|
microsoftPhotoLines.AddRange(["</rdf:Bag>", "</MicrosoftPhoto:LastKeywordXMP>"]);
|
|
hierarchicalSubjectLines.AddRange(["</rdf:Bag>", "</lr:hierarchicalSubject>"]);
|
|
catalogSetLines.AddRange(["</rdf:Bag>", "</mediapro:CatalogSets>"]);
|
|
subjectLines.AddRange(["</rdf:Bag>", "</dc:subject>"]);
|
|
List<string> lines = [];
|
|
lines.AddRange(regionLines);
|
|
lines.AddRange(regionsLinesB);
|
|
lines.AddRange(digiKamLines);
|
|
lines.AddRange(microsoftPhotoLines);
|
|
lines.AddRange(hierarchicalSubjectLines);
|
|
lines.AddRange(catalogSetLines);
|
|
lines.AddRange(subjectLines);
|
|
string text = string.Join(Environment.NewLine, lines);
|
|
if (trimmed[rdfLine - 1] != descriptionLine) {
|
|
trimmed[rdfLine - 1] = $"{trimmed[rdfLine - 1][..^2]}>";
|
|
trimmed.Insert(rdfLine, descriptionLine);
|
|
rdfLine++;
|
|
}
|
|
trimmed.Insert(rdfLine - 1, text);
|
|
string allText = string.Join(Environment.NewLine, trimmed);
|
|
File.WriteAllText(digiKamFile.Replace("{}", outputDirectoryName), allText);
|
|
if (!string.IsNullOrEmpty(originalFile)) {
|
|
if (Debugger.IsAttached) {
|
|
File.WriteAllText(".xml", allText);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static ReadOnlyDictionary<long, ReadOnlyCollection<string>> GetKeyValuePairs(string searchPattern, string pathRoot) {
|
|
Dictionary<long, ReadOnlyCollection<string>> results = [];
|
|
long fileNameFirstSegment;
|
|
List<string>? collection;
|
|
string fileNameWithoutExtension;
|
|
Dictionary<long, List<string>> keyValuePairs = [];
|
|
string[] files = Directory.GetFiles(pathRoot, searchPattern, SearchOption.AllDirectories);
|
|
foreach (string file in files) {
|
|
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
|
|
if (!long.TryParse(fileNameWithoutExtension.Split('.')[0], out fileNameFirstSegment)) {
|
|
continue;
|
|
}
|
|
if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection)) {
|
|
keyValuePairs.Add(fileNameFirstSegment, []);
|
|
if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection)) {
|
|
throw new Exception();
|
|
}
|
|
}
|
|
collection.Add(file);
|
|
}
|
|
foreach (KeyValuePair<long, List<string>> keyValuePair in keyValuePairs) {
|
|
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
|
|
}
|
|
return results.AsReadOnly();
|
|
}
|
|
|
|
private static ReadOnlyDictionary<string, FaceFile> GetKeyValuePairs(ILogger<Worker> logger, ReadOnlyCollection<ExifDirectory> exifDirectories) {
|
|
Dictionary<string, FaceFile> results = [];
|
|
string personKey;
|
|
FaceFile? faceFile;
|
|
foreach (ExifDirectory exifDirectory in exifDirectories) {
|
|
faceFile = IMetadata.GetFaceFile(exifDirectory);
|
|
if (faceFile is null) {
|
|
logger.LogError("faceFile is null!");
|
|
} else if (faceFile.Location is null) {
|
|
logger.LogError("faceFile location is null!");
|
|
} else if (faceFile.OutputResolution?.Orientation is not 0 and not 1) {
|
|
logger.LogWarning("faceFile output-resolution orientation is not aloud!");
|
|
} else {
|
|
personKey = Path.GetFileName(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(exifDirectory.FilePath.FullName))));
|
|
if (results.ContainsKey(personKey)) {
|
|
continue;
|
|
}
|
|
results.Add(personKey, faceFile);
|
|
}
|
|
}
|
|
return results.AsReadOnly();
|
|
}
|
|
|
|
private static ReadOnlyDictionary<long, ReadOnlyCollection<ExifDirectory>> GetKeyValuePairs(string searchPattern, string pathRoot, ResultSettings resultSettings, MetadataSettings metadataSettings) {
|
|
Dictionary<long, ReadOnlyCollection<ExifDirectory>> results = [];
|
|
FileInfo faceFileInfo;
|
|
long fileNameFirstSegment;
|
|
ExifDirectory? exifDirectory;
|
|
List<ExifDirectory>? collection;
|
|
string fileNameWithoutExtension;
|
|
Dictionary<long, List<ExifDirectory>> keyValuePairs = [];
|
|
string[] files = Directory.GetFiles(pathRoot, searchPattern, SearchOption.AllDirectories);
|
|
foreach (string file in files) {
|
|
faceFileInfo = new(file);
|
|
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(faceFileInfo.FullName);
|
|
if (!long.TryParse(fileNameWithoutExtension.Split('.')[0], out fileNameFirstSegment)) {
|
|
continue;
|
|
}
|
|
exifDirectory = IMetadata.GetExifDirectory(resultSettings, metadataSettings, faceFileInfo);
|
|
if (exifDirectory is null)
|
|
continue;
|
|
if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection)) {
|
|
keyValuePairs.Add(fileNameFirstSegment, []);
|
|
if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection))
|
|
throw new Exception();
|
|
}
|
|
collection.Add(exifDirectory);
|
|
}
|
|
foreach (KeyValuePair<long, List<ExifDirectory>> keyValuePair in keyValuePairs) {
|
|
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
|
|
}
|
|
return results.AsReadOnly();
|
|
}
|
|
|
|
private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, ReadOnlyDictionary<long, ReadOnlyCollection<ExifDirectory>> keyValuePairs, ReadOnlyDictionary<long, ReadOnlyCollection<string>> keyValuePairsXMP) {
|
|
ReadOnlyCollection<string>? digiKamFiles;
|
|
foreach (KeyValuePair<long, ReadOnlyCollection<ExifDirectory>> keyValuePair in keyValuePairs) {
|
|
if (!keyValuePairsXMP.TryGetValue(keyValuePair.Key, out digiKamFiles)) {
|
|
logger.LogWarning("{fileNameFirstSegment}) Didn't find a matching file!", keyValuePair.Key);
|
|
} else {
|
|
string? originalFile = null;
|
|
WriteFaceData(logger, outputDirectoryName, originalFile, keyValuePair.Key, keyValuePair.Value, digiKamFiles);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, string? originalFile, long? fileNameFirstSegment, ReadOnlyCollection<string> digiKamFiles, ReadOnlyDictionary<string, FaceFile> keyValuePairs) {
|
|
#if xmp
|
|
IXmpMeta xmp;
|
|
using FileStream stream = File.OpenRead(digiKamFile);
|
|
xmp = XmpMetaFactory.Parse(stream);
|
|
foreach (var property in xmp.Properties) {
|
|
logger.LogDebug("Path={property.Path} Namespace={property.Namespace} Value={property.Value}", property.Path, property.Namespace, property.Value);
|
|
}
|
|
xmp.Sort();
|
|
SerializeOptions serializeOptions = new(SerializeOptions.EncodeUtf8);
|
|
string check = XmpMetaFactory.SerializeToString(xmp, serializeOptions);
|
|
File.WriteAllText(".xmp", check);
|
|
#endif
|
|
string[] requiredLines = [
|
|
"xmlns:digiKam=\"http://www.digikam.org/ns/1.0/\"",
|
|
"xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\"",
|
|
"xmlns:exif=\"http://ns.adobe.com/exif/1.0/\"",
|
|
"xmlns:tiff=\"http://ns.adobe.com/tiff/1.0/\"",
|
|
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\"",
|
|
"xmlns:acdsee=\"http://ns.acdsee.com/iptc/1.0/\"",
|
|
"xmlns:lr=\"http://ns.adobe.com/lightroom/1.0/\"",
|
|
"xmlns:MP=\"http://ns.microsoft.com/photo/1.2/\"",
|
|
"xmlns:stArea=\"http://ns.adobe.com/xmp/sType/Area#\"",
|
|
"xmlns:photoshop=\"http://ns.adobe.com/photoshop/1.0/\"",
|
|
"xmlns:MicrosoftPhoto=\"http://ns.microsoft.com/photo/1.0/\"",
|
|
"xmlns:MPReg=\"http://ns.microsoft.com/photo/1.2/t/Region#\"",
|
|
"xmlns:stDim=\"http://ns.adobe.com/xap/1.0/sType/Dimensions#\"",
|
|
"xmlns:MPRI=\"http://ns.microsoft.com/photo/1.2/t/RegionInfo#\"",
|
|
"xmlns:mediapro=\"http://ns.iview-multimedia.com/mediapro/1.0/\"",
|
|
"xmlns:mwg-rs=\"http://www.metadataworkinggroup.com/schemas/regions/\"",
|
|
"</rdf:RDF>"
|
|
];
|
|
foreach (string digiKamFile in digiKamFiles) {
|
|
string[] lines = File.ReadAllLines(digiKamFile);
|
|
List<string> trimmed = lines.Select(l => l.Trim()).ToList();
|
|
int? digiKamLine = GetMatchingLine(requiredLines[0], trimmed.AsReadOnly());
|
|
if (digiKamLine is null) {
|
|
logger.LogError("{fileNameFirstSegment}) Didn't fine digiKam line!", fileNameFirstSegment);
|
|
} else {
|
|
foreach (string requiredLine in requiredLines) {
|
|
if (!trimmed.Contains(requiredLine)) {
|
|
trimmed.Insert(digiKamLine.Value + 1, requiredLine);
|
|
}
|
|
}
|
|
int? rdfLine = GetMatchingLine(requiredLines[^1], trimmed.AsReadOnly());
|
|
if (rdfLine is null) {
|
|
logger.LogError("{fileNameFirstSegment}) Didn't fine description line!", fileNameFirstSegment);
|
|
} else {
|
|
WriteFaceData(outputDirectoryName, originalFile, keyValuePairs, digiKamFile, trimmed, rdfLine.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void Extract(string file, double width, double height, int left, int top, string suffix) {
|
|
#if SystemDrawingCommon
|
|
Rectangle rectangle = new(left, top, width, height);
|
|
using (Bitmap source = new(file)) {
|
|
using (Bitmap bitmap = new(width, height)) {
|
|
using (Graphics graphics = Graphics.FromImage(bitmap)) {
|
|
graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
|
|
}
|
|
bitmap.Save($"{file}{suffix}");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} |