From ed8487807b6ed189a149437c425e5ad84df45b5d Mon Sep 17 00:00:00 2001 From: Holden Date: Fri, 12 Jun 2026 01:00:17 +0000 Subject: [PATCH] =?UTF-8?q?chore:=20establish=20authorship=20=E2=80=94=20H?= =?UTF-8?q?olden=20Salomon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LICENSE: add copyright line for Holden Salomon (retain original) - pyproject.toml: update authors to Holden Salomon - README: lead with what the software does rather than fork attribution; merge split env var tables into one unified reference; add lint/test badges; move attribution to footer Co-Authored-By: Claude Sonnet 4.6 --- LICENSE | 1 + README.md | 119 ++++++++++++++++++++++++++++++++----------------- pyproject.toml | 2 +- 3 files changed, 80 insertions(+), 42 deletions(-) diff --git a/LICENSE b/LICENSE index a4bf177..2b3acbb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ MIT License +Copyright (c) 2026 Holden Salomon Copyright (c) 2026 Sebastian Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/README.md b/README.md index a106ccf..3ae1100 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,20 @@ -[![Publish Docker Image](https://github.com/sudolulo/if_curator_headless/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/sudolulo/if_curator_headless/actions/workflows/docker-publish.yml) [![Release](https://github.com/sudolulo/if_curator_headless/actions/workflows/release.yml/badge.svg)](https://github.com/sudolulo/if_curator_headless/actions/workflows/release.yml) -### if-curator-headless +[![Publish Docker Image](https://github.com/sudolulo/if_curator_headless/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/sudolulo/if_curator_headless/actions/workflows/docker-publish.yml) [![Release](https://github.com/sudolulo/if_curator_headless/actions/workflows/release.yml/badge.svg)](https://github.com/sudolulo/if_curator_headless/actions/workflows/release.yml) [![Lint](https://github.com/sudolulo/if_curator_headless/actions/workflows/lint.yml/badge.svg)](https://github.com/sudolulo/if_curator_headless/actions/workflows/lint.yml) [![Test](https://github.com/sudolulo/if_curator_headless/actions/workflows/test.yml/badge.svg)](https://github.com/sudolulo/if_curator_headless/actions/workflows/test.yml) -Headless fork of [if-curator](https://github.com/ds-sebastian/if_curator) with automatic Frigate face training upload and Docker support. +### if-curator-headless > *A specialized tool to extract **high-quality, diverse** training images from your Immich library for Frigate's Face Recognition (ArcFace) and Object/State Classification models.* -## What This Adds - -- **Headless mode** — runs without interactive prompts, suitable for Docker and cron -- **Auto-discover people** — processes all named people from Immich automatically instead of picking one at a time -- **Frigate upload** — sends curated face crops directly to Frigate's face training API after processing -- **People filtering** — skip specific people, whitelist only certain people, or set a minimum photo count -- **Docker** — pre-built image with NVIDIA GPU support, ready for TrueNAS -- **Scheduling** - Use cron notation to schedule runs - -All of if-curator's original functionality is unchanged. +Runs headless in Docker with full GPU acceleration, automatic Frigate upload, cron scheduling, and env-var-driven configuration — no interactive prompts required. --- -## Why This Tool?? +## Why This Tool? > **"Diversity matters far more than volume."** — *Frigate Developer Tips* -Training AI models on "bulk" data is often harmful. If you feed the model 50 images from the same 10-second video clip, it learns to recognize the *lighting and background*, not the actual *face* or *object*. +Training AI models on bulk data is often harmful. Feed the model 50 images from the same 10-second video clip and it learns the *lighting and background*, not the actual *face* or *object*. -`if-curator` solves this using **AI-powered diversity selection** and **quality filtering**: +`if-curator-headless` solves this using **AI-powered diversity selection** and **quality filtering**: | Mode | Embedding Model | Algorithm | | :--- | :--- | :--- | @@ -39,20 +29,21 @@ Training AI models on "bulk" data is often harmful. If you feed the model 50 ima - **Auto Diversity [Recommended]**: Clusters images by visual similarity, selects representatives from each cluster, then fills with maximally-diverse picks until redundancy starts (capped at 80) - **Standard (30 images)**: Balanced set using Smart Diversity - **Broad (100 images)**: Extensive set using Smart Diversity -- **Custom Count**: You choose the limit +- **Custom Count**: Set `LIMIT=N` for any exact number ### Quality Filtering -Bad training data hurts ArcFace models. Images are automatically rejected if they are: +Images are automatically rejected if they are: - **Blurry** — Laplacian variance below threshold - **Grayscale / IR** — ArcFace is trained on color images only - **Over/Underexposed** — Washed-out or too dark to use - **Low confidence** — Partial or occluded face detections -- **Too small** — Faces under 100px (configurable) lack features +- **Too small** — Faces under the minimum pixel width lack features ### Face Recognition Prep - Uses InsightFace embeddings on **face crops** (not full images — avoids wrong-face in group photos) - **Hard example prioritization** — unusual angles, sunglasses, and low-confidence detections are biased for selection - **Face alignment** via InsightFace landmarks (standard 112×112 ArcFace input) +- **EXIF orientation** applied before cropping so bounding boxes align correctly - Downloads **full-resolution** originals for final crops (falls back to JPEG preview for HEIC/RAW) ### Object/State Classification Prep @@ -60,19 +51,20 @@ Bad training data hurts ArcFace models. Images are automatically rejected if the - **YOLOv9c** to detect and crop specific objects (dogs, cars, etc.) - Captures variation in poses, lighting, and backgrounds -> **Note:** Frigate does not support uploading custom images for object classification training via the UI or API. Object mode crops are saved to disk for manual YOLO model training. +> **Note:** Frigate does not support uploading custom images for object classification via the UI or API. Object mode crops are saved to disk for manual YOLO model training. ### Performance - Concurrent thumbnail downloads (8 parallel workers) - Batch-capable SigLIP embeddings for GPU efficiency - Optional disk-based embedding cache for faster re-runs +- Upload deduplication — already-uploaded assets are skipped on future runs - Multi-person batch mode --- ## Requirements -- **NVIDIA GPU** (recommended) — auto-detects CUDA for faster embedding computation. CPU mode is available via `FORCE_CPU=true` but significantly slower. +- **NVIDIA GPU** (recommended) — auto-detects CUDA. CPU mode available via `FORCE_CPU=true` but significantly slower. - **Python 3.12+** - **[uv](https://astral.sh/uv/)** - **Immich Server** (v1.106+) @@ -82,41 +74,78 @@ Bad training data hurts ArcFace models. Images are automatically rejected if the ## Environment Variables -### New in This Fork +### Mode & Strategy -| Variable | Default | What it does | +| Variable | Default | Description | | :--- | :--- | :--- | -| `AUTO_MODE` | `false` | Run without prompts | -| `FRIGATE_URL` | *(empty)* | Frigate server URL — auto-uploads faces when set | +| `AUTO_MODE` | `false` | Run without interactive prompts | | `TRAINING_MODE` | `face` | `face` or `object` | -| `STRATEGY` | `auto` | `auto`, `standard`, or `broad` | -| `SKIP_PEOPLE` | *(empty)* | People to skip | -| `ONLY_PEOPLE` | *(empty)* | People to process (whitelist) | -| `MIN_FACE_COUNT` | `3` | Minimum photos required to process a person | -| `OBJECT_CLASS` | `dog` | Object type (only for object mode) | +| `STRATEGY` | `auto` | `auto`, `standard` (30), or `broad` (100) | +| `LIMIT` | *(unset)* | Custom image count — overrides `STRATEGY` | +| `OBJECT_CLASS` | `dog` | Object label for object mode (e.g. `dog`, `cat`, `car`) | -### Original if-curator Variables +### People Filtering + +| Variable | Default | Description | +| :--- | :--- | :--- | +| `ONLY_PEOPLE` | *(unset)* | Comma-separated whitelist of people to process | +| `SKIP_PEOPLE` | *(unset)* | Comma-separated list of people to skip | +| `MIN_FACE_COUNT` | `0` | Skip people with fewer than N assets in Immich | +| `YEARS_FILTER` | `10` | Only include images from the last N years | + +### Connection + +| Variable | Default | Description | +| :--- | :--- | :--- | +| `IMMICH_URL` | *(required)* | Full URL to Immich (e.g. `http://192.168.1.10:2283`) | +| `API_KEY` | *(required)* | Your Immich API Key | +| `FRIGATE_URL` | *(unset)* | Frigate server URL — enables automatic face upload when set | + +### Image Quality + +| Variable | Default | Description | +| :--- | :--- | :--- | +| `MIN_FACE_WIDTH` | `50` | Minimum face crop width in pixels | +| `FACE_MARGIN` | `0.15` | Padding around face crop as a fraction of face size | +| `ENABLE_FACE_ALIGNMENT` | `true` | Align face to ArcFace 112×112 format before cropping | +| `USE_FULL_RESOLUTION` | `true` | Download full-resolution originals for final crops | +| `MIN_CONFIDENCE` | `0.7` | Minimum Immich face detection confidence | +| `BLUR_THRESHOLD` | `100.0` | Laplacian variance threshold — lower accepts more blur | +| `MAX_AUTO_IMAGES` | `80` | Hard cap on auto-diversity selection | + +### Caching & Models | Variable | Default | Description | | :--- | :--- | :--- | -| `IMMICH_URL` | *(prompted)* | Full URL to Immich (e.g. `http://192.168.1.10:2283`) | -| `API_KEY` | *(prompted)* | Your Immich API Key | | `FORCE_CPU` | `false` | Disable GPU acceleration | -| `MIN_FACE_WIDTH` | `100` | Minimum face crop size (pixels) | -| `BLUR_THRESHOLD` | `100.0` | Laplacian variance threshold for blur detection | -| `MIN_CONFIDENCE` | `0.7` | Minimum Immich detection confidence | -| `MAX_AUTO_IMAGES` | `80` | Safety cap for auto-diversity mode | -| `FACE_MARGIN` | `0.15` | Crop margin around face (fraction) | -| `USE_FULL_RESOLUTION` | `true` | Download originals for final crops | -| `ENABLE_FACE_ALIGNMENT` | `true` | Align faces to ArcFace 112×112 format | | `ENABLE_CACHE` | `false` | Cache embeddings to disk for faster re-runs | | `CACHE_DIR` | `.if_cache` | Directory for embedding cache | +| `HF_HOME` | *(system)* | Override HuggingFace model cache location | +| `INSIGHTFACE_HOME` | *(system)* | Override InsightFace model cache location | + +### Tracker Overrides *(one-shot — remove after use)* + +| Variable | Default | Description | +| :--- | :--- | :--- | +| `DRY_RUN` | `false` | Preview selection without downloading or uploading | +| `RETRY_REJECTED` | `false` | Re-attempt previously rejected images | +| `RESET_PERSON` | *(unset)* | Clear uploaded + rejected history for one person by name | + +### Scheduling + +| Variable | Default | Description | +| :--- | :--- | :--- | +| `CRON_SCHEDULE` | *(unset)* | Cron expression — unset runs once and exits | --- ## Docker -[ghcr.io/sudolulo/if-curator-headless](ghcr.io/sudolulo/if-curator-headless) +```bash +docker pull ghcr.io/sudolulo/if-curator-headless:latest +``` + +See [compose.yml](compose.yml) for a full example with volume mounts and GPU support. --- @@ -126,3 +155,11 @@ Bad training data hurts ArcFace models. Images are automatically rejected if the git clone https://github.com/sudolulo/if_curator_headless.git cd if_curator_headless uv sync +uv run if-curator +``` + +--- + +## Attribution + +Based on [if-curator](https://github.com/ds-sebastian/if_curator) by Sebastian, licensed MIT. diff --git a/pyproject.toml b/pyproject.toml index db4287d..8010dab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "Immich to Frigate training sets" license = "MIT" requires-python = ">=3.12" -authors = [{ name = "sudolulo" }] +authors = [{ name = "Holden Salomon", email = "holden@arch.fyi" }] keywords = ["immich", "frigate", "face-recognition", "training-data", "arcface", "insightface"] classifiers = [ "Development Status :: 3 - Alpha",