-
Notifications
You must be signed in to change notification settings - Fork 0
Setup
- Immich v1.106+ with face recognition enabled and people tagged
- Frigate v0.16+ (face mode only)
- Docker with the appropriate GPU runtime (optional but strongly recommended)
- Open Immich → Account Settings → API Keys
- Click New API Key, give it a name (e.g.
winnow), copy the key
This is the base URL of your Frigate instance, e.g. http://192.168.1.10:5000. Only needed for face mode — omit it entirely if you're using object mode.
| Tag | Arch | Acceleration |
|---|---|---|
:latest |
amd64 + arm64 | NVIDIA CUDA 12.8 (amd64) · requires NVIDIA Container Toolkit · minimum driver 570 |
:rocm |
amd64 | AMD ROCm · pass /dev/kfd + /dev/dri
|
:intel |
amd64 | Intel Arc / iGPU via OpenVINO · pass /dev/dri, set OPENVINO_DEVICE=GPU
|
:cpu |
amd64 + arm64 | CPU only · ~2 GB smaller · no GPU required |
Use :cpu if your host has no supported GPU — it skips the entire GPU stack. Set a container memory limit of at least 2 GB (mem_limit: 2g) — the embedding models need roughly 1–1.5 GB.
Copy compose.yml and .env.example to a directory on your host:
mkdir winnow && cd winnow
curl -O https://raw.githubusercontent.com/sudolulo/winnow/main/compose.yml
curl -O https://raw.githubusercontent.com/sudolulo/winnow/main/.env.example
cp .env.example .envEdit .env with your values:
IMMICH_URL=http://192.168.1.10:2283
API_KEY=your-immich-api-key
FRIGATE_URL=http://192.168.1.10:5000Edit the volume paths in compose.yml to point to directories on your host where models, cache, and output crops should be stored:
volumes:
- /your/path/to/models:/models
- /your/path/to/cache:/app/.if_cache
- /your/path/to/output:/app/frigate_trainThese directories will be created automatically by Docker if they don't exist.
Start it:
docker compose up -dLogs:
docker compose logs -f winnowOn the first run, winnow downloads the embedding models (~1–2 GB) from HuggingFace and InsightFace. This happens once — subsequent runs use the cached models from your mounted volume and start immediately.
CRON_SCHEDULE controls both the run schedule and container lifetime:
CRON_SCHEDULE |
Behaviour |
|---|---|
| (unset) | Run once on startup, then exit |
| (empty string) | Stay alive; trigger manually with docker exec -it winnow winnow
|
| Cron expression | Run on startup, then repeat on schedule |
Example — every Sunday at 3 AM:
CRON_SCHEDULE=0 3 * * 0
In scheduled mode the process (and loaded models) stays resident between runs, so each subsequent run starts immediately without re-loading models.
Include the deploy block in your compose.yml (present in the example) and ensure the NVIDIA Container Toolkit is installed on your host:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]Verify GPU access:
docker run --rm --gpus all nvidia/cuda:13.3.0-base-ubuntu22.04 nvidia-smiUse image: ghcr.io/sudolulo/winnow:rocm and replace the deploy: block with:
devices:
- /dev/kfd
- /dev/dri
group_add:
- video
- renderUse image: ghcr.io/sudolulo/winnow:intel and replace the deploy: block with:
devices:
- /dev/dri
group_add:
- render
environment:
- OPENVINO_DEVICE=GPU # omit to run OpenVINO inference on CPU (default)OPENVINO_DEVICE selects the compute device within the OpenVINO runtime:
-
GPU— Intel iGPU or Arc GPU (requires/dev/dripassthrough) -
CPU(default) — CPU cores, via OpenVINO's CPU plugin which uses AVX512 and multi-threading. This is not the iGPU — it runs on the CPU itself, but OpenVINO's optimised kernels are meaningfully faster than standard CPUExecutionProvider.
Use CPU if you want OpenVINO's faster CPU path without passing through the GPU device.
Set VERBOSE=true in your .env to enable DEBUG-level output on the console. The log file always captures DEBUG regardless of this setting:
# log file is written to the output volume (frigate_train mount)
docker exec winnow cat /app/frigate_train/winnow.log| Variable | Default | Description |
|---|---|---|
IMMICH_URL |
(required) | Full URL to your Immich instance |
API_KEY |
(required) | Immich API key |
FRIGATE_URL |
(unset) | Frigate base URL — required for face upload; omit for object mode only |
| Variable | Default | Description |
|---|---|---|
TRAINING_MODE |
face |
face — upload crops to Frigate; object — save crops to disk |
STRATEGY |
auto |
auto (embedding-based adaptive), standard (30 images), broad (100 images) |
LIMIT |
(unset) | Exact image count — overrides STRATEGY
|
OBJECT_CLASS |
dog |
Target class for object mode (any YOLO class: dog, cat, car, etc.) |
AUTO_MODE |
(auto) | Force non-interactive mode in a terminal; auto-detected otherwise |
VERBOSE |
false |
Enable DEBUG-level console output (log file is always DEBUG) |
| Variable | Default | Description |
|---|---|---|
ONLY_PEOPLE |
(unset) | Comma-separated whitelist — process only these people |
SKIP_PEOPLE |
(unset) | Comma-separated list — skip these people |
MIN_FACE_COUNT |
0 |
Skip people with fewer than N tagged assets in Immich |
YEARS_FILTER |
10 |
Ignore images older than N years |
| Variable | Default | Description |
|---|---|---|
MIN_FACE_WIDTH |
90 |
Minimum face crop width in pixels |
FACE_MARGIN |
0.15 |
Padding around bounding box crop (fraction of face size) |
ENABLE_FACE_ALIGNMENT |
true |
Align to ArcFace 112×112 format using facial landmarks |
USE_FULL_RESOLUTION |
true |
Download full-resolution originals rather than preview thumbnails |
MIN_CONFIDENCE |
0.7 |
Minimum Immich face detection confidence |
BLUR_THRESHOLD |
120.0 |
Laplacian variance threshold — higher rejects more blur |
MAX_AUTO_IMAGES |
80 |
Maximum training images per person in Frigate |
QUALITY_REPLACEMENT |
true |
When at cap, swap a weaker tracked image for a better candidate. With Frigate scoring active, targets the most redundant image (highest recognize score); otherwise uses blur score. Never touches manually added Frigate files. Set false to skip people at cap |
FRIGATE_SCORE_CEILING |
0.0 |
Skip uploads whose pre-upload Frigate recognize score exceeds this value — they are already well-covered. 0 disables; requires at least one prior run to have scores |
ENABLE_FRIGATE_SCORES |
true |
Call Frigate's recognize endpoint pre-upload to store diversity scores used for quality replacement. Adds ~200 ms per upload. Disable to use blur-score replacement only |
| Variable | Default | Description |
|---|---|---|
FORCE_CPU |
false |
Disable GPU — fall back to CPU for all inference |
OPENVINO_DEVICE |
CPU |
Intel variant only: GPU = iGPU/Arc (requires /dev/dri); CPU = OpenVINO CPU plugin (optimised CPU kernels, not the iGPU) |
ENABLE_CACHE |
true |
Cache computed embeddings to disk (speeds up re-runs on the same library) |
CACHE_DIR |
.if_cache |
Path for embedding cache and upload tracker files |
HF_HOME |
(system) | HuggingFace model cache path (SigLIP) |
INSIGHTFACE_HOME |
(system) | InsightFace model cache path (Buffalo_L) |
| Variable | Default | Description |
|---|---|---|
OUTPUT_DIR |
./frigate_train |
Directory for object-mode crops and the winnow.log file. In Docker, set this via the volume mount instead |
| Variable | Default | Description |
|---|---|---|
DRY_RUN |
false |
Preview selection without downloading or uploading |
RETRY_REJECTED |
false |
Re-attempt assets previously rejected by Frigate |
RESET_PERSON |
(unset) | Clear upload history for one person and delete their winnow-managed Frigate training files so the next run starts fresh. Manually added Frigate files are never touched |
| Variable | Default | Description |
|---|---|---|
CRON_SCHEDULE |
(unset) | Unset = run once and exit; empty string = stay alive; cron expression = scheduled |