Keep the dense landmarks behind each eye reading, packed
The 106 points the eye boxes were cut from, stored beside the reading as 16-bit fixed point over the frame: 424 bytes a face, a seventh of a pixel on a 6000-pixel frame, where f16 at the same size would have been six. Derived data like the embedding, kept for the same reason — it cost a fetch and a model run, and the next per-face pass should run from the catalog. Shards carry it; a peer's shard from before it is still read.
This commit is contained in:
@@ -127,6 +127,11 @@ pub struct DetectedFace {
|
||||
/// models were not present at indexing, or the face came from a peer
|
||||
/// that had none; the measuring pass fills it in (schema V16).
|
||||
pub eyes: Option<EyeReading>,
|
||||
/// The 106 dense landmarks the eyes were read from, as
|
||||
/// `dr_face::Landmarks::to_packed_bytes` — 424 bytes, or empty where
|
||||
/// the face was never read (schema V18). Kept so a later per-face pass
|
||||
/// need not fetch the original again.
|
||||
pub landmarks_dense: Vec<u8>,
|
||||
/// Which model produced the embedding. Comparing across models is the one
|
||||
/// mistake that yields plausible garbage rather than an error.
|
||||
pub model_id: String,
|
||||
@@ -161,6 +166,8 @@ pub struct Face {
|
||||
pub quality: Option<f32>,
|
||||
/// See [`DetectedFace::eyes`]. `None` for a face never read.
|
||||
pub eyes: Option<EyeReading>,
|
||||
/// See [`DetectedFace::landmarks_dense`]. Empty for a face never read.
|
||||
pub landmarks_dense: Vec<u8>,
|
||||
pub model_id: String,
|
||||
/// `None` when the face belongs to no one yet.
|
||||
pub person: Option<PersonId>,
|
||||
@@ -267,9 +274,9 @@ pub fn record_detections(
|
||||
(image_id, x, y, w, h, landmarks, detector_confidence,
|
||||
embedding, crop_px, model_id, detected_at, crop, quality,
|
||||
eye_right, eye_right_px, eye_right_sharp,
|
||||
eye_left, eye_left_px, eye_left_sharp, sunglasses)
|
||||
eye_left, eye_left_px, eye_left_sharp, sunglasses, landmarks_dense)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13,
|
||||
?14, ?15, ?16, ?17, ?18, ?19, ?20)",
|
||||
?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21)",
|
||||
rusqlite::params![
|
||||
image_id.0 as i64,
|
||||
f.x as f64,
|
||||
@@ -294,6 +301,7 @@ pub fn record_detections(
|
||||
f.eyes.map(|e| f64::from(e.left.px)),
|
||||
f.eyes.map(|e| f64::from(e.left.sharpness)),
|
||||
f.eyes.map(|e| f64::from(e.sunglasses)),
|
||||
(!f.landmarks_dense.is_empty()).then_some(f.landmarks_dense.as_slice()),
|
||||
],
|
||||
)?;
|
||||
let id = FaceId(tx.last_insert_rowid() as u64);
|
||||
@@ -354,6 +362,8 @@ pub struct Measurement {
|
||||
/// See [`DetectedFace::eyes`]. `None` where the measuring device has no
|
||||
/// eye models, in which case the stored reading is left as it was.
|
||||
pub eyes: Option<EyeReading>,
|
||||
/// See [`DetectedFace::landmarks_dense`]; written with `eyes`.
|
||||
pub landmarks_dense: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Write fresh embeddings over faces that were found before their quality was
|
||||
@@ -397,7 +407,7 @@ pub fn record_measurements(
|
||||
"UPDATE faces
|
||||
SET eye_right = ?2, eye_right_px = ?3, eye_right_sharp = ?4,
|
||||
eye_left = ?5, eye_left_px = ?6, eye_left_sharp = ?7,
|
||||
sunglasses = ?8
|
||||
sunglasses = ?8, landmarks_dense = ?9
|
||||
WHERE id = ?1",
|
||||
rusqlite::params![
|
||||
m.face.0 as i64,
|
||||
@@ -408,6 +418,7 @@ pub fn record_measurements(
|
||||
f64::from(e.left.px),
|
||||
f64::from(e.left.sharpness),
|
||||
f64::from(e.sunglasses),
|
||||
(!m.landmarks_dense.is_empty()).then_some(m.landmarks_dense.as_slice()),
|
||||
],
|
||||
)?;
|
||||
}
|
||||
@@ -619,7 +630,8 @@ pub fn for_image(conn: &Connection, image_id: ImageId) -> Result<Vec<Face>, Cata
|
||||
f.detector_confidence, f.crop_px, f.model_id,
|
||||
fp.person_id, fp.probability, fp.confirmed, f.quality,
|
||||
f.eye_right, f.eye_right_px, f.eye_right_sharp,
|
||||
f.eye_left, f.eye_left_px, f.eye_left_sharp, f.sunglasses
|
||||
f.eye_left, f.eye_left_px, f.eye_left_sharp, f.sunglasses,
|
||||
f.landmarks_dense
|
||||
FROM faces f
|
||||
LEFT JOIN face_person fp ON fp.face_id = f.id
|
||||
WHERE f.image_id = ?1
|
||||
@@ -914,7 +926,8 @@ pub fn for_person(
|
||||
f.detector_confidence, f.crop_px, f.model_id,
|
||||
fp.person_id, fp.probability, fp.confirmed, f.quality,
|
||||
f.eye_right, f.eye_right_px, f.eye_right_sharp,
|
||||
f.eye_left, f.eye_left_px, f.eye_left_sharp, f.sunglasses
|
||||
f.eye_left, f.eye_left_px, f.eye_left_sharp, f.sunglasses,
|
||||
f.landmarks_dense
|
||||
FROM faces f
|
||||
JOIN face_person fp ON fp.face_id = f.id
|
||||
WHERE fp.person_id = ?1 AND (?2 OR fp.confirmed = 1)
|
||||
@@ -1175,6 +1188,7 @@ fn read_face(r: &rusqlite::Row<'_>) -> rusqlite::Result<Face> {
|
||||
crop_px: r.get::<_, f64>(8)? as f32,
|
||||
quality: r.get::<_, Option<f64>>(13)?.map(|q| q as f32),
|
||||
eyes: read_eyes(r, 14)?,
|
||||
landmarks_dense: r.get::<_, Option<Vec<u8>>>(21)?.unwrap_or_default(),
|
||||
model_id: r.get(9)?,
|
||||
person: person.map(|p| PersonId(p as u64)),
|
||||
probability: r.get::<_, Option<f64>>(11)?.unwrap_or(0.0) as f32,
|
||||
@@ -1361,6 +1375,7 @@ mod tests {
|
||||
},
|
||||
sunglasses: 0.1,
|
||||
}),
|
||||
landmarks_dense: vec![7; 424],
|
||||
model_id: "w600k_mbf".into(),
|
||||
crop: Vec::new(),
|
||||
}
|
||||
@@ -1398,6 +1413,11 @@ mod tests {
|
||||
// And the eyes, as one reading.
|
||||
assert_eq!(got[0].eyes.map(|e| e.left.open), Some(0.8));
|
||||
assert_eq!(got[0].eyes.map(|e| e.right.sharpness), Some(0.2));
|
||||
assert_eq!(
|
||||
got[0].landmarks_dense.len(),
|
||||
424,
|
||||
"the dense landmarks ride along"
|
||||
);
|
||||
assert_eq!(
|
||||
got[0].eyes.map(|e| e.state()),
|
||||
Some(dr_face::EyeState::Open)
|
||||
@@ -1442,6 +1462,7 @@ mod tests {
|
||||
embedding: vec![9; 1024],
|
||||
quality: 21.5,
|
||||
eyes: None,
|
||||
landmarks_dense: Vec::new(),
|
||||
}],
|
||||
&[],
|
||||
)
|
||||
@@ -1471,11 +1492,17 @@ mod tests {
|
||||
},
|
||||
sunglasses: 0.0,
|
||||
}),
|
||||
landmarks_dense: vec![9; 424],
|
||||
}],
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(faces_unmeasured(&c, "w600k_mbf", true).unwrap(), 0);
|
||||
assert_eq!(
|
||||
for_image(&c, img).unwrap()[0].landmarks_dense.len(),
|
||||
424,
|
||||
"written with the reading"
|
||||
);
|
||||
let got = for_image(&c, img).unwrap();
|
||||
let read = got.iter().find(|f| f.id == ids[0]).unwrap();
|
||||
assert_eq!(
|
||||
@@ -1527,6 +1554,7 @@ mod tests {
|
||||
embedding: vec![9; 1024],
|
||||
quality: 21.5,
|
||||
eyes: None,
|
||||
landmarks_dense: Vec::new(),
|
||||
}],
|
||||
&[ids[1]],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user