PhotoPrism for more locations

This commit is contained in:
2022-12-25 13:54:17 -07:00
parent a510019c1d
commit 37c7b6760d
31 changed files with 395 additions and 509 deletions

View File

@ -1,54 +0,0 @@
using System.Text.Json.Serialization;
namespace View_by_Distance.Map.Models;
public record DatabaseFile(
[property: JsonPropertyName("id")] int Id,
[property: JsonPropertyName("photo_id")] int PhotoId,
[property: JsonPropertyName("photo_uid")] string PhotoUid,
[property: JsonPropertyName("photo_taken_at")] string PhotoTakenAt,
[property: JsonPropertyName("time_index")] string TimeIndex,
[property: JsonPropertyName("media_id")] string MediaId,
[property: JsonPropertyName("media_utc")] object MediaUtc,
[property: JsonPropertyName("instance_id")] string InstanceId,
[property: JsonPropertyName("file_uid")] string FileUid,
[property: JsonPropertyName("file_name")] string FileName,
[property: JsonPropertyName("file_root")] string FileRoot,
[property: JsonPropertyName("original_name")] string OriginalName,
[property: JsonPropertyName("file_hash")] string FileHash,
[property: JsonPropertyName("file_size")] int FileSize,
[property: JsonPropertyName("file_codec")] string FileCodec,
[property: JsonPropertyName("file_type")] string FileType,
[property: JsonPropertyName("media_type")] string MediaType,
[property: JsonPropertyName("file_mime")] string FileMime,
[property: JsonPropertyName("file_primary")] long FilePrimary,
[property: JsonPropertyName("file_sidecar")] long FileSidecar,
[property: JsonPropertyName("file_missing")] long FileMissing,
[property: JsonPropertyName("file_portrait")] long FilePortrait,
[property: JsonPropertyName("file_video")] long FileVideo,
[property: JsonPropertyName("file_duration")] long FileDuration,
[property: JsonPropertyName("file_fps")] object FileFps,
[property: JsonPropertyName("file_frames")] object FileFrames,
[property: JsonPropertyName("file_width")] int FileWidth,
[property: JsonPropertyName("file_height")] int FileHeight,
[property: JsonPropertyName("file_orientation")] int FileOrientation,
[property: JsonPropertyName("file_projection")] string FileProjection,
[property: JsonPropertyName("file_aspect_ratio")] double FileAspectRatio,
[property: JsonPropertyName("file_hdr")] long FileHdr,
[property: JsonPropertyName("file_watermark")] long FileWatermark,
[property: JsonPropertyName("file_color_profile")] string FileColorProfile,
[property: JsonPropertyName("file_main_color")] string FileMainColor,
[property: JsonPropertyName("file_colors")] string FileColors,
[property: JsonPropertyName("file_luminance")] string FileLuminance,
[property: JsonPropertyName("file_diff")] long FileDiff,
[property: JsonPropertyName("file_chroma")] long FileChroma,
[property: JsonPropertyName("file_software")] string FileSoftware,
[property: JsonPropertyName("file_error")] string FileError,
[property: JsonPropertyName("mod_time")] long ModTime,
[property: JsonPropertyName("created_at")] string CreatedAt,
[property: JsonPropertyName("created_in")] long CreatedIn,
[property: JsonPropertyName("updated_at")] string UpdatedAt,
[property: JsonPropertyName("updated_in")] long UpdatedIn,
[property: JsonPropertyName("published_at")] object PublishedAt,
[property: JsonPropertyName("deleted_at")] object DeletedAt
);

View File

@ -1,8 +0,0 @@
using System.Text.Json.Serialization;
namespace View_by_Distance.Map.Models;
public record DatabaseFileRoot(
[property: JsonPropertyName("table")] string Table,
[property: JsonPropertyName("rows")] IReadOnlyList<DatabaseFile> Files
);

View File

