Name segmented people from the faces already recognised in them
The segmenter knows it found a person; the face index knows which person. Joining them turns "person" in the mask list into "Anna", which is the difference between a vocabulary of eighty COCO classes and one that includes the user's family. Selecting a subject in a group photograph stops being a guessing game between three identical rows. Containment, not IoU. A face is a small part of the person it belongs to, so a correct pairing has an IoU near zero and anything IoU-based would reject every true match. Confirmed names only. A suggestion is the system's guess, and printing a guessed name onto a mask region would launder it into a fact. Writing the tests corrected the design once: a tight head-and-shoulders portrait, where the face fills most of the person box, is the case where naming is most certain, not least. An earlier guard rejected exactly that and has been removed, with the reasoning left as a test because it is easy to get backwards a second time. The names hang on the develop session, set when the image opens because that is the one moment the catalog and the image id are both in reach. Every segmentation run afterwards picks them up for free, and a library with no face indexing behaves exactly as it did before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -293,6 +293,43 @@ pub fn compute(
|
||||
})
|
||||
}
|
||||
|
||||
/// Classes a recognised face may put a name on.
|
||||
///
|
||||
/// Only these. A face inside a `tv` or a `laptop` is a photograph of someone on
|
||||
/// a screen, and renaming the television to "Anna" would be worse than leaving
|
||||
/// it as the model found it.
|
||||
fn is_person_class(name: &str) -> bool {
|
||||
name == "person"
|
||||
}
|
||||
|
||||
impl Segmentation {
|
||||
/// Relabel `person` instances with the name of the face inside them.
|
||||
///
|
||||
/// The segmenter knows it found *a person*; the face index knows *which*
|
||||
/// person. Joining them turns "person" in the mask list into "Anna", which
|
||||
/// is the difference between COCO's eighty classes and a vocabulary that
|
||||
/// includes the user's family — and it is the same click either way, so the
|
||||
/// gain is entirely in being able to tell two people apart before clicking.
|
||||
///
|
||||
/// `faces` are in the same proxy pixels as [`InstanceSummary::bbox`]. The
|
||||
/// geometry lives in `dr_face::naming`, where it is testable without a
|
||||
/// model or a catalog.
|
||||
///
|
||||
/// Returns how many instances gained a name. An unrecognised person keeps
|
||||
/// the model's own label, which is the right default: "person" is merely
|
||||
/// unhelpful, where a wrong name is wrong and the user cannot tell which
|
||||
/// they are looking at.
|
||||
pub fn apply_names(&mut self, faces: &[dr_face::NamedFace<'_>]) -> usize {
|
||||
dr_face::name_instances(
|
||||
&mut self.instances,
|
||||
faces,
|
||||
|i| i.bbox,
|
||||
|i| is_person_class(&i.class_name),
|
||||
|i, name| i.class_name = name.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Load the model and run it.
|
||||
///
|
||||
/// Loading is ~24 ms against the ~470 ms of inference that follows, and this
|
||||
|
||||
Reference in New Issue
Block a user