Skip to content

krites cull

Description

Judge a shoot — analyse every frame and assign a keep / maybe / reject verdict with reasons (spec 0001 §4.2). For each frame krites measures model-free quality signals — focus (variance of the Laplacian) and exposure (blown-highlight / crushed-shadow clipping) — and resolves a verdict through a cull profile. Hard gates reject, a soft focus floor demotes to maybe, and a clean frame is a keep. Near-duplicate bursts are then grouped (perceptual hash): the sharpest frame is kept and the rest demoted to maybe, tagged with the burst they belong to (R-DUP-1).

krites proposes; you dispose — a verdict is a label written to .krites/verdicts.yaml, never a file operation (R-CULL-3). Originals are never touched.

Each cull also banks an analysis cache (.krites/analysis.yaml) — the raw per-frame signals plus the profile fingerprint in force — and, on every verdict, records the machine's proposed ruling alongside your final one. That captured history is what the Phase-4 learning loop will train on to auto-tune the profile to your taste (spec 0005); it also makes verdicts explainable and re-culls fast.

The shoot must already be registered with krites ingest.

Usage

krites cull [flags]

The shoot is taken from --shoot, or the current working directory if omitted (R-SCOPE-1).

Flags

Flag Description Default
--shoot Shoot directory to cull the current directory
--profile Cull profile to apply wedding-default
--reanalyze Recompute analysis, ignoring the cache false
--output Output format: text or json text

Examples

# Cull the shoot in the current directory
cd ~/shoots/smith-wedding && krites cull

# Or target it by path, with JSON output
krites cull --shoot ~/shoots/smith-wedding --output json

Text output:

Shoot "smith-wedding" — 4 frames
  keep 1   maybe 0   reject 3

  keep   01-sharp.jpg
  reject 02-flat.jpg    — out of focus (sharpness 0 below 50)
  reject 03-dark.jpg    — out of focus (sharpness 0 below 50); crushed shadows (100% over 30%)
  reject 04-bright.jpg  — out of focus (sharpness 0 below 50); blown highlights (100% over 10%)

The cull engine also judges eyes open vs. closed (spec 0004): the wedding-default profile carries an eye gate, and when a face provider is configured the worst-face eye-open probability drives the verdict —

  • a likely blink (below eye_open_soft, seed 0.50) demotes a frame to maybe with a subject blinking … reason;
  • eye_open_hard is disabled by default (0), so a closed-eye frame is never auto-rejected — it may be the only record of a moment, so you dispose (spec 0004 R-EYE-3). Raise it to opt into auto-reject;
  • only faces clearing min_face_box (seed 0.10 of the frame's shorter side) count, so a blinking guest in the far background doesn't demote a portrait;
  • within a near-duplicate burst, an open-eyed frame is kept over a sharper blink (R-EYE-6).

The face detection that feeds the eye signal is a local ONNX model (UltraFace detector + InsightFace landmarks → eye-aspect-ratio), run with no CGO. It is off by default (local-first, opt-in); enable it in config:

face:
  enabled: true
  library_path: /path/to/libonnxruntime.so   # or .dylib on macOS
  # detector_model / landmark_model are optional — when omitted, krites fetches
  # the pinned models into its cache once and verifies their checksums.
  # optional — the EAR→eye-open calibration (defaults are tuned to real frames):
  ear:
    closed: 0.12   # EAR at/below which eyes read fully closed
    open:  0.26    # EAR at/above which eyes read fully open

The ONNX models are fetched + checksum-verified automatically on first use; only the ONNX Runtime library path is supplied by hand (it's platform-specific).

With it on, the cull demotes blinks to maybesubject blinking (N% eye-open, below 50%) — and banks min_eye_open per frame. Off, the cull runs model-free (no faces ⇒ no eye penalty), exactly as before. The deterministic gate, signal projection and burst ranking are pure / WASM-safe; only the native adapter is gated behind config. See spec 0004.

Notes & current limits

  • The wedding-default profile is the seed ruleset; its focus/exposure/eye thresholds are tunable starting points (spec 0001 §6). A configurable profile catalog (krites profile …) lands later; only wedding-default is available today.
  • Re-culls reuse the analysis cache — tuning a threshold and re-culling re-resolves verdicts from the cached signals (no re-decode, no model pass), so it's near-instant on a big shoot (R-GLOBAL-6). --reanalyze forces a full recompute; the cache is also invalidated automatically when a signal-affecting setting (the face-size floor, or turning eye detection on/off) changes.
  • Eye detection runs on the CPU execution provider today; the Apple-Silicon CoreML acceleration and automatic model fetch (so you don't supply the model paths by hand) are the remaining 0004 follow-ups. RAW frames decode via JPEG/PNG only for now (cull on embedded previews is later).