feat: store Frigate recognition scores and use them for quality replacement

After each successful upload, call POST /api/faces/recognize to get
Frigate's own confidence score (0-1) for the uploaded crop. Store it
in the tracker as frigate_scores alongside the existing blur score.

When quality replacement activates and frigate_scores are present,
use them for the replacement comparison instead of blur scores — an
image Frigate recognizes poorly is a worse training image than one it
recognizes well, regardless of sharpness. Falls back to blur scores
on first run before any frigate_scores are populated.

Also surfaces frigate_score in TRACE_CROP_SIZE output.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 16:21:58 +00:00
co-authored by Claude Sonnet 4.6
parent ab641847b2
commit 03be6ce2cb
4 changed files with 115 additions and 23 deletions
+27
View File
@@ -53,6 +53,33 @@ def get_frigate_person_files(person_name: str) -> list[str] | None:
return files if isinstance(files, list) else []
def recognize_face(file_path: str) -> float | None:
"""Submit an image to Frigate's recognize endpoint and return the confidence score.
Returns None if FRIGATE_URL is unset, the API is unreachable, no face is
detected, or face recognition is not enabled in Frigate.
"""
frigate_url = os.environ.get("FRIGATE_URL", "").rstrip("/")
if not frigate_url:
return None
try:
with open(file_path, "rb") as f:
resp = requests.post(
f"{frigate_url}/api/faces/recognize",
files={"file": (os.path.basename(file_path), f, "image/jpeg")},
timeout=15,
)
if not resp.ok:
return None
data = resp.json()
if data.get("success") and "score" in data:
return round(float(data["score"]), 4)
return None
except Exception as e:
logger.debug(f"Frigate recognize failed for {file_path}: {e}")
return None
def delete_frigate_person_files(person_name: str, filenames: list[str]) -> bool:
"""Delete specific training files for a person from Frigate.