diff --git a/core/dr-catalog/src/faces.rs b/core/dr-catalog/src/faces.rs index 2212d89..ab1d7d4 100644 --- a/core/dr-catalog/src/faces.rs +++ b/core/dr-catalog/src/faces.rs @@ -254,7 +254,9 @@ pub fn record_detections( /// How much of the library has been through face detection. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct Coverage { - /// Images that are candidates at all — present, not trashed. + /// Images that are candidates at all — present, not trashed, and not the + /// shadowed half of a RAW+JPEG pair. The same population every sweep's + /// work list is drawn from; see [`coverage`] for why that matters. pub images: u64, /// Images this model has actually looked at. pub indexed: u64, @@ -292,9 +294,21 @@ impl Coverage { /// progress figure. Answerable only because [`record_detections`] writes a run /// marker: counting `faces` rows would report how many faces exist, which is a /// different number and never reaches the image count. +/// +/// # Shadowed images are not candidates, and the denominator has to agree +/// +/// A shadowed image is the JPEG half of a RAW+JPEG pair. It is not a separate +/// photograph — the grid does not show it, and every sweep that builds a work +/// list excludes it. Counting it here anyway is not a rounding error: on the +/// reference library it put 4,424 images into the denominator that no pass is +/// permitted to touch, so "4,593 outstanding" had a floor of 4,424 that no +/// amount of indexing could ever bring down, and [`Coverage::is_complete`] +/// could never once return true. A progress figure that cannot reach its own +/// target reads exactly like a stuck job, which is what it was taken for. pub fn coverage(conn: &Connection, model_id: &str) -> Result { let images: i64 = conn.query_row( - "SELECT COUNT(*) FROM images WHERE trashed_at IS NULL", + "SELECT COUNT(*) FROM images + WHERE trashed_at IS NULL AND shadowed_by IS NULL", [], |r| r.get(0), )?; @@ -302,7 +316,8 @@ pub fn coverage(conn: &Connection, model_id: &str) -> Result