faster calibration curve generation

jellyfin intergration
This commit is contained in:
2026-06-12 17:54:23 +02:00
parent d753062c6c
commit a1d6759abc
17 changed files with 1379 additions and 166 deletions
+31 -11
View File
@@ -18,7 +18,16 @@ using json = nlohmann::json;
// KPN sink node: accumulates SceneAnnotations and writes the final JSON on EOF.
//
// Verbosity::minimal — merges per-frame presence into contiguous time windows.
// Output: { "movie": "...", "actors": [{ "name", "imdb_id", "scenes": [[t0,t1], ...] }] }
// Output: {
// "schema_version": 1, "movie": "...", "sample_fps": ..., "anneal_sec": ...,
// "actors": [{ "name", "imdb_id", "tmdb_id", "jellyfin_id", "scenes": [[t0,t1], ...] }]
// }
// This is the spec consumed by the Jellyfin plugin: each actor carries every
// identity key the gallery knows (empty string if not resolved). The plugin
// should prefer "jellyfin_id" (direct Person item GUID) when non-empty, and
// otherwise resolve "imdb_id"/"tmdb_id" against the item's People ProviderIds.
// To find who's on screen at timestamp t, scan each actor's "scenes" for a
// window where start <= t <= end.
//
// Verbosity::standard — per-frame detail including bboxes, similarity, unknowns.
// Output: { "frames": [{ "t", "identified": [...], "unknowns": [...] }] }
@@ -55,6 +64,10 @@ struct ResultSinkFunc {
}
private:
// Bump when the minimal/standard output JSON structure changes in a way
// the Jellyfin plugin needs to detect.
static constexpr int kSchemaVersion = 1;
static int count_known(const std::vector<IdentifiedActor>& v) {
int n = 0;
for (const auto& a : v) if (a.actor_idx >= 0) ++n;
@@ -73,6 +86,7 @@ private:
if (cfg_.verbosity == Verbosity::xray) {
root = build_xray();
} else {
root["schema_version"] = kSchemaVersion;
root["movie"] = cfg_.movie_path;
root["sample_fps"] = cfg_.sample_fps;
root["anneal_sec"] = cfg_.anneal_sec;
@@ -91,20 +105,20 @@ private:
}
struct ActorWindow {
std::string name, imdb_id;
std::string name, imdb_id, tmdb_id, jellyfin_id;
std::vector<std::pair<double, double>> scenes; // [start_sec, end_sec]
};
// Core logic: merge per-frame detections into annealed [start, end] windows.
std::vector<ActorWindow> build_actor_windows() {
struct Info { std::string name, imdb_id; };
struct Info { std::string name, imdb_id, tmdb_id, jellyfin_id; };
std::map<int, Info> actor_info;
std::map<int, std::vector<double>> timestamps;
for (const auto& frame : frames_) {
for (const auto& ia : frame.visible_actors) {
if (ia.actor_idx < 0) continue;
actor_info[ia.actor_idx] = {ia.name, ia.imdb_id};
actor_info[ia.actor_idx] = {ia.name, ia.imdb_id, ia.tmdb_id, ia.jellyfin_id};
timestamps[ia.actor_idx].push_back(frame.timestamp_sec);
}
}
@@ -112,8 +126,10 @@ private:
std::vector<ActorWindow> result;
for (auto& [idx, ts_vec] : timestamps) {
ActorWindow aw;
aw.name = actor_info[idx].name;
aw.imdb_id = actor_info[idx].imdb_id;
aw.name = actor_info[idx].name;
aw.imdb_id = actor_info[idx].imdb_id;
aw.tmdb_id = actor_info[idx].tmdb_id;
aw.jellyfin_id = actor_info[idx].jellyfin_id;
double win_start = ts_vec[0], win_end = ts_vec[0];
for (size_t i = 1; i < ts_vec.size(); ++i) {
@@ -136,9 +152,11 @@ private:
for (const auto& [s, e] : aw.scenes)
windows.push_back({s, e});
json ja;
ja["name"] = aw.name;
ja["imdb_id"] = aw.imdb_id;
ja["scenes"] = std::move(windows);
ja["name"] = aw.name;
ja["imdb_id"] = aw.imdb_id;
ja["tmdb_id"] = aw.tmdb_id;
ja["jellyfin_id"] = aw.jellyfin_id;
ja["scenes"] = std::move(windows);
actors.push_back(std::move(ja));
}
return actors;
@@ -178,8 +196,10 @@ private:
if (ia.actor_idx >= 0) {
json ja;
ja["name"] = ia.name;
ja["imdb_id"] = ia.imdb_id;
ja["name"] = ia.name;
ja["imdb_id"] = ia.imdb_id;
ja["tmdb_id"] = ia.tmdb_id;
ja["jellyfin_id"] = ia.jellyfin_id;
ja["similarity"] = ia.similarity;
ja["track_id"] = ia.track_id;
ja["bbox"] = jbox;