{"slug": "ai-powered-photo-gallery-without-the-cloud", "title": "AI Powered Photo Gallery Without the Cloud", "summary": "Arkalogy released Best Photo Picker (bpp), a free, open-source, local-first photo curation tool that auto-scores images for sharpness, lighting, faces, and composition without uploading to the cloud. The tool runs entirely on the user's machine, clusters near-duplicates, and allows boosting specific people, addressing privacy concerns and subscription fatigue associated with Apple Photos and Google Photos.", "body_md": "**Drowning in 15,000 family photos?** Apple Photos and Google Photos bury your best shots in their feed. Best Photo Picker runs entirely on your machine, scores every photo on sharpness, lighting, faces, and composition, and lets you curate the perfect 50 in minutes — no cloud, no subscription, no compromise.\n\nIt's the only photo tool that **auto-scores your whole library for curation without uploading anything**, then **clusters near-duplicates** and lets you **boost specific people** so the final pick favors the faces that matter — your kid, for the grandparents.\n\nTry it in 30 seconds — no photos needed:\n\n```\npip install \"bppicker[web]\" && bpp demo\n```\n\nGenerates a sample library, runs the full UI locally, and quits cleanly when you close the tab. Nothing leaves your machine.\n\n*By Arkalogy — a PM-directed, AI-built product; the architectural decisions are captured in *\n\n`docs/adr/`\n\n. Why & how it was built →For an annotated walkthrough of the full workflow — import → adjust → pick → export — plus the side surfaces (faces, calendar, map, duplicates), see ** docs/quickstart-gallery.md**.\n\n📣 **Found a bug or have a feature idea?** [Open an issue](https://github.com/Arkalogy/best-photo-picker/issues/new/choose) · [Start a discussion](https://github.com/Arkalogy/best-photo-picker/discussions) · [Contributing guide](https://github.com/Arkalogy/best-photo-picker/blob/main/CONTRIBUTING.md). Security issues: please use [private vulnerability reporting](https://github.com/Arkalogy/best-photo-picker/security/advisories/new), not a public issue.\n\n| Best Photo Picker | Apple Photos | Google Photos | Lightroom | digiKam | |\n|---|---|---|---|---|---|\n| Local-first / runs offline | ✅ | ❌ cloud | ✅ | ||\n| Auto-scores photos for picking | ✅ | ❌ | ❌ | ||\n| Boost picks by named person | ✅ | ❌ | ❌ | ||\n| Near-duplicate deduplication | ✅ | ❌ | ✅ | ||\n| Free + open source | ✅ MIT | ❌ | ❌ | ❌ $120/yr | ✅ GPL |\n| No account / no subscription | ✅ | ❌ | ❌ | ❌ | ✅ |\n| Optional LAN sharing w/ device pairing | ✅ | ❌ | ❌ | ❌ | ❌ |\n\nIf you live in the Apple/Google ecosystem and your collection fits their feeds, those tools work great. **bpp exists for the case they're bad at: you have thousands of photos of a specific subject — your kid, your dog, last summer's trip — and you want to find the actual best fifty without uploading anything anywhere.**\n\n**Quality scoring**— sharpness, exposure, face detection (YuNet + SCRFD + BlazeFace + MediaPipe + dlib), composition** Smart deduplication**— perceptual hashing (dHash + aHash) and CLIP semantic similarity** Temporal diversity**— per-day caps and monthly coverage for balanced selection** Face recognition**— automatic clustering, per-person smart albums, merge, dismiss, reassign, drag-to-fix mis-detected bboxes** Library management**— import by copying to managed library with SHA-256 dedup** Album system**— manual + 19 smart album types (person, time, score, duplicates, pets, etc.)** Interactive web UI**— real-time slider tuning, photo grid, lightbox, compare view, batch operations** Desktop app**— native macOS app via Tauri v2 (wraps the web UI in a native window)** Soft delete**— 30-day recovery, Recently Deleted album** Demo mode**— try instantly with generated sample photos** Privacy**— local-first, no telemetry, no analytics. The few network calls bpp makes (model downloads on first analyze, OpenStreetMap tiles when you open the Map view, update checks against GitHub Releases, optional LAN sharing, optional`pip install`\n\nof extra features) are all enumerated below and individually disclosable; nothing about your library is ever sent.\n\nRequires Python 3.11(3.12+ is not yet supported — it's pinned to match the desktop sidecar). On macOS, Homebrew ships a newer Python by default; install 3.11 with`brew install python@3.11`\n\nor[, or use the no-Python]`pyenv`\n\n[desktop app]below.\n\nRecommended — [pipx](https://pipx.pypa.io/) keeps bpp in its own\nenvironment and makes updating one command:\n\n```\npipx install \"bppicker[web]\"\n\n# Update to the latest release later:\npipx upgrade bppicker\n```\n\nPlain pip works too:\n\n```\npip install \"bppicker[web]\"\n\n# Optional: face recognition\npip install \"bppicker[web,faces]\"\n\n# Optional: HEIC support\npip install \"bppicker[heic]\"\n```\n\nMost ML-powered features (face recognition, NudeNet, RAW import,\nHEIC, AI inpainting) install on demand from **Settings → Advanced\n→ ML Models** in the running app, or you can pre-install\neverything at once with `pip install \"bppicker[heic,faces,raw,nudity,inpaint]\"`\n\n.\n\nPrefer a dock icon over a terminal? Each release ships a standalone Mac app. No Python, no terminal:\n\n- Download\n(always the latest release).[BestPhotoPicker-macOS-arm64.dmg](https://github.com/Arkalogy/best-photo-picker/releases/latest/download/BestPhotoPicker-macOS-arm64.dmg) - Double-click the\n`.dmg`\n\nand drag**Best Photo Picker** into**Applications**. - Open it from Applications or Spotlight.\n\nThe app is signed with an Apple Developer ID and notarized by Apple,\nso it opens on double-click — no security warning. To update, download\nthe newer `.dmg`\n\nthe same way; your photo library and settings live\noutside the app and carry over untouched.\n\nApple Silicon (M1/M2/M3 and later) only. On an Intel Mac, or on Windows/Linux, use the\n\n`pipx install \"bppicker[web]\"`\n\npath above.\n\n```\nbpp demo\n```\n\nGenerates sample photos and launches the web UI — no configuration needed.\n\n```\n# Start the photo management server (default library: ~/Pictures/BestPhotoPicker)\nbpp serve\n\n# Or specify a custom library path\nbpp serve --library ~/Photos/2024\n```\n\nOpen [http://127.0.0.1:5001](http://127.0.0.1:5001) in your browser. Import folders, adjust scoring weights with sliders, and export your curated selection.\n\n```\n# One-shot: analyze + select best 50 photos\nbpp run --input ~/Photos/MyKid --k 50 --out ~/curated --gallery\n\n# Or step-by-step:\nbpp analyze --input ~/Photos/MyKid --out ~/workdir\nbpp select --workdir ~/workdir --k 50 --out ~/curated --gallery\nImport → Analyze → Score → Deduplicate → Select → Export\n```\n\n**Import**: photos copied to managed library with SHA-256 dedup** Analyze**: parallel feature extraction (blur, exposure, faces, composition) cached in SQLite** Score**: weighted combination of sub-scores, tunable in real-time via sliders** Deduplicate**: perceptual hash clustering removes burst duplicates; optional CLIP semantic dedup** Select**: greedy selection with per-day caps and monthly coverage for temporal diversity** Export**: copy/hardlink/symlink selected photos with optional HTML gallery\n\n| Feature | Description |\n|---|---|\n| Photo grid | Thumbnail grid with score badges, zoom control, sort & filter |\n| Lightbox | Full-size viewer with keyboard navigation |\n| Sliders | Real-time weight tuning (blur, exposure, face, composition) |\n| BPP Picks | Sidebar sub-item picks best K photos across the full library; toolbar chip filters picks in the current album/view |\n| Albums | Manual and smart albums (by time, score, person) |\n| Faces | Auto-clustered face detection with merge, dismiss, and per-person albums |\n| Batch ops | Multi-select with Cmd/Ctrl-click, bulk include/exclude/favorite |\n| Import | Drag folders or archives into the library |\n| Export | Copy or link selected photos to an output directory |\n\n| Parameter | Default | Description |\n|---|---|---|\n`blur_weight` |\n0.30 | Weight for sharpness in aggregate score |\n`exposure_weight` |\n0.20 | Weight for exposure quality |\n`face_weight` |\n0.35 | Weight for face detection & framing |\n`composition_weight` |\n0.15 | Weight for composition (rule of thirds) |\n`max_long_side` |\n1024 | Downscale images to this before analysis |\n`hash_distance_threshold` |\n10 | dHash Hamming distance for \"same\" image |\n`time_window_seconds` |\n15 | Cluster burst photos within this window |\n`max_per_day` |\n3 | Max selected photos per calendar day |\n`min_per_month` |\n1 | Try to include at least 1 per month |\n\nbpp runs all ML inference on CPU by default. ONNX-based models (SCRFD\nface detection, CLIP semantic search, YOLOv11n pet detection) can opt\ninto hardware acceleration through the `BPP_ONNX_PROVIDERS`\n\nenv var:\n\n```\n# Apple Silicon (M1/M2/M3) — use the Apple Neural Engine via CoreML\nBPP_ONNX_PROVIDERS=\"CoreMLExecutionProvider,CPUExecutionProvider\" \\\n    bpp serve --library ~/Pictures/BestPhotoPicker\n\n# NVIDIA GPU (Linux) — install onnxruntime-gpu first\npip uninstall -y onnxruntime && pip install onnxruntime-gpu\nBPP_ONNX_PROVIDERS=\"CUDAExecutionProvider,CPUExecutionProvider\" \\\n    bpp serve --library ~/Pictures/BestPhotoPicker\n\n# Windows DirectML\nBPP_ONNX_PROVIDERS=\"DmlExecutionProvider,CPUExecutionProvider\" \\\n    bpp serve --library ~/Pictures/BestPhotoPicker\n```\n\nCPU is always appended as the final fallback so a missing provider in your wheel falls through cleanly with a warning rather than crashing. Ordering matters — providers are tried in the order listed.\n\n| Surface | Tested in CI | Supported | Notes |\n|---|---|---|---|\nmacOS arm64 (M1/M2/M3) — CPU |\nyes (dev box) | yes | Primary dev target. Tauri desktop app ships only this. |\nLinux x86_64 — CPU |\nyes (`ubuntu-latest` ) |\nyes | Default for PyPI install. |\nLinux arm64 — CPU |\nno | yes | Docker image builds; runtime not exercised in CI. |\nWindows — CPU |\nno | yes | Code path exists; never run end-to-end. Reports welcome via GitHub issues. |\nmacOS arm64 — CoreMLExecutionProvider (ANE) |\nno | yes | Code path exists. Expected 5-10× face inference speedup; correctness depends on the specific ONNX graph and ONNX Runtime version. |\nLinux x86_64 — CUDAExecutionProvider |\nno | yes | Requires `pip install onnxruntime-gpu` (replaces base `onnxruntime` ). Free-tier CI doesn't have a GPU runner. |\nWindows — DmlExecutionProvider |\nno | yes | Same caveat as Windows CPU — code path exists, no end-to-end run. |\n\n\"Supported\" means the code path accepts the configuration and\nshouldn't crash; report bugs via GitHub Issues if it does. \"Tested in\nCI\" means we run the full test suite on every push for that surface.\nAnything between the two is on the user's risk surface — your\nhardware, your driver stack, your call. We'd love to expand the\ntested column as users report working setups; PRs welcome that add\nmatrix entries to `.github/workflows/ci.yml`\n\n.\n\nFace extraction is single-threaded per subprocess by default\n(`_face_extract_workers=1`\n\n). On a 6,000-photo library that's ~50 min\non Apple Silicon (roughly 0.5 s/photo). Two config knobs let operators\ntrade RAM for time:\n\n```\n# settings table or YAML config\n_face_extract_workers: 4        # number of parallel workers\n_face_extract_pool: process     # \"process\" | \"thread\"\n```\n\nRecommended combos:\n\n| Setup | `workers` |\n`pool` |\nExpected throughput | Peak RAM |\n|---|---|---|---|---|\n| Default (safe everywhere) | 1 | thread | 1× baseline (~50 min / 6k photos) | ~700 MB |\n| Power user, 16+ GB RAM | 4 | process | ~3-4× (~13 min / 6k photos) | ~3 GB |\n\n*Measured on Apple M-series; results vary by photo resolution and system load.*\n\nThe `process`\n\npool is the only memory-safe parallel option — there is\nan audited race in the embed/landmark thread-stack that can SIGSEGV\na multi-threaded worker. ProcessPool gives each worker its own model\narena so any race is isolated. Trade-off: each worker pays a ~5-10 s\ncold start for model loads, so it's only worthwhile for libraries with\nhundreds of photos or more.\n\nThe native-thread-pool pinning (OMP_NUM_THREADS / OPENBLAS_NUM_THREADS\n/ MKL_NUM_THREADS = 1) at `bpp/web/face_worker.py`\n\nand\n`bpp/web/analyze_worker.py`\n\nmodule-import time applies regardless of\npool choice — required to prevent nested-thread oversubscription that\nwould crash the child silently. See `tests/test_face_thread_safety.py`\n\nfor the full regression guard suite.\n\nOpen the library on a phone, tablet, or another computer connected to the same Wi-Fi. Two-step flow: phone scans QR, owner approves on Mac. After that, the device is trusted and reconnects silently.\n\n- Open\n**Settings → Share** on the host machine - Toggle\n**LAN sharing** on.**First time only**: if the server started with sharing off, it bound to loopback only and the toggle replies**\"Restart required\"**. Quit and relaunch bpp once — the next start picks up the persisted flag and binds the LAN interface. Subsequent toggles flip instantly. - The pane fills in with a share URL, a scannable QR code, and a live Devices list\n\n- On phone: point the camera at the QR code → tap the link\n- Phone shows a\n**\"Waiting for the owner to approve…\"** page - On Mac: a new card appears under\n**Pending requests**(e.g. \"iPhone — 192.168.1.42\") within a few seconds - Click\n**Approve**→ phone auto-reloads into the full app - The phone moves into\n**Trusted devices** and stays there. Future visits skip the approval step entirely.\n\n**Trusted devices → Revoke** kicks the device immediately. The phone shows \"Access revoked\" within ~3 seconds.**Pending requests → Block** rejects a request you didn't make (e.g. someone else scanned the QR by accident).- The phone never silently re-requests access. To ask again, the\nphone user has to actively tap\n**\"Request access again\"** on the revoked page.\n\nIf the URL leaked entirely (someone screenshotted it, you sent it to the wrong person):\n\n**Settings → Share → Revoke** rotates the share token. All current URLs stop working immediately. A new URL + QR replaces the old one. Already-trusted devices keep working until you also revoke them individually.\n\n**Pending requests** with badge count when phones are waiting**Trusted devices** with last-seen timestamps and a \"previously revoked\" tag if you'd kicked them before**Recent access**— last 10 share-link auth events (deduped per 10-minute window)\n\n**Only enable on networks you\ntrust** — home Wi-Fi, not coffee shops or hotel networks. This is\nLAN-only: there is no cloud relay and no internet exposure.\n\nFor the threat model, schema, and OSS extension hooks (HTTPS,\nProxyFix, future user accounts), see [docs/security.md](https://github.com/Arkalogy/best-photo-picker/blob/main/docs/security.md).\n\n`--host`\n\ndefaults to `127.0.0.1`\n\n(loopback only) when LAN sharing is\noff, and `0.0.0.0`\n\nwhen sharing is on at startup. The canonical way\nto enable LAN access is **Settings → Share**, not the flag — pass\n`--host`\n\nexplicitly only for reverse-proxy / Docker setups.\n\nA double-clickable launcher template ships at\n`launch.command.template`\n\n; copy to `launch.command`\n\n, mark executable\n(`chmod +x`\n\n), and Finder-double-click to start the server + Tauri\nwindow with the documented default library path. Override via\n`BPP_LIBRARY`\n\nenv var.\n\nA `Dockerfile`\n\nships at the repo root for self-hosters who want to\nrun bpp behind a reverse proxy. It's a multi-stage build that\ninstalls the `[web,faces,nudity,heic]`\n\nextras; the README's\n\"native macOS app\" framing applies to the Tauri desktop wrapper,\nnot the server. Bind to loopback inside the container and publish\nvia `-p 127.0.0.1:5001:5001`\n\n— the LAN-sharing toggle is owner-\nonly by design and isn't appropriate for an internet-exposed\ncontainer. Set `BPP_TRUSTED_PROXIES`\n\nto your reverse proxy's CIDR\nso the LAN gate honors `X-Forwarded-For`\n\ncorrectly; see\n[docs/security.md](https://github.com/Arkalogy/best-photo-picker/blob/main/docs/security.md).\n\n```\nbpp serve [--library PATH] [--host HOST] [--port 5001] [--no-browser] [--config FILE]\nbpp demo [--port 5001] [--no-browser] [--keep]\nbpp web [--input DIR] [--workdir DIR] [--port 5001] [--no-browser]\nbpp analyze --input DIR --out WORKDIR [--config FILE] [--max N] [--workers K]\nbpp select --workdir DIR --k N --out DIR [--copy|--hardlink|--symlink] [--gallery]\nbpp run --input DIR --k N --out DIR [all options from analyze + select]\nbpp pick LIBRARY [--top N] [--boost-face NAME]... [--out DIR] [--json|--paths-only]\n                 [--quality original|high|medium|low] [--dry-run]\nbpp model {list|accept|accepted|use-context|byom|remove|registry}\n                 — text-mode parity with the GUI click-through for\n                   restricted-license models. Use when running headless,\n                   in CI, or scripting the acceptance flow.\nbpp db restore-backup --library PATH [--previous] [--yes] [--accept-stale] [--force]\n\nGlobal: --seed INT (default 42), --extensions STR (default \"jpg,jpeg,png,heic\")\n```\n\nThis tool is **local-first** — your photos never leave your machine\nfor analysis, scoring, or selection. There is no telemetry and no\ncloud account.\n\nNetwork usage is limited to the following — each is independently disclosable, and the ones that fire automatically (update check, map tiles, model downloads on first analyze) can be turned off:\n\n**Model downloads**(first-run only) — face detection, CLIP, etc., fetched from public model hosts: HuggingFace (`huggingface.co`\n\n), Google Storage (`storage.googleapis.com`\n\n), the OpenCV opencv_zoo on`media.githubusercontent.com`\n\n, OpenAI's Azure CDN for the CLIP tokenizer vocab (`openaipublic.azureedge.net`\n\n), GitHub releases for Ultralytics/YOLO (`github.com/ultralytics`\n\n), and GitHub releases for LaMa inpainting weights (`github.com/enesmsahin`\n\n, only when`bppicker[inpaint]`\n\nis installed and the user clicks \"Remove object\")**Runtime dependency installs**(only when you click Install in Settings → Advanced → ML Models) — runs`pip install`\n\nagainst PyPI to fetch optional packages like face recognition, NudeNet, ONNX Runtime, or AI inpainting. TLS-only trust; transitive dependencies are not hash-pinned (same trust level as`pip install`\n\nfrom a terminal).**Model weights** fetched at first use are all SHA-256 pinned and verified by bpp before loading, including the LaMa inpainting weights (`big-lama.pt`\n\n).**Update check**(background, default-on) — calls api.github.com to see if a new version is out; can be turned off in Settings → App. Sends only a generic User-Agent and your current version — no library data, no telemetry. 256 KB response cap enforced in bpp (client-side) — oversized responses are refused before any bytes are parsed**Map tiles**(only when viewing photo locations on the Map view or in the lightbox for a geo-tagged photo) — OpenStreetMap is queried with the tile coordinates around your photo. Each visited tile reveals approximate photo coordinates to OSM's tile-server access logs. The map view is opt-in by user action (you have to open it); no tile fetches happen on first paint**LAN sharing**(only if you enable it) — phone access over your local Wi-Fi; never traverses the internet\n\nAll photo analysis runs on your machine. Originals are never sent anywhere, and soft-delete keeps recoverable copies for 30 days.\n\n**Python 3.11**(CI tests this version only — 3.12+ is not validated). On macOS, Homebrew ships 3.14 by default — install 3.11 via`brew install python@3.11`\n\nor`pyenv install 3.11`\n\n.**Node.js 18+** and npm (for desktop app only)**Rust**(stable, for desktop app only) — install via[rustup.rs](https://rustup.rs)** CMake + C++ compiler**(for dlib face recognition) — macOS:`brew install cmake`\n\n```\ngit clone https://github.com/Arkalogy/best-photo-picker.git\ncd best-photo-picker\n\npython3.11 -m venv .venv\nsource .venv/bin/activate\npip install -e \".[dev,web,faces]\"\n\n# Optional: HEIC support\npip install -e \".[heic]\"\nbpp serve --library ~/Pictures/BestPhotoPicker --no-browser\n# Open http://127.0.0.1:5001\n```\n\nThe desktop app wraps the web UI in a native macOS window via Tauri.\n\n```\n# Install JS dependencies\ncd desktop && npm install && cd ..\n\n# Dev mode (starts Python server + Tauri window)\ncd desktop && npm run dev\n```\n\nFor a standalone `.app`\n\nbundle (launchable from Finder/Spotlight):\n\n```\n# Build sidecar (PyInstaller-bundled Python server)\ncd desktop && ./build-sidecar.sh\n\n# Build .app + .dmg\nnpm run build\n# Output: desktop/src-tauri/target/release/bundle/\n```\n\nThe sidecar binary lands at `desktop/src-tauri/binaries/bpp-server-{target-triple}`\n\n(e.g., `bpp-server-aarch64-apple-darwin`\n\n).\n\n```\n# Python\nruff check . && ruff format --check .\npytest -v                        # full pytest suite\n\n# Frontend (JS) — requires `npm install` once\nnpm run lint                     # ESLint + auto-generated globals allowlist\nnpm run format:check             # Prettier (scoped to dev-tool JS)\nnpm run typecheck                # tsc --noEmit on @ts-check files\nnpm run test:js                  # Vitest + jsdom unit tests under tests-js/\n\n# End-to-end smoke tests (Playwright)\nnpx playwright install chromium  # one-time browser install\n# (start `bpp serve` first, then:)\nnpm run test:e2e                 # full Playwright suite under tests-e2e/\nbpp/\n  cli.py, commands.py       # CLI entry points\n  scoring/                  # Blur, exposure, face (SCRFD/BlazeFace/dlib), composition\n  dedupe/                   # Perceptual hash + CLIP semantic dedup\n  selection/                # Greedy chooser with diversity constraints\n  db/                       # SQLite schema, migrations, CRUD\n  web/                      # Flask app, blueprint-per-feature, background workers\n    static/js/              # Vanilla JS SPA (modules under static/js/modules/)\n    templates/index.html    # Single-page HTML shell\ndesktop/\n  src-tauri/                # Tauri v2 Rust app\n  build-sidecar.sh          # PyInstaller sidecar build\n  scripts/dev-macos.sh      # Dev launcher\n```\n\n— integration branch. Feature branches merge here.`develop`\n\n— stable/release. Only`main`\n\n`develop`\n\n→`main`\n\nmerges.- CI runs on PRs to\n`main`\n\nonly (lint + pytest, Python 3.11).\n\n```\ncd desktop\n./build-sidecar.sh   # builds the Python server into a standalone binary\nnpm run build         # builds .app + .dmg\n```\n\nThe `.dmg`\n\nis at `desktop/src-tauri/target/release/bundle/dmg/`\n\n. Open it and drag to `/Applications`\n\n.\n\nNote:Without an Apple Developer certificate, macOS Gatekeeper will block the app. To open it: right-click → Open → Open. This only needs to be done once.\n\nThe library folder (default `~/Pictures/BestPhotoPicker/`\n\n) contains everything:\n\n```\nBestPhotoPicker/\n  photos/    # your imported photos (organized by batch)\n  data/      # SQLite database (scores, faces, albums, settings)\n  cache/     # thumbnails and face crops (regenerated automatically)\n  logs/      # server logs\n```\n\nTo move to a new machine:\n\n- Copy the entire\n`BestPhotoPicker/`\n\nfolder to the new machine (same path or any location) - Install the app on the new machine\n- Start it:\n`bpp serve --library ~/Pictures/BestPhotoPicker`\n\nThe app automatically detects that file paths have changed (different username, different OS path) and **remaps all photos by SHA-256 hash** on first startup. Your scores, albums, face clusters, favorites — everything is preserved. The `cache/`\n\nfolder can be skipped to save transfer time; thumbnails regenerate on demand.\n\n** dlib fails to install** — Requires CMake and a C++ compiler. macOS:\n\n`brew install cmake`\n\n. Ubuntu: `sudo apt install cmake build-essential`\n\n.**HEIC photos not recognized** — `pip install \"bppicker[heic]\"`\n\n. Linux may also need `libheif-dev`\n\n.\n\n**Port 5001 in use** — `bpp serve --port 8080`\n\n, or check what's holding it: `lsof -i :5001`\n\n.\n\n** ImportError: cv2-related errors after upgrading or installing extras** —\n\n`bpp`\n\ndeclares `opencv-python-headless`\n\nas its base OpenCV. `bppicker[faces]`\n\n(mediapipe) pulls in `opencv-contrib-python`\n\nand `bppicker[inpaint]`\n\n(simple-lama-inpainting) pulls in `opencv-python`\n\n. All three install into the same `cv2`\n\nnamespace and the last-installed one wins on disk. With matching version numbers it's harmless, but a future minor bump in any of them can produce hard-to-diagnose import-order errors. If you hit one, reset to the headless variant: `pip uninstall opencv-python opencv-contrib-python && pip install --force-reinstall opencv-python-headless`\n\n.**Desktop app won't reopen from dock** — Quit fully (Cmd+Q), then relaunch. The Tauri app handles dock-click reopen.\n\n**App fails to start after an upgrade / failed migration** — every\nschema migration writes a verified `data/photopicker.db.backup`\n\nbefore mutating anything. To roll back the live DB to that\nsnapshot:\n\n```\n# Stop any running server first.\npkill -f 'bpp serve'\n\n# Restore. The current DB is moved aside with a timestamped\n# suffix so nothing is destroyed. Use --previous to roll back\n# to the older `.backup.prev` snapshot if `.backup` is also\n# bad.\nbpp db restore-backup --library ~/Pictures/BestPhotoPicker\n```\n\nThe command verifies the backup's integrity before swapping it in and refuses if the backup is corrupt. Once the app starts and your library looks correct, you can delete the moved-aside files manually.\n\n**Downgrading bpp to an older version** — bpp's schema migrations\nare *forward-only*. Once a newer bpp version migrates your library\nDB, an older bpp will refuse to open it (or crash on missing\ncolumns). To roll back:\n\n- Install the older bpp version (\n`pip install bppicker==X.Y.Z`\n\nor reinstall the matching bundle). - Restore the pre-migration snapshot:\n\n```\npkill -f 'bpp serve'\nbpp db restore-backup --library ~/Pictures/BestPhotoPicker --previous\n```\n\n- Start the older bpp.\n\nThe `--previous`\n\nflag restores `.backup.prev`\n\n— the snapshot taken\nbefore the most recent backup rotation, which is what bpp wrote\njust before the upgrade migration ran. This works for **single-\nversion rollbacks** (upgraded once, want to go back). If you've\nalready started the new version multiple times or run multi-step\nmigrations, `.backup.prev`\n\nmay have been overwritten — at that\npoint the only path back is restoring from your own external\nbackup of the library directory.\n\nUser-facing release notes live in [CHANGELOG.md](https://github.com/Arkalogy/best-photo-picker/blob/main/CHANGELOG.md).\n\nIf you find this useful, [star the repo](https://github.com/Arkalogy/best-photo-picker) and spread the word. Bug reports and PRs welcome.\n\n[☕ Buy us a coffee](https://buymeacoffee.com/arkalogy)— covers a model-download CDN bill and a focused afternoon of bug fixes[📬 Hire Arkalogy](mailto:support@arkalogy.com)— custom integrations, AI-assisted product work, or PM consulting\n\nBuilt and maintained by [ Arkalogy](https://arkalogy.com). bpp started as a tool to triage a large family photo library without sending it to anyone's cloud — most existing options either live in someone else's data center or are designed for professional photographers. If you have the same problem and this helps, that's the win.\n\nThis codebase was built with **Claude (Anthropic)** — implementation and line-by-line code review are both AI-driven, not human. The maintainer works as the product owner and architect: directing the work, approving every commit, and capturing load-bearing architectural decisions in [ docs/adr/](/Arkalogy/best-photo-picker/blob/main/docs/adr). It's a deliberate experiment in how far a PM-led, AI-built product can go while staying disciplined — hence the registries, the test gates, the legal posture in\n\n[, and the ADRs.](/Arkalogy/best-photo-picker/blob/main/MODEL_POLICY.md)\n\n`MODEL_POLICY.md`\n\nMIT — see [LICENSE](https://github.com/Arkalogy/best-photo-picker/blob/main/LICENSE). See [NOTICE.txt](https://github.com/Arkalogy/best-photo-picker/blob/main/NOTICE.txt) for the full third-party license breakdown.\n\nbpp itself and every dependency installed by default are permissively licensed (MIT / Apache-2.0 / BSD / HPND). Copyleft components are strictly opt-in and never installed without an explicit extra:\n\npulls in`bppicker[heic]`\n\n`pillow-heif`\n\n(GPLv2 binary wheel, due to bundled x265; Python source is BSD-3) for HEIC support.pulls in`bppicker[nudity]`\n\n`NudeNet`\n\n(GPL-3.0) for the optional NSFW filter. If you distribute a derived work that bundles NudeNet, GPL-3.0 terms attach to the whole work — that's why we ship it as an opt-in extra and never as a default dependency.pulls in`bppicker[raw]`\n\n`rawpy`\n\n(MIT, links to LibRaw which is LGPL-2.1) for camera-RAW support.pulls in`bppicker[inpaint]`\n\n`simple-lama-inpainting`\n\n(Apache 2.0) for AI object removal, plus`torch`\n\nand`torchvision`\n\n(both BSD-3) transitively. The LaMa model weights are downloaded on first use from the upstream LaMa research project ([advimman/lama](https://github.com/advimman/lama)) and have their own license terms — review them before redistributing a derived work.\n\nThe Ultralytics YOLOv11n weights downloaded for pet detection are AGPL-3.0; we use the ONNX-exported weights only (no Ultralytics Python code runs in your process). See [NOTICE.txt](/Arkalogy/best-photo-picker/blob/main/NOTICE.txt) for the full picture.\n\nBppicker bundles no model weights. Every optional model is downloaded by your local installation directly from the upstream provider on first use, subject to upstream model terms — bppicker does not redistribute or relicense them.\n\nArkalogy will not monetize, sell, or market Best Photo Picker for commercial workflows. Because the source code is MIT-licensed, third parties may still use it commercially; restricted-model access is separately controlled by model-specific terms and app-level gates (commercial-use gate, hard-block, click-through acknowledgment). Optional third-party models may have separate licenses, including non-commercial or research-only restrictions — commercial users must select commercial-safe models or provide their own licensed weights.\n\nFor face recognition specifically, bppicker's default embedder (SFace) is permissively licensed and commercial-use-safe. Other face embedders are available behind an opt-in click-through; some are research-only / non-commercial and are hard-blocked when you select \"Commercial\" as your use context. The Bring-Your-Own-Model path lets you point bppicker at a local ONNX file you have your own rights to.", "url": "https://wpnews.pro/news/ai-powered-photo-gallery-without-the-cloud", "canonical_source": "https://github.com/Arkalogy/best-photo-picker", "published_at": "2026-06-27 00:07:04+00:00", "updated_at": "2026-06-27 00:35:40.794297+00:00", "lang": "en", "topics": ["ai-tools", "computer-vision", "ai-products"], "entities": ["Arkalogy", "Best Photo Picker", "Apple Photos", "Google Photos", "Lightroom", "digiKam", "YuNet", "SCRFD"], "alternates": {"html": "https://wpnews.pro/news/ai-powered-photo-gallery-without-the-cloud", "markdown": "https://wpnews.pro/news/ai-powered-photo-gallery-without-the-cloud.md", "text": "https://wpnews.pro/news/ai-powered-photo-gallery-without-the-cloud.txt", "jsonld": "https://wpnews.pro/news/ai-powered-photo-gallery-without-the-cloud.jsonld"}}