@ -936,7 +936,7 @@ public class MapLogic
return results;
}
public void SaveFilteredOriginalImagesFromJLinks(string[] jLinks, string a2PeopleSingletonDirectory, PersonContainer[] personContainers, Mapping[] mappingCollection, Dictionary<long, int> personKeyToCount, int totalNotMapped)
public void SaveFilteredOriginalImagesFromJLinks(string[] jLinks, PersonContainer[] personContainers, string a2PeopleSingletonDirectory, Mapping[] mappingCollection, Dictionary<long, int> personKeyToCount, int totalNotMapped)
{
if (_Configuration is null)
throw new NullReferenceException(nameof(_Configuration));
@ -1149,237 +1149,166 @@ public class MapLogic
}
}
private DatabaseFileRoot GetDatabaseFileRoot()
private static JsonProperty[] GetJsonProperty(string fileName)
{
if (_Configuration is null)
throw new NullReferenceException(nameof(_Configuration));
string file = Path.Combine(_Configuration.PhotoPrismDirectory, "files.json");
string json = File.ReadAllText(file);
DatabaseFileRoot? databaseFileRoot = JsonSerializer.Deserialize<DatabaseFileRoot>(json);
if (databaseFileRoot is null)
throw new NullReferenceException(nameof(databaseFileRoot));
return databaseFileRoot;
}
private Marker[] GetMarkers()
{
if (_Configuration is null)
throw new NullReferenceException(nameof(_Configuration));
string file = Path.Combine(_Configuration.PhotoPrismDirectory, "markers.json");
string json = File.ReadAllText(file);
Marker[]? markerRoot = JsonSerializer.Deserialize<Marker[]>(json);
if (markerRoot is null)
throw new NullReferenceException(nameof(markerRoot));
return markerRoot;
}
private static Dictionary<string, DatabaseFile> Get(DatabaseFileRoot databaseFileRoot)
{
Dictionary<string, DatabaseFile> fileUidToFile = new();
for (int i = 0; i < databaseFileRoot.Files.Count; i++)
fileUidToFile.Add(databaseFileRoot.Files[i].FileUid, databaseFileRoot.Files[i]);
return fileUidToFile;
}
private static MarkerWith GetMarkerWith(int? dlib, DatabaseFile databaseFile, Marker marker, int? count, double? percent, int? normalizedRectangle, long? personKey, string personKeyFormatted)
{
return new(marker.MarkerUid,
marker.FileUid,
marker.MarkerType,
marker.MarkerSrc,
marker.MarkerName,
marker.MarkerReview,
marker.MarkerInvalid,
marker.SubjUid,
marker.SubjSrc,
marker.FaceId,
marker.FaceDist,
marker.EmbeddingsJson,
marker.LandmarksJson,
marker.X,
marker.Y,
marker.W,
marker.H,
marker.Q,
marker.Size,
marker.Score,
marker.Thumb,
marker.MatchedAt,
marker.CreatedAt,
marker.UpdatedAt,
databaseFile.Id,
databaseFile.FileName,
dlib,
count,
percent,
normalizedRectangle,
personKey,
personKeyFormatted);
}
private static Dictionary<string, List<Face>> GetFacesByFileName(List<Item> filteredItems)
{
Dictionary<string, List<Face>> results = new();
string key;
foreach (Item item in filteredItems)
{
foreach (Face face in item.Faces)
{
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null || face.Mapping is null)
continue;
key = Path.GetFileNameWithoutExtension(face.Mapping.MappingFromItem.RelativePath);
if (!results.ContainsKey(key))
results.Add(key, new());
results[key].Add(face);
}
}
JsonProperty[] results;
string json = File.ReadAllText(fileName);
JsonElement? jsonElement = JsonSerializer.Deserialize<JsonElement>(json);
results = jsonElement.Value.EnumerateObject().ToArray();
return results;
}
public void FindMatch(List<Item> filteredItems)
private static Marker[]? GetMarkers(string photoPrismDirectory)
{
if (_Configuration is null)
throw new NullReferenceException(nameof(_Configuration));
int? dlib;
double? percent;
long? personKey;
const int zero = 0;
List<Face>? matches;
MarkerWith markerWith;
int? normalizedRectangle;
string personKeyFormatted;
DatabaseFile? databaseFile;
PersonBirthday personBirthday;
Marker[] markers = GetMarkers();
string fileNameWithoutExtension;
PersonContainer[]? personContainers;
System.Drawing.Rectangle dlibRectangle;
System.Drawing.Rectangle prismRectangle;
System.Drawing.Rectangle intersectRectangle;
(Face Face, double Percent)[] sortedCollection;
List<(Face Face, double Percent)> collection = new();
DatabaseFileRoot databaseFileRoot = GetDatabaseFileRoot();
Dictionary<string, DatabaseFile> fileUidToFile = Get(databaseFileRoot);
Dictionary<int, PersonContainer[]>? normalizedRectangleToPersonContainers;
Dictionary<string, List<Face>> keyValuePairs = GetFacesByFileName(filteredItems);
foreach (Marker marker in markers)
{
dlib = null;
personKey = null;
collection.Clear();
normalizedRectangle = null;
personKeyFormatted = string.Empty;
normalizedRectangleToPersonContainers = null;
if (!fileUidToFile.TryGetValue(marker.FileUid, out databaseFile))
continue;
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(Path.Combine("C:", databaseFile.FileName));
prismRectangle = new((int)(marker.X * databaseFile.FileWidth), (int)(marker.Y * databaseFile.FileHeight), (int)(marker.W * databaseFile.FileWidth), (int)(marker.H * databaseFile.FileHeight));
if (!keyValuePairs.TryGetValue(fileNameWithoutExtension, out matches) || !int.TryParse(fileNameWithoutExtension, out int id))
percent = null;
else
{
dlib = id;
_ = _IdThenNormalizedRectangleToPersonContainers.TryGetValue(dlib.Value, out normalizedRectangleToPersonContainers);
foreach (Face face in matches)
{
if (face.Location is null || face.OutputResolution is null)
continue;
dlibRectangle = new(face.Location.Left, face.Location.Top, face.Location.Right - face.Location.Left, face.Location.Bottom - face.Location.Top);
intersectRectangle = System.Drawing.Rectangle.Intersect(prismRectangle, dlibRectangle);
if (intersectRectangle.Width == 0 || intersectRectangle.Height == 0)
continue;
percent = (double)intersectRectangle.Width * intersectRectangle.Height / (dlibRectangle.Width * dlibRectangle.Height);
if (percent < 0.000001)
continue;
collection.Add(new(face, percent.Value));
}
}
if (!collection.Any())
percent = null;
else
{
sortedCollection = collection.OrderByDescending(l => l.Percent).ToArray();
percent = sortedCollection[zero].Percent;
normalizedRectangle = sortedCollection[zero].Face.Mapping?.MappingFromLocation.NormalizedRectangle;
if (normalizedRectangleToPersonContainers is null || normalizedRectangle is null || !normalizedRectangleToPersonContainers.TryGetValue(normalizedRectangle.Value, out personContainers))
personContainers = null;
else
{
foreach (PersonContainer personContainer in personContainers)
{
if (personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any())
continue;
personBirthday = personContainer.Birthdays[zero];
personKey = personBirthday.Value.Ticks;
personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personBirthday);
break;
}
}
}
markerWith = GetMarkerWith(dlib, databaseFile, marker, collection.Count, percent, normalizedRectangle, personKey, personKeyFormatted);
string json = JsonSerializer.Serialize(markerWith, new JsonSerializerOptions() { WriteIndented = true });
if (IPath.WriteAllText(Path.Combine(_Configuration.PhotoPrismDirectory, "With", $"{marker.MarkerUid}.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true))
continue;
}
Marker[]? results;
string file = Path.Combine(photoPrismDirectory, "markers.json");
JsonProperty[] jsonProperties = GetJsonProperty(file);
results = JsonSerializer.Deserialize<Marker[]>(jsonProperties[1].Value);
return results;
}
public void SaveMarkers()
public static void SaveMarkers(string photoPrismDirectory)
{
if (_Configuration is null)
throw new NullReferenceException(nameof(_Configuration));
double[]? encoding;
string file = Path.Combine(_Configuration.PhotoPrismDirectory, "markers.json");
string json = File.ReadAllText(file);
Marker[]? markers = JsonSerializer.Deserialize<Marker[]>(json);
if (string.IsNullOrEmpty(photoPrismDirectory))
throw new Exception();
// double[]? encoding;
// Marker[]? markers = GetMarkers(photoPrismDirectory);
// if (markers is null)
// throw new NullReferenceException(nameof(markers));
// foreach (Marker marker in markers)
// {
// encoding = JsonSerializer.Deserialize<double[]>(marker.EmbeddingsJson[1..^1]);
// File.WriteAllText(Path.Combine(photoPrismDirectory, "EmbeddingsJson", $"{marker.MarkerUid}.json"), marker.EmbeddingsJson);
// if (encoding is null)
// continue;
// }
}
private static Dictionary<string, List<Marker>> GetFileUIdToMarkers(string photoPrismDirectory)
{
Dictionary<string, List<Marker>> results = new();
Marker[]? markers = GetMarkers(photoPrismDirectory);
if (markers is null)
throw new NullReferenceException(nameof(markers));
foreach (Marker marker in markers)
{
encoding = JsonSerializer.Deserialize<double[]>(marker.EmbeddingsJson[1..^1]);
File.WriteAllText(Path.Combine(_Configuration.PhotoPrismDirectory, "EmbeddingsJson", $"{marker.MarkerUid}.json"), marker.EmbeddingsJson);
if (encoding is null)
continue;
if (!results.ContainsKey(marker.FileUid))
results.Add(marker.FileUid, new());
results[marker.FileUid].Add(marker);
}
return results;
}
public void LoadMatches()
private static DatabaseFile[]? GetDatabaseFiles(string photoPrismDirectory)
{
DatabaseFile[]? results;
string file = Path.Combine(photoPrismDirectory, "files.json");
JsonProperty[] jsonProperties = GetJsonProperty(file);
results = JsonSerializer.Deserialize<DatabaseFile[]>(jsonProperties[1].Value);
return results;
}
public static Dictionary<string, List<MappingFromPhotoPrism>> GetFileNameToCollection(string photoPrismDirectory)
{
Dictionary<string, List<MappingFromPhotoPrism>> results = new();
List<Marker>? makers;
MappingFromPhotoPrism mappingFromPhotoPrism;
List<MappingFromPhotoPrism>? mappingFromPhotoPrismCollection;
DatabaseFile[]? databaseFiles = GetDatabaseFiles(photoPrismDirectory);
if (databaseFiles is null)
throw new NullReferenceException(nameof(databaseFiles));
Dictionary<string, List<Marker>> fileUIdToMarkers = GetFileUIdToMarkers(photoPrismDirectory);
foreach (DatabaseFile databaseFile in databaseFiles)
{
if (!results.TryGetValue(databaseFile.FileName, out mappingFromPhotoPrismCollection))
{
results.Add(databaseFile.FileName, new());
if (!results.TryGetValue(databaseFile.FileName, out mappingFromPhotoPrismCollection))
throw new Exception();
}
if (!fileUIdToMarkers.TryGetValue(databaseFile.FileUid, out makers))
mappingFromPhotoPrism = new(databaseFile, new());
else
mappingFromPhotoPrism = new(databaseFile, makers);
mappingFromPhotoPrismCollection.Add(mappingFromPhotoPrism);
}
return results;
}
public void WriteMatches(long ticks, Face[] distinctFilteredFaces)
{
if (_Configuration is null)
throw new NullReferenceException(nameof(_Configuration));
string json;
MarkerWith? markerWith;
List<MarkerWith> collection = new();
long? personKey;
const int zero = 0;
int? normalizedRectangle;
string personKeyFormatted;
List<string> subjects = new();
PersonBirthday personBirthday;
string personDisplayDirectoryName;
StringBuilder stringBuilder = new();
List<(int Count, MarkerWith MarkerWith)> countCollection = new();
List<(double Percent, MarkerWith MarkerWith)> percentCollection = new();
string[] files = Directory.GetFiles(Path.Combine(_Configuration.PhotoPrismDirectory, "With"), "*.json", SearchOption.TopDirectoryOnly);
foreach (string file in files)
PersonContainer[]? personContainers;
System.Drawing.Rectangle dlibRectangle;
System.Drawing.Rectangle? prismRectangle;
System.Drawing.Rectangle intersectRectangle;
Dictionary<int, PersonContainer[]>? normalizedRectangleToPersonContainers;
(MappingFromPhotoPrism MappingFromPhotoPrism, Marker Marker, double Percent)[] sortedCollection;
List<(MappingFromPhotoPrism MappingFromPhotoPrism, Marker Marker, double Percent)> collection = new();
foreach (Face face in distinctFilteredFaces)
{
json = File.ReadAllText(file);
markerWith = JsonSerializer.Deserialize<MarkerWith>(json);
if (markerWith is null || markerWith.DlibId is null)
collection.Clear();
normalizedRectangle = face.Mapping?.MappingFromLocation.NormalizedRectangle;
if (normalizedRectangle is null)
continue;
collection.Add(markerWith);
if (markerWith.Count is null || markerWith.Count.Value == 0)
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null || face.Mapping is null)
continue;
countCollection.Add(new(markerWith.Count.Value, markerWith));
if (markerWith.Percent is null)
if (face.Mapping.MappingFromPhotoPrismCollection is null)
continue;
percentCollection.Add(new(markerWith.Percent.Value, markerWith));
if (string.IsNullOrEmpty(markerWith.PersonKeyFormatted))
_ = _IdThenNormalizedRectangleToPersonContainers.TryGetValue(face.Mapping.MappingFromItem.Id, out normalizedRectangleToPersonContainers);
if (normalizedRectangleToPersonContainers is null || !normalizedRectangleToPersonContainers.TryGetValue(normalizedRectangle.Value, out personContainers))
continue;
_ = stringBuilder.
Append("update `markers` set subj_src = 'manual' marker_name = '").
Append(markerWith.PersonKeyFormatted).
Append("' where marker_uid = '").
Append(markerWith.MarkerUid).
AppendLine("';");
dlibRectangle = new(face.Location.Left, face.Location.Top, face.Location.Right - face.Location.Left, face.Location.Bottom - face.Location.Top);
foreach (MappingFromPhotoPrism mappingFromPhotoPrism in face.Mapping.MappingFromPhotoPrismCollection)
{
foreach (Marker marker in mappingFromPhotoPrism.Markers)
{
prismRectangle = ILocation.GetRectangle(face.OutputResolution, mappingFromPhotoPrism.DatabaseFile, marker);
if (prismRectangle is null)
continue;
intersectRectangle = System.Drawing.Rectangle.Intersect(dlibRectangle, prismRectangle.Value);
if (intersectRectangle.Width == 0 || intersectRectangle.Height == 0)
continue;
double percent = (double)intersectRectangle.Width * intersectRectangle.Height / (dlibRectangle.Width * dlibRectangle.Height);
if (percent < 0.000001)
continue;
collection.Add(new(mappingFromPhotoPrism, marker, percent));
}
}
if (!collection.Any())
continue;
sortedCollection = collection.OrderByDescending(l => l.Percent).ToArray();
foreach ((MappingFromPhotoPrism mappingFromPhotoPrism, Marker marker, double percent) in sortedCollection)
{
foreach (PersonContainer personContainer in personContainers)
{
if (personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any())
continue;
personBirthday = personContainer.Birthdays[zero];
personKey = personBirthday.Value.Ticks;
personDisplayDirectoryName = personContainer.DisplayDirectoryName;
personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personBirthday);
subjects.Add($"update `subjects` set subj_alias = '{personKeyFormatted}' where subj_name = '{personDisplayDirectoryName}';");
_ = stringBuilder.
Append("update `markers` set subj_src = 'manual', marker_name = '").
Append(personDisplayDirectoryName).
Append("' where marker_uid = '").
Append(marker.MarkerUid).
AppendLine("';");
}
}
}
(int, MarkerWith)[] countSorted = countCollection.OrderByDescending(l => l.Count).ToArray();
(double, MarkerWith)[] percentSorted = percentCollection.OrderBy(l => l.Percent).ToArray();
if (collection.Any())
{ }
File.WriteAllText(Path.Combine(_Configuration.PhotoPrismDirectory, "marker_name_update.sql"), stringBuilder.ToString());
File.WriteAllLines(Path.Combine(_Configuration.PhotoPrismDirectory, $"{ticks}-subject_alias_update.sql"), subjects.Distinct());
File.WriteAllText(Path.Combine(_Configuration.PhotoPrismDirectory, $"{ticks}-marker_name_update.sql"), stringBuilder.ToString());
}
public Dictionary<int, Dictionary<int, PersonContainer[]>> GetMissing(Dictionary<int, Dictionary<int, Mapping>> idToNormalizedRectangleToMapping)

