One completeness job over a registry of repairs, and a re-index button
A library's records are never all complete at once. A face found before its quality was kept has no quality; one found before the eye models existed has no reading; one adopted from a peer's shard has no crop; an image the fast detector examined on a 1024 px proxy has boxes the current detector would not have drawn; an image the scan stat'ed has no capture date. On the reference library that is 17,762 faces under the bare w600k_mbf id with no quality, no reading and no dense landmarks, 4,144 of them without a crop, beside 12,217 images the fast detector examined and found nothing in. Every one of those gaps was its own pass — V14's measuring pass, §17.5's eye pass, the sweep's proxy repair, the sweep's detector upgrade — with its own work list, its own count and its own idea of done, and adding a per-face field meant adding a pass. There was no pass at all for the case the library is actually in: boxes and landmarks drawn by a weaker detector on a proxy, which every later per-face pass would have read from. dr_ui::repairs replaces them with one job over a registry. A Repair names one thing a record can lack — the predicate that says which images still owe it, the input its handler needs (a header, the original, or a native render), the handler, and what to record for an image that can never be done. The job unions the predicates into one work list, fetches each image once at the most any claimant asks for, renders it at most once, and runs every handler whose predicate that image still matches, checked again before each because a detection writes every field a per-face handler would fill. The registry today: face-proxy, face-quality, face-eyes, face-crop, face-detection, face-upgrade, metadata — the last there to say that this is not a face job. Adding a field is one entry. A repair's predicate is the only definition of its work: the count the settings page shows, the list the job fetches and the check before its handler run are one predicate, so the job converges. That is why the registry is cut to what the device can do rather than listing what it skips — an entry is a count and a set of originals to fetch — and why an eye reading that cannot be cut is not a criterion. The catalog side is generic to match: record_updates writes whichever fields a FaceUpdate carries and re-marks the image so the shards export it; faces_needing and count_needing answer a predicate the caller supplies, replacing the measuring pass's three special cases. Two buttons on the settings page run the job and differ in one predicate. "Index faces" converges on coverage: has anything examined this image. "Re-index every face" converges on provenance: face-detection claims every image with no marker under the chosen detector, in either of its forms (FaceDetector::model_ids, so a desktop in f32 and a tablet on the Hexagon do not re-index each other's work), and a marker saying a weaker one looked is not that. An original over the fetch budget is left exactly as it was under the re-index, where the sweep marks it examined: a re-detection with nothing found would delete the faces, and "cannot fetch" is not "no faces".
This commit is contained in:
@@ -91,6 +91,12 @@ export component SettingsPage inherits Rectangle {
|
||||
/// No model on disk, so the pass cannot run at all.
|
||||
in property <bool> face-model-missing: false;
|
||||
callback index-faces();
|
||||
/// TRACES: FR-CULL-8 | FR-CULL-10
|
||||
/// The re-index: every image the chosen detector has not been over at
|
||||
/// native resolution, detected again with names carried across. The
|
||||
/// same running state as the pass above — one job, two ways to ask for
|
||||
/// it — so both buttons go quiet together.
|
||||
callback reindex-faces();
|
||||
/// TRACES: FR-CULL-8
|
||||
/// Which detector the pass finds faces with — docs/faces.md §12.3 for
|
||||
/// what each costs and finds. The choice is a model change: coverage is
|
||||
@@ -520,6 +526,49 @@ export component SettingsPage inherits Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// TRACES: FR-CULL-8 | FR-CULL-10
|
||||
// The re-index, under the pass it is the heavier
|
||||
// form of. Both run the completeness job
|
||||
// (dr_ui::repairs) and differ in one predicate:
|
||||
// indexing converges on coverage and leaves a face a
|
||||
// weaker detector found on a small proxy as found;
|
||||
// this one detects every such image again so every
|
||||
// box, landmark, crop and vector is the current
|
||||
// detector's from the native render, with names,
|
||||
// suggestions and rejections carried onto the new
|
||||
// faces. It fetches every original it visits, which
|
||||
// is why it says so and never starts on its own.
|
||||
if root.library-open: Rectangle {
|
||||
height: Theme.gap-sm;
|
||||
}
|
||||
|
||||
if root.library-open: Caption {
|
||||
text: "Re-indexing detects every face again with "
|
||||
+ "the chosen detector at full resolution, "
|
||||
+ "on every photograph it has not yet been "
|
||||
+ "over — including those an earlier, "
|
||||
+ "faster pass looked at — and fills in "
|
||||
+ "whatever else a record is missing on the "
|
||||
+ "way. Names, suggestions and rejections "
|
||||
+ "are carried onto the new faces. It "
|
||||
+ "fetches every original it visits, and "
|
||||
+ "it can be stopped and resumed.";
|
||||
wrap: word-wrap;
|
||||
}
|
||||
|
||||
if root.library-open: Rectangle {
|
||||
height: Theme.control-height;
|
||||
|
||||
Button {
|
||||
x: 0;
|
||||
text: root.face-indexing
|
||||
? "Indexing faces…"
|
||||
: "Re-index every face";
|
||||
enabled: !root.face-indexing && !root.face-model-missing;
|
||||
clicked => { root.reindex-faces(); }
|
||||
}
|
||||
}
|
||||
|
||||
if root.activity-kept > 0: Rectangle {
|
||||
height: Theme.control-height;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user