The analysis cache — why re-culling is instant¶
The first cull of a four-thousand-frame wedding is slow: every frame is decoded and measured, and with the face provider on, a model runs over each one. The second cull of the same shoot is near-instant. This page explains the split that makes that true, and the exact conditions under which the cache is reused.
Measuring and judging are separate steps¶
A cull is two things that are easy to conflate:
Measuring — decode the frame, compute its sharpness, its clipping, its perceptual hash, and (if a model is running) the eye-open, smile and facing-camera probabilities for each face. This is expensive. It scales with pixels and with model passes.
Judging — take those numbers, compare them against the profile's thresholds, and resolve a verdict with reasons. This is arithmetic over a few floats per frame. It costs nothing.
.krites/analysis.yaml stores the measurements. Verdicts are re-derived from
them every time.
This is why tuning a threshold and re-culling is fast: changing
soft_sharpness changes the judging, not the measuring. Nothing is re-decoded
and no model runs. Threshold changes are deliberately not part of the cache
validity test — that is the whole point of the design, not an oversight.
The exact conditions for reuse¶
The cache is reused only when all of these hold:
--reanalyzewas not passed..krites/analysis.yamlexists and parses.- Its cache version matches the version the running krites expects. This is bumped whenever a signal's computation changes, so an upgrade that alters how sharpness is measured invalidates every cache rather than mixing old numbers with new.
- The profile's
min_face_boxis unchanged. That threshold decides which faces count at all, so it is baked into the measurement rather than applied afterwards. - Whether the eye/face model ran is unchanged — turning
face.enabledon or off invalidates the cache in both directions. - Whether the aesthetic scorer ran is unchanged.
- Every frame in the manifest has both a signal entry and a perceptual hash in the cache. Adding a frame to the shoot invalidates it.
Fail any one and krites re-measures the whole shoot. There is no partial reuse.
Which settings force a fresh pass, and which do not¶
| Change | Cache survives? |
|---|---|
cull.profile.min_sharpness, soft_sharpness |
Yes — re-judged from cached signals |
max_clipped_highlights, max_clipped_shadows |
Yes |
eye_open_soft, eye_open_hard, smile_soft, facing_soft, frown_soft |
Yes |
dedup_distance |
Yes — bursts are re-clustered from cached hashes |
cull.profile.min_face_box |
No |
face.enabled on or off |
No |
face.detector, face.strategy, face.mesh.margin |
No — these change what the model produces |
| Upgrading krites across a signal change | No |
| Adding frames to the folder and re-ingesting | No |
When to reach for --reanalyze¶
krites cull --reanalyze skips the cache check entirely and re-measures
everything. Use it when:
- you replaced or edited an original file in place — the cache does not check file modification times, so krites will otherwise happily judge the new file from the old file's numbers;
- you want to confirm a result you suspect came from a stale cache;
- a krites upgrade did not bump the cache version but you want the current measurement anyway.
--reanalyze is also the right instinct when a cull comes out wrong, rather than
krites reset shoot. It rebuilds the measurements and deletes nothing — no
verdicts, no ratings, no edits, no removal patches.
Your verdicts survive a re-cull¶
Re-culling does not overwrite decisions you made. For any frame you overrode by hand, or gave a star rating:
- kept, because they are yours — the verdict, the override marker, the rating, the timestamp you decided it, and the burst pick;
- refreshed, because they are the machine's current opinion — what krites now proposes for that frame, and the reasons behind it.
That split is deliberate and was a correction to an earlier implementation which restored the whole record. Under that behaviour an overridden frame kept its original machine proposal forever, so a re-cull under tuned thresholds could never show that krites had come round to your view — which makes "is this getting better?" unanswerable and any agreement statistic meaningless.
Why the cache exists at all, beyond speed¶
Speed is the visible benefit. The durable one is that the cache is a record of what was measured, frame by frame, alongside what the machine proposed and what you decided.
That is the data a learning loop would need: not "she rejected this frame", but "she rejected this frame, which had this sharpness, this exposure, these faces at these eye-open probabilities, when the machine wanted to keep it". Banking it now costs a YAML file per shoot. Reconstructing it later would mean asking a photographer to re-cull a year of weddings.