View File

@ -189,7 +189,7 @@ internal abstract class MapLogic
return results;
}
internal static List<(string, string[], string)> DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory, PersonContainer[] personContainers)
internal static List<(string, string[], string)> DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, PersonContainer[] personContainers, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory)
{
List<(string, string[], string)> results;
string personKeyFormatted;

View File

@ -3,9 +3,9 @@ namespace View_by_Distance.Map.Models.Stateless.Methods;
public interface IMapLogic
{ // ...
List<(string, string[], string)> TestStatic_DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory, Shared.Models.PersonContainer[] personContainers) =>
DeleteEmptyDirectoriesAndGetMappedFaceFiles(configuration, ticks, a2PeopleContentDirectory, eDistanceContentDirectory, personContainers);
static List<(string, string[], string)> DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory, Shared.Models.PersonContainer[] personContainers) =>
MapLogic.DeleteEmptyDirectoriesAndGetMappedFaceFiles(configuration, ticks, a2PeopleContentDirectory, eDistanceContentDirectory, personContainers);
List<(string, string[], string)> TestStatic_DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, Shared.Models.PersonContainer[] personContainers, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory) =>
DeleteEmptyDirectoriesAndGetMappedFaceFiles(configuration, personContainers, ticks, a2PeopleContentDirectory, eDistanceContentDirectory);
static List<(string, string[], string)> DeleteEmptyDirectoriesAndGetMappedFaceFiles(Configuration configuration, Shared.Models.PersonContainer[] personContainers, long ticks, string? a2PeopleContentDirectory, string? eDistanceContentDirectory) =>
MapLogic.DeleteEmptyDirectoriesAndGetMappedFaceFiles(configuration, personContainers, ticks, a2PeopleContentDirectory, eDistanceContentDirectory);
}