Face Recognition / People Integration #61

Merged
cameron merged 23 commits from feature/face-recog-phase3-file-watch into master 2026-04-30 17:22:09 +00:00
Showing only changes of commit 5a2f406429 - Show all commits

View File

@@ -2234,6 +2234,17 @@ async fn update_face_handler<D: FaceDao>(
orientation: None,
model_version: Some(current.model_version.clone()),
};
// Soft contract on the re-embed: we'd LIKE a fresh ArcFace
// vector for the new crop, but the operator's bbox edit is
// sacred. If detection finds no face in the new region (they
// dragged the box slightly off-center, or moved it to a back-
// of-head shot they've already manually tagged), or returns a
// bad embedding, we keep the old embedding and apply the bbox
// anyway. Cost: stale embedding for that row, which slightly
// pollutes clustering for files re-detected against this
// person — accepted because dropping the user's drag is a
// worse UX. Transient failures (cuda_oom, engine unavailable)
// still 503 so the operator can retry once Apollo recovers.
match face_client.embed(crop_bytes, meta).await {
Ok(resp) => {
if let Some(face) = resp.faces.first() {
@@ -2241,20 +2252,23 @@ async fn update_face_handler<D: FaceDao>(
Ok(b) => new_embedding = Some(b),
Err(e) => {
warn!(
"PATCH /image/faces/{}: bad embedding from face service: {:?}",
"PATCH /image/faces/{}: bad embedding from face service ({:?}); keeping old embedding, bbox still applied",
id, e
);
return HttpResponse::BadGateway()
.body("invalid embedding from face service");
}
}
} else {
return HttpResponse::UnprocessableEntity()
.body("no face in new bbox");
info!(
"PATCH /image/faces/{}: no face detected in new bbox — keeping old embedding, bbox still applied",
id
);
}
}
Err(FaceDetectError::Permanent(e)) => {
return HttpResponse::UnprocessableEntity().body(format!("{}", e));
info!(
"PATCH /image/faces/{}: embed permanent error ({}); keeping old embedding, bbox still applied",
id, e
);
}
Err(FaceDetectError::Transient(e)) => {
return HttpResponse::ServiceUnavailable().body(format!("{}", e));