Filter the grid to a person with their eyes open
An "Eyes open" chip beside the people chips, offered only while someone is chosen and dropped when the last person goes, so no term narrows the grid with nothing on the bar to say so. It compiles the rule in dr_face::eyes into the person's face subquery — Anna, eyes open, whoever else is blinking beside her — and drops a frame only on a closed eye that could be read: sunglasses, eyes too small or soft to read, and faces never read all pass, so an old library shows everything under the chip until the measuring pass has run. A test drives the same readings through the SQL and through the rule and requires them to agree. The People screen badges a face "Eyes closed", "Sunglasses" or "Eyes unclear" so the reason a frame is or is not in the grid can be read off the face; the sweep loads the three models when they are beside the pair and reads eyes on the indexing and measuring passes from the native render; the coverage line counts unread faces as work to measure so an already-indexed library keeps its Index button. The term travels with the place.
This commit is contained in:
@@ -101,6 +101,9 @@ pub struct FaceCell {
|
||||
/// of its raw embedding (`dr_face::MIN_GALLERY_QUALITY`). `None` for a
|
||||
/// face indexed before it was kept.
|
||||
pub quality: Option<f32>,
|
||||
/// TRACES: FR-CULL-13
|
||||
/// What the eyes are doing, where they were read.
|
||||
pub eyes: Option<dr_face::EyeReading>,
|
||||
}
|
||||
|
||||
impl FaceCell {
|
||||
@@ -144,6 +147,22 @@ impl FaceCell {
|
||||
pub fn in_gallery(&self) -> bool {
|
||||
dr_face::in_gallery(self.quality)
|
||||
}
|
||||
|
||||
/// The eye state as a badge — "Eyes closed", "Sunglasses" or "Eyes
|
||||
/// unclear" — or nothing.
|
||||
///
|
||||
/// Nothing for open eyes and nothing for a face never read, on the rule
|
||||
/// the confirmed marker follows: the common case carries no mark, so the
|
||||
/// marks that appear mean something. The three that do appear are the
|
||||
/// states the eyes-open filter treats differently from open — one it
|
||||
/// drops, two it lets through — and a user asking why a frame is or is
|
||||
/// not in the grid can read the answer off the face.
|
||||
pub fn eyes_label(&self) -> &'static str {
|
||||
match self.eyes.map(|e| e.state()) {
|
||||
Some(dr_face::EyeState::Open) | None => "",
|
||||
Some(state) => state.label(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Put a grouping preview into words.
|
||||
@@ -284,6 +303,7 @@ pub fn load_faces(
|
||||
probability: f.probability,
|
||||
crop_px: f.crop_px,
|
||||
quality: f.quality,
|
||||
eyes: f.eyes,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -787,6 +807,7 @@ mod tests {
|
||||
embedding: vec![seed; 1024],
|
||||
crop_px: 180.0,
|
||||
quality: Some(f32::from(seed) + 10.0),
|
||||
eyes: None,
|
||||
model_id: "w600k_mbf".into(),
|
||||
crop: Vec::new(),
|
||||
}
|
||||
@@ -835,6 +856,7 @@ mod tests {
|
||||
probability: 0.87,
|
||||
crop_px: 120.0,
|
||||
quality: Some(17.26),
|
||||
eyes: None,
|
||||
};
|
||||
assert_eq!(cell.confidence_label(), "87% likely");
|
||||
|
||||
@@ -858,6 +880,7 @@ mod tests {
|
||||
probability: 0.5,
|
||||
crop_px: 120.0,
|
||||
quality: Some(17.26),
|
||||
eyes: None,
|
||||
};
|
||||
assert_eq!(cell.quality_label(), "Quality 17.3");
|
||||
assert!(cell.in_gallery());
|
||||
@@ -880,6 +903,58 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// Only the two states the filter treats differently from open are
|
||||
/// badged; open and unread carry no mark.
|
||||
#[test]
|
||||
fn the_eye_badge_names_a_blink_or_sunglasses_and_nothing_else() {
|
||||
let eye = |open| dr_face::Eye {
|
||||
open,
|
||||
px: 40.0,
|
||||
sharpness: 0.2,
|
||||
};
|
||||
let reading = |right, left, sunglasses| {
|
||||
Some(dr_face::EyeReading {
|
||||
right: eye(right),
|
||||
left: eye(left),
|
||||
sunglasses,
|
||||
})
|
||||
};
|
||||
let cell = FaceCell {
|
||||
face: FaceId(1),
|
||||
image: ImageId(1),
|
||||
crop: None,
|
||||
confirmed: false,
|
||||
probability: 0.5,
|
||||
crop_px: 120.0,
|
||||
quality: Some(17.26),
|
||||
eyes: None,
|
||||
};
|
||||
assert_eq!(cell.eyes_label(), "");
|
||||
let open = FaceCell {
|
||||
eyes: reading(0.9, 0.9, 0.0),
|
||||
..cell.clone()
|
||||
};
|
||||
assert_eq!(open.eyes_label(), "");
|
||||
let blink = FaceCell {
|
||||
eyes: reading(0.9, 0.2, 0.0),
|
||||
..cell.clone()
|
||||
};
|
||||
assert_eq!(blink.eyes_label(), "Eyes closed");
|
||||
let shades = FaceCell {
|
||||
eyes: reading(0.9, 0.2, 0.9),
|
||||
..cell.clone()
|
||||
};
|
||||
assert_eq!(shades.eyes_label(), "Sunglasses");
|
||||
let mut soft = reading(0.1, 0.1, 0.0).unwrap();
|
||||
soft.right.sharpness = 0.0;
|
||||
soft.left.sharpness = 0.0;
|
||||
let unclear = FaceCell {
|
||||
eyes: Some(soft),
|
||||
..cell
|
||||
};
|
||||
assert_eq!(unclear.eyes_label(), "Eyes unclear");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_people_rail_reports_the_unassigned_pool() {
|
||||
let c = catalog();
|
||||
|
||||
Reference in New Issue
Block a user