application to prune images with the help of metrics and ai
  • Python 78.1%
  • HTML 21.1%
  • Dockerfile 0.6%
  • Go Template 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-11-02 09:45:07 +01:00
.agents docs(agents): point the guidance at refs and commands that exist 2026-10-06 00:39:37 +02:00
cache Release Candidate v1.0.0 2026-07-05 15:31:17 +05:30
docs fix(deploy): mount an emptyDir on /tmp so uploads over 1 MB parse 2026-10-08 18:37:10 +02:00
helm/culler fix(deploy): mount an emptyDir on /tmp so uploads over 1 MB parse 2026-10-08 18:37:10 +02:00
logs Release Candidate v1.0.0 2026-07-05 15:31:17 +05:30
models Release Candidate v1.0.0 2026-07-05 15:31:17 +05:30
output Release Candidate v1.0.0 2026-07-05 15:31:17 +05:30
scripts feat(cli): installable culler command with run, dashboard and report 2026-10-05 22:43:55 +02:00
src/culler DRAFT: select to download KEEP/REVIEW/REJECT 2026-11-02 09:45:07 +01:00
tests test: add signal_floors edge case tests for Subject Too Small and Face Not Visible 2026-11-01 22:39:12 +01:00
utils Release Candidate v1.0.0 2026-07-05 15:31:17 +05:30
.dockerignore feat(deploy): container image, compose and Helm chart with ingress basic auth 2026-10-07 02:55:44 +02:00
.gitignore fix: improve cross-platform compatibility 2026-08-25 17:40:07 +05:30
CHANGELOG.md chore: V1.0 architectural restructuring and final release freeze 2026-07-14 19:55:58 +05:30
CODE_OF_CONDUCT.md Release Candidate v1.0.0 2026-07-05 15:31:17 +05:30
config.yml feat(duplicates): group by previous frame, not by the group's first 2026-10-05 22:42:37 +02:00
CONTRIBUTING.md docs: reorganize project documentation into structured docs hierarchy 2026-07-14 19:59:53 +05:30
docker-compose.yml feat(deploy): container image, compose and Helm chart with ingress basic auth 2026-10-07 02:55:44 +02:00
Dockerfile Docker: multi-stage build 2026-10-07 23:29:35 +02:00
LICENSE Release Candidate v1.0.0 2026-07-05 15:31:17 +05:30
main.py feat(cli): installable culler command with run, dashboard and report 2026-10-05 22:43:55 +02:00
pyproject.toml feat(server): sessions, job queue and env-overridable config for container hosting 2026-10-07 02:47:22 +02:00
README.md Readme: update git url to local git 2026-10-07 23:54:03 +02:00
requirements.txt fix: improve cross-platform compatibility 2026-08-25 17:40:07 +05:30
run_dashboard.py feat(cli): installable culler command with run, dashboard and report 2026-10-05 22:43:55 +02:00
SECURITY.md Release Candidate v1.0.0 2026-07-05 15:31:17 +05:30
SUPPORT.md docs: reorganize project documentation into structured docs hierarchy 2026-07-14 19:59:53 +05:30
VERSION Release Candidate v1.0.0 2026-07-05 15:31:17 +05:30

Local AI Culling

Local AI Culling is an open-source, cross-platform, completely offline, privacy-first AI-assisted image culling application built specifically for professional photographers. It analyzes your raw photo shoots, groups bursts, detects duplicates, evaluates technical quality (focus, noise, composition, expressions), and automatically organizes your images into KEEP, REVIEW, and REJECT folders so you can immediately begin editing your best work in your preferred editor.

Because it runs 100% locally on your own hardware, your photographs are never uploaded to the cloud, ensuring total client privacy and eliminating subscription fees.

Platform & Hardware Support

Local AI Culling is engineered with a single, unified codebase that runs seamlessly across platforms with automatic, platform-neutral hardware acceleration:

Operating System Architecture Hardware Acceleration Runtime Fallback
Windows x86_64 NVIDIA CUDA (where available) Universal CPU
macOS Apple Silicon (arm64) / Intel Apple Metal / MPS (Metal Performance Shaders) ARM NEON / CPU
Linux x86_64 / arm64 NVIDIA CUDA (where available) Universal CPU

Features

  • Duplicate Detection: Groups visually identical photos taken moments apart.
  • Burst Detection & Ranking: Identifies high-speed bursts and automatically selects the single best frame.
  • Intelligent Classification: Sorts images into KEEP, REVIEW, and REJECT based on customizable scoring.
  • Expression Intelligence: Understands facial semantics (e.g., closed eyes, blinking, awkward mouth shapes) to avoid penalizing artistic choices while flagging ruined shots.
  • Image Editability Engine: Simulates shadow and highlight recovery on the raw preview to estimate real-world editability (penalizing noise and color degradation).
  • Scene Intelligence: Detects environmental and subject context (e.g., weddings, portraits, low-light).
  • Photographer Feedback Dashboard: A local web interface to review decisions, adjust thresholds, and understand exactly why the AI made a decision.
  • Hardlink Export Engine: Instantly creates organized output folders using zero-byte storage hardlinks across Windows drives and POSIX filesystem devices. The original files are never duplicated or modified.
  • Explainable Decisions: Transparent scoring. No black-box AI magic—you can see exactly why a photo was rejected.

Dashboard Overview (Browser Mode)

Dashboard Overview

Scene Intelligence (Inspector Mode)

Scene Intelligence

KEEP Folder View

Keep View

REVIEW Folder View

Review View

REJECT Folder View

Reject View

Hardlinked Export Structure

Export Structure

output/
├── KEEP/
├── REVIEW/
├── REJECT/
├── final_export.csv
└── session.json

Architecture

Local AI Culling operates as a highly modular pipeline.

graph TD
    A[Image Ingestion] --> B[Technical Analysis]
    B --> C[Duplicate Detection]
    C --> D[Burst Ranking]
    D --> E[Classification]
    E --> F[Scene Intelligence]
    F --> G[Export Engine]
    G --> H[Feedback Dashboard]
    
    subgraph TA [Technical Analysis]
    B1(Blur & Focus)
    B2(Composition)
    B3(Subject Quality)
    B4(Face & Eye State)
    B5(Editability Engine)
    end
    B -.-> TA

Session Management

The dashboard includes an automated Session Management engine. When the pipeline processes a new directory, it generates a deterministic session.json based on the dataset name and filenames. When the feedback dashboard launches, it compares this session against previous feedback files. If it detects a new photography assignment, it safely archives the older feedback to prevent data collisions, ensuring that photographers can seamlessly review multiple consecutive shoots without manual cleanup.

Installation

Prerequisites

  • Python: 3.11 recommended (Python 3.10+ supported; the type union syntax in bulk_rules is 3.10-only).
  • Storage: SSD highly recommended for fast preview reading and zero-byte hardlink exports.

Setup

  1. Clone the repository:

    git clone https://git.christofschulze.com/photo-culling.git
    cd photo-culling
    
  2. Create and activate a virtual environment:

    # macOS / Linux
    python3 -m venv .venv
    source .venv/bin/activate
    
    # Windows
    python -m venv .venv
    .venv\Scripts\activate
    

    Keep the environment activated for the next step so that pip targets this environment, and so that culler is on your PATH afterwards.

  3. Install the package:

    pip install -e .
    

    This installs dependencies and the project itself, which is what creates the culler entry point.

    Do not use pip install -r requirements.txt as the install step. It installs the third-party dependencies only, so the culler script is never created and you are left running the code as loose scripts. Use it only to pre-populate dependencies without installing the package, such as in a network-restricted environment, followed by pip install -e . --no-deps.

  4. Verify the install:

    culler --help
    culler --version
    

    Both should print output without error. If culler: command not found, the virtual environment is not active in the current shell.

  5. Model Placement: Models download automatically on first run into the per-user cache ($XDG_CACHE_HOME/local-ai-culling, defaulting to ~/.cache/local-ai-culling). To use existing copies instead, place them in the project root or models/ directory:

    • yolov8n.pt (Ultralytics YOLOv8)
    • face_landmarker.task (MediaPipe FaceLandmarker)

Keeping culler available

The script lives in the virtual environment, so it is only on your PATH while that environment is active. To have it available in every new shell, either activate the environment from your shell profile:

# ~/.bashrc or ~/.zshrc
source /path/to/.venv/bin/activate

Or invoke it by full path without activating anything:

/path/to/.venv/bin/culler run -i path/to/photos -o output

Usage

The installed culler command has three subcommands.

1. Running the Pipeline

Run the culling engine on a folder of photographs:

culler run -i path/to/your/photos -o output

This will analyze the photos and create the final KEEP, REVIEW, and REJECT folders via hardlinks.

Configuration is read from config.yml in the current directory, falling back to the copy shipped with the project. Pass --config (a top-level option, so it must come before the subcommand) to point at another file:

culler --config path/to/config.yml run -i path/to/your/photos -o output

2. Launching the Dashboard

To review the results visually:

culler dashboard -o output

Open your browser to http://localhost:5000. The -o must point at the same output directory the pipeline used, since that is where feedback is recorded.

3. Container / Server Deployment (multiple photographers)

For hosting the dashboard as a shared service — per-user sessions, a job queue with upload (zip or files) and download, CPU-only pods, and HTTP Basic Auth at the ingress — use the container build and Helm chart:

docker build -t lai-culler . && docker compose up -d
# or the full Kubernetes story:
helm install culler helm/culler --set ingress.enabled=true --set ingress.host=culling.example.com

See docs/architecture/Hosting_plan.md and docs/architecture/Deployment.md for the plan and operator guide, and helm/culler/README.md for the values reference. The container runs culler serve, which opens a workspace list in the browser: drop a zip or photographs to create a workspace — the culler starts as soon as the upload lands and the finished export opens by itself — then download the culled output as a zip.

4. Generating a Feedback Report

To summarise a set of dashboard decisions and derive configuration suggestions:

culler report -o output

Legacy scripts

python main.py and python run_dashboard.py still work and behave as culler run and culler dashboard respectively. They exist for compatibility with older setups; prefer the culler command.


Project Structure

  • src/culler/ai/: Core AI and scoring models (blur, composition, face/eye, editability, duplicate detection).
  • src/culler/engine/: System engine (pipeline orchestration, configuration loading, caching).
  • src/culler/engine/io/: File ingestion, JSON/CSV exports, visual export, and preview generation.
  • src/culler/engine/analysis/: Profiling and metadata utilities.
  • src/culler/dashboard/: Local web dashboard interface (feedback_server.py) and HTML templates.
  • tests/: Smoke tests and module linkage tests.
  • docs/: System documentation, phase plans, and architectural walk-throughs.
  • models/: Directory for required machine learning weights (.pt, .task).
  • output/: Default directory for hardlinked output and runtime metadata.

Configuration

The software is highly customizable via config.yml. Key configurable options include:

  • thresholds: Adjust the strictness for blur, noise, and face quality.
  • weights: Adjust how much different factors (e.g., composition vs focus) contribute to the final score.
  • export.mode: Choose between hardlink (default, zero extra storage) or copy.
  • culling.max_burst_time_gap: The maximum time gap (in seconds) to consider photos part of the same burst.

Documentation

For a deeper dive into the engineering and design philosophy behind Local AI Culling, see:


Roadmap

Check out the ROADMAP.md for a summary of completed milestones (like Expression Intelligence and the Editability Engine) and a look at our future goals.

License

This project is licensed under the MIT License - see the LICENSE file for details.