Add quality replacement for Frigate face training images

When a person is at MAX_AUTO_IMAGES, winnow now replaces the
lowest-quality mapped training image in Frigate if a higher-confidence
candidate is available, keeping the training set always optimised.

Only files winnow uploaded (tracked via frigate_files mapping) are ever
replaced — manually added Frigate training images are never touched.
A concurrent-upload race condition is detected per-file: if N>1 new
files appear after one upload, the mapping is skipped rather than
guessed, logging at INFO level. The per-file snapshot approach is
retained over a batch approach because wrong mappings (which a batch
approach risks on race) are worse than no mapping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 03:44:36 +00:00
co-authored by Claude Sonnet 4.6
parent 927ad3a68c
commit 129c74720e
8 changed files with 279 additions and 33 deletions
+21 -11
View File
@@ -293,24 +293,34 @@ def auto_configure(people: list[dict]) -> list[dict]:
already_uploaded = fc if fc is not None else person_summary.get("uploaded", 0)
capacity = Config.MAX_AUTO_IMAGES - already_uploaded
if capacity <= 0:
if not Config.QUALITY_REPLACEMENT:
rprint(
f" [dim]Skipping {name} (at cap:"
f" {already_uploaded}/{Config.MAX_AUTO_IMAGES}, quality replacement disabled).[/dim]"
)
continue
rprint(
f" [dim]Skipping {name} (at lifetime cap:"
f" {already_uploaded}/{Config.MAX_AUTO_IMAGES} trained).[/dim]"
f" [cyan]{name}: at cap ({already_uploaded}/{Config.MAX_AUTO_IMAGES}),"
f" checking for quality improvements...[/cyan]"
)
continue
quality_replacement_only = True
else:
quality_replacement_only = False
config["quality_replacement"] = quality_replacement_only or Config.QUALITY_REPLACEMENT
has_embedding = is_embedding_available(entity_type)
limit, selection_mode = _resolve_strategy(strategy, has_embedding)
# Cap selection to remaining capacity.
# For auto mode with partial training, keep "auto" so adaptive stopping
# still runs — just trim the result to the remaining capacity afterward.
# Cap selection to remaining capacity (no cap when replacement-only — executor
# decides per-image whether to swap; any candidate could be an improvement).
auto_cap = None
if limit == "auto":
if already_uploaded > 0:
auto_cap = capacity
else:
limit = min(limit, capacity)
if not quality_replacement_only:
if limit == "auto":
if already_uploaded > 0:
auto_cap = capacity
else:
limit = min(limit, capacity)
if selection_mode == "skip":
continue