Skip to content

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:

  1. --reanalyze was not passed.
  2. .krites/analysis.yaml exists and parses.
  3. 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.
  4. The profile's min_face_box is unchanged. That threshold decides which faces count at all, so it is baked into the measurement rather than applied afterwards.
  5. Whether the eye/face model ran is unchanged — turning face.enabled on or off invalidates the cache in both directions.
  6. Whether the aesthetic scorer ran is unchanged.
  7. 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.