Let a 32-pixel face count, and move the blur floor with it
64 source pixels was too strict: it threw away 70% of everything the detector
finds, and plenty of what it took were faces a person could name.
Lowering it is not a one-line change, because the two floors are coupled. A face
under 112 pixels is *upsampled* to reach the embedder and upsampling invents no
edges, so a small face scores low on sharpness however crisp the original was.
Re-measured over the reference library with `face_index --quality`:
min crop min sharp size cut blur cut kept
32 0.000 44% 0% 56%
32 0.002 44% 3% 53%
32 0.005 44% 8% 48%
32 0.010 44% 16% 40%
32 0.020 44% 27% 30%
64 0.020 70% 7% 23%
Holding the blur floor at 0.020 while dropping the size floor to 32 would have
rejected a further 27% — for being small rather than for being blurred — and
kept only 30%, barely more than the 23% the strict pair kept. Most of the point
of lowering the size floor would have gone straight back out through the other
gate.
0.005 removes 8% of what the size floor leaves, which is the same job 0.020 was
doing at 64 (7%): the large-but-soft face this gate exists for. Together they
now keep 48% of what the detector finds, against 23% before.
The box pre-filter follows down to 24, staying below what the real floor accepts
so it cannot reject a face that would have cleared 32.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -46,13 +46,13 @@ pub struct DetectOptions {
|
||||
/// it is measured on the aligned crop rather than the box. This one exists
|
||||
/// only to throw away the obviously hopeless before paying for a warp, so
|
||||
/// it is deliberately set *below* what the real floor will accept: the
|
||||
/// aligned crop spans roughly 1.3x the box's shorter edge, so 48 here
|
||||
/// cannot reject a face that would have cleared 64 there.
|
||||
/// aligned crop spans roughly 1.3x the box's shorter edge, so 24 here
|
||||
/// cannot reject a face that would have cleared 32 there.
|
||||
pub min_face_px: f32,
|
||||
/// Smallest face the embedder may be given, in **source pixels across the
|
||||
/// aligned crop** — `crop_px` in the catalog.
|
||||
///
|
||||
/// The honest statement of "a face must be at least 64x64", because this is
|
||||
/// The honest statement of "a face must be at least 32x32", because this is
|
||||
/// the number of real pixels behind the 112x112 the model actually sees.
|
||||
/// The box's own size is not that: the ArcFace template reaches past the
|
||||
/// box for forehead and chin, so a 64-pixel box and a 64-pixel crop are
|
||||
@@ -74,6 +74,26 @@ pub struct DetectOptions {
|
||||
/// and a caller cannot enable one gate while forgetting the other.
|
||||
///
|
||||
/// Zero disables it, which is what a measurement run wants.
|
||||
///
|
||||
/// # It has to move with the size floor
|
||||
///
|
||||
/// The two are coupled, because an upsampled face scores low here whatever
|
||||
/// its original sharpness. Measured over the reference library, with the
|
||||
/// size floor at 32 source pixels:
|
||||
///
|
||||
/// | min sharpness | of what the size floor left, this removes |
|
||||
/// |---|---|
|
||||
/// | 0.002 | 3% |
|
||||
/// | 0.005 | 8% |
|
||||
/// | 0.010 | 16% |
|
||||
/// | 0.020 | 27% |
|
||||
///
|
||||
/// At a 64-pixel floor, 0.020 removed 7% — the same *kind* of face, the
|
||||
/// large-but-soft one this gate exists for. Holding 0.020 while dropping
|
||||
/// the size floor to 32 would have thrown away a quarter of the newly
|
||||
/// admitted faces for being small rather than for being blurred, undoing
|
||||
/// most of the point of lowering it. 0.005 removes 8% at 32, which is the
|
||||
/// same job.
|
||||
pub min_sharpness: f32,
|
||||
}
|
||||
|
||||
@@ -82,9 +102,9 @@ impl Default for DetectOptions {
|
||||
Self {
|
||||
confidence: 0.5,
|
||||
nms_iou: 0.4,
|
||||
min_face_px: 48.0,
|
||||
min_source_px: 64.0,
|
||||
min_sharpness: 0.020,
|
||||
min_face_px: 24.0,
|
||||
min_source_px: 32.0,
|
||||
min_sharpness: 0.005,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,13 +464,14 @@ fn report_quality(
|
||||
println!("{}", "-".repeat(52));
|
||||
for (min_px, min_sharp) in [
|
||||
(0.0_f32, 0.0_f32),
|
||||
(64.0, 0.0),
|
||||
(0.0, 0.010),
|
||||
(64.0, 0.005),
|
||||
(32.0, 0.0),
|
||||
(32.0, 0.002),
|
||||
(32.0, 0.005),
|
||||
(32.0, 0.010),
|
||||
(32.0, 0.020),
|
||||
(48.0, 0.005),
|
||||
(64.0, 0.010),
|
||||
(64.0, 0.020),
|
||||
(80.0, 0.010),
|
||||
(96.0, 0.010),
|
||||
] {
|
||||
let by_size = found.iter().filter(|f| f.0 < min_px).count();
|
||||
let by_blur = found
|
||||
|
||||
Reference in New Issue
Block a user