Jeder kennt es: Tausende Fotos auf verschiedenen Festplatten, eine Synology im Keller, Backups in der Cloud — und die einzige Suchfunktion ist manuelles Durchklicken. „Welche Fotos habe ich von der Sardinien-Reise 2019?" erfordert eine halbe Stunde Ordnernavigation und gute Erinnerung an die Dateistruktur.

Mein Archiv bestand aus 15 Ordnern, verteilt auf drei NAS-Volumes, gewachsen über 15 Jahre. Kameras von Canon über GoPro bis Insta360. Formate von JPEG über RAW bis zu 360°-Videos. Keine einheitlichen Dateinamen, keine Tags, keine Suche.

Mein Ziel: Eine App, die das gesamte Archiv als digitales Leuchtpult darstellt — durchsuchbar nach Ort, Zeit, Kamera, Inhalt und Personen. Offline-fähig. Auf dem iPad. Das Ergebnis nach drei Wochen:

151.636
Dateien
70.342
GPS-Punkte
260.259
KI-Beschreibungen
4,6 GB
iPad-Paket

Die Pipeline — 11 Stufen im Überblick

Die Pipeline verwandelt eine unstrukturierte Festplatte in eine durchsuchbare Datenbank. Jede Stufe ist eigenständig — du kannst nach Stufe 2 aufhören und hast bereits ein brauchbares Archiv. Oder du gehst bis Stufe 11.

01

Dateisystem-Scan & Metadaten Pflicht

Rekursiver Walk, EXIF-Extraktion, Video-Metadaten → SQLite-Datenbank

02

Kleine Thumbnails (96 + 176 px) Pflicht

Grid-Ansicht und Leuchtpult — parallel zur Indexierung

03

Große Vorschauen (800 px) Optional

Detail-Ansicht und Input für KI-Beschreibungen

04

Abgeleitete Tiers (80 + 360 px) Optional

Extra-klein und mittel, kaskadiert aus vorhandenen Tiers

05

Video-Filmstrips Optional

Sprite-Grids: alle 30s ein Frame, max 30, 800 px, 5 Spalten

06

KI-Bildbeschreibungen GPU

Vision-LLM beschreibt jedes Bild als strukturiertes JSON

07

Filmstrip-Beschreibungen GPU

Frame-für-Frame und Gesamtbild, mit Reverse Geocoding

08

Embeddings GPU

Text (BGE-M3, 1024-dim) + Bild (SigLIP2, 768-dim) für semantische Suche

09

Gesichtserkennung GPU

Detection → ArcFace Embedding → DBSCAN Clustering (nur auf Originalen!)

10

Import in PostgreSQL Optional

SQLite → Postgres mit PostGIS für räumliche Suche

11

Pack & Deploy auf iPad Optional

267.000 Thumbnails → 1 SQLite-DB → 103× schnellerer Transfer

Das Herzstück: Was die Metadatenerfassung wirklich tut

Stufe 1 klingt unspektakulär — „Dateien scannen und EXIF lesen". Aber sie ist der Grund, warum alles andere funktioniert. Ohne saubere Metadaten gibt es keine GPS-Karte, keine Zeitachse, keine Kamerafilter, keine Event-Erkennung. Jede spätere Funktion in ArchivBlick baut auf dem auf, was in diesen ersten 12 Stunden Indexierung passiert.

Hier ist, was das Skript reindex_all.py aus jeder einzelnen Datei herausholt — und warum:

Aus jedem Bild: EXIF-Daten

Beispiel: Canon EOS R5, Sardinien 2019

📷
kamera + kamera_make Canon EOS R5 · Canon → Kamerafilter in der App. „Zeige nur GoPro-Aufnahmen" oder „nur Drohne".
🔭
objektiv + iso RF 24-105mm f/4 · ISO 400 → Erkennt Nachtaufnahmen (ISO > 3200) und Makros (hohe Brennweite).
📅
datum + jahr + monat 2019-06-15 14:32:08 → Zeitachse, Jahresfilter, Event-Erkennung (Zeitlücken > 4h = neues Event).
📍
gps_lat + gps_lon + gps_alt 39.2153, 9.1107, 42m → GPS-Weltkarte, Umkreissuche, Reverse Geocoding für Ortsnamen.
📐
aufloesung 8192×5464 → Erkennt, ob genug Pixel für Gesichtserkennung vorhanden sind (min. 3000 px).
💾
groesse_bytes + extension + mimetype 42.318.592 · .cr2 · image/x-canon-cr2 → Dateityp-Filter, Speicherplatz-Statistik, RAW-Erkennung.

Aus jedem Video: ffprobe-Metadaten

Beispiel: GoPro HERO12 Black, Motorradtour

dauer_sek 183.4 Sekunden → Bestimmt Filmstrip-Frames (alle 30s) und Gesamtlänge in der Timeline.
🎞
aufloesung + fps 3840×2160 · 59.94 fps → Unterscheidet 4K von 1080p, Slowmo (120 fps) von Normal.
📍
GPS aus Video-Tags ISO 6709 Location Tag → Viele Kameras (GoPro, DJI, iPhone) schreiben GPS in die MP4-Metadaten.
🔗
quickxor_hash SHA-256 Kurzhash aus Pfad → Verknüpft Video mit seinem Filmstrip-Sprite.

Warum jedes Feld zählt — der Datenfluss in ArchivBlick

Jedes Metadatenfeld ermöglicht eine konkrete Funktion in der App. Ohne die Felder fehlt die Funktion:

Metadatenfeld Ermöglicht in ArchivBlick
datum + jahr Zeitachse, Jahresfilter, Event-Erkennung (Zeitlücke > 4h = neues Event → 834 erkannte Events)
gps_lat + gps_lon GPS-Weltkarte (Leaflet/MapKit), Umkreissuche, Event-Erkennung (GPS-Distanz > 50 km = neues Event)
kamera + kamera_make Kamera-Filter („nur GoPro"), Statistik-View (45 erkannte Kameramodelle), Filmstrip-Filter
aufloesung Qualitäts-Badge (4K/1080p/720p), Entscheidung ob Gesichtserkennung sinnvoll ist
dauer_sek Video-Länge im Viewer, Filmstrip-Frame-Berechnung (min(dauer/30 + 1, 30))
groesse_bytes Speicherplatz-Statistik, Duplikaterkennung (gleiche Größe + gleicher Hash = Duplikat)
extension + mimetype Typ-Filter (Bild/Video/Audio/RAW), Icon-Auswahl, Thumbnail-Strategie (Bild: Pillow, Video: ffmpeg)
pfad + archiv Ordner-Navigation (Sidebar-Tree), Archiv-Zuordnung, Duplikat-Check (archiv+pfad = unique)
quickxor_hash SharePoint-Abgleich (99,91% Match-Rate), Datenintegritäts-Prüfung über Systeme hinweg
llava_beschreibung Volltextsuche („Sonnenuntergang am Strand"), Basis für Text-Embeddings
Die Erkenntnis: Metadaten sind nicht Beiwerk — sie sind die App. Ohne GPS kein Kartenview, ohne Datum keine Timeline, ohne Kameramodell kein Filter. Das Skript, das 12 Stunden lang Dateien scannt, erzeugt dabei die gesamte Datengrundlage für alles, was danach kommt.

Die Datenbank: Was am Ende drinsteht

Nach dem Scan liegt alles in einer einzigen SQLite-Datei: multimedia_index.db. Die Tabelle dateien hat 20 Spalten und 151.636 Zeilen. Jede Zeile ist eine Mediendatei. Jede Spalte ist ein Metadatenfeld, das eine Funktion in der App ermöglicht.

Festplatte mit 151.636 Dateien
reindex_all.py · ~4 Dateien/s
↓ ↓ ↓
EXIF (Pillow)
Video (ffprobe)
Thumbs (sm+md)
multimedia_index.db (149 MB)
iPad-App
Web-Viewer
KI-Pipeline

Thumbnails: Vier Größen, ein System

Nicht jede Ansicht braucht dasselbe Bild. Die Grid-Ansicht mit 200 Bildern pro Seite braucht winzige Vorschauen. Die Detail-Ansicht braucht 800 Pixel. Und ein Vision-LLM braucht mindestens 800 Pixel, um etwas Sinnvolles zu erkennen. Deshalb gibt es vier Tiers:

Tier Pixel Zweck Quelle
xs80 pxKompakte ListenAbgeleitet von sm
sm96 pxiPad-Grid (Leuchtpult)Direkt aus Original
md176 / 360 pxWeb-Viewer, mittlere AnsichtDirekt oder von lg
lg800 pxDetail-Ansicht, KI-InputDirekt aus Original

Alle Thumbnails werden als JPEG gespeichert (Qualität 80%, LANCZOS-Resampling) und über ein Sharding-System in Ordner aufgeteilt: datei_id // 1000 ergibt den Ordnernamen. So enthält jeder Ordner maximal ~1.000 Dateien.

Video-Filmstrips: 30 Frames erzählen die Geschichte

Ein einzelnes Thumbnail sagt wenig über ein 15-minütiges Video. Ein Filmstrip — 30 Frames in einem Sprite-Grid — erzählt die ganze Geschichte auf einen Blick. Im Web-Viewer kann man per Hover über den Filmstrip scrubben und sieht sofort, was in welcher Minute passiert.

Für jedes Video extrahiert filmstrip_gen_nas.py mit 6 parallelen ffmpeg-Prozessen:

  • Alle 30 Sekunden ein Frame, maximal 30 Frames
  • Jeder Frame 800 px breit (Höhe proportional)
  • Zusammenbau als 5-Spalten Sprite-Grid (hstack → vstack)
  • Dazu ein JSON mit Metadaten: Hash, Pfad, Dauer, GPS, Grid-Layout

Insta360-Sonderfall: 360°-Kameras speichern zwei Fisheye-Linsen in einer Datei. Für Filmstrips gibt es die Variante Dual-Lens (Front oben, Back unten, je 400 px) — sie zeigt beide Perspektiven gleichzeitig, ohne aufwendiges Stitching.

5.723
Filmstrips
6,8–18,6×
Echtzeit
2,9 GB
Speicher

KI-Beschreibungen: Drei Wege zum Ziel

Metadaten beantworten „Wo?" und „Womit?" — aber nicht „Was?". Dafür braucht es ein Vision-LLM, das jedes Bild in Sprache übersetzt. Danach findet die Suche nach „Sonnenuntergang am Strand" die richtigen 47 Bilder aus 151.000.

Eigener Mac

Ollama + LLaVA / Qwen

  • + Kostenlos
  • + Daten bleiben lokal
  • ~5 Sek/Bild
  • 6 Tage für 131k Bilder

Cloud-API

Claude Batch / Gemini Flash

  • + Kein Server nötig
  • + Höchste Qualität
  • Daten gehen in die Cloud
  • ~5–15 €/100k Bilder

Die aktuelle Pipeline erzeugt strukturiertes JSON mit 11 Feldern — von der Kurzbeschreibung über erkannte Objekte und Personen bis zu Stimmung, Farben und erkanntem Text im Bild.

Auch ohne KI brauchbar: GPS, Kameramodell, Aufnahmedatum und Ordnerstruktur reichen für die meisten Suchen. Die KI-Beschreibungen sind mächtig, aber nicht zwingend.

Gesichtserkennung: Auflösung schlägt Algorithmus

Die überraschendste Erkenntnis des ganzen Projekts:

800 px Thumbnails: 1% Erkennungsrate. Original (5.472 px): über 90%. ArcFace braucht einen Gesichtsausschnitt von 112 × 112 Pixeln — in einem 800-px-Bild ist ein Gesicht in einer Dreiergruppe nur 60 Pixel breit. Zu wenig.

Die Pipeline ist dreistufig: Detection (Gesichter finden via Vision-LLM), Embedding (512-dimensionaler Fingerabdruck via InsightFace/ArcFace), Clustering (automatische Gruppierung via DBSCAN). Die Benennung passiert über einen iCloud-Trick: Apple Photos hat 28 Personen bereits benannt — per Positionsvergleich werden die Cluster automatisch zugeordnet.

Am Ende: 4,6 GB statt 1,6 TB

Alle Daten werden in drei SQLite-Datenbanken gepackt — keine losen Dateien. 267.000 einzelne JPEGs per Finder: 60 Minuten. Eine SQLite-DB: 35 Sekunden. Faktor 103.

149 MB
Index + Beschreibungen
1,4 GB
Thumbnails
3,06 GB
Filmstrips
97%
Kompression

Drei Ausbaustufen — was du wirklich brauchst

Minimal

Stufen 1 + 2

Durchsuchbar nach Datum, Kamera, GPS, Ordner. Kleine Thumbnails. ~12 Stunden, kostenlos.

Komfortabel

Stufen 1 – 5

Plus 800-px-Vorschauen, Filmstrips, abgestufte Tiers. iPad-ready. +1–2 Tage, kostenlos.

Vollausbau

Stufen 1 – 11

KI-Beschreibungen, semantische Suche, Gesichtserkennung. +1–3 Wochen, 0–240 €.

Lessons Learned

  • Auflösung schlägt Algorithmus — Der Unterschied zwischen dem besten und zweitbesten Gesichtserkennungs-Modell: 0,2%. Zwischen Thumbnail und Original: 89 Prozentpunkte.
  • GPU auf dem NAS bringt nichts — ffmpeg-Thumbnail-Extraktion ist I/O-gebunden, nicht rechengebunden. Spare dir die VAAPI-Konfiguration.
  • Lokale KI ist praxistauglich — 25.000 Bildbeschreibungen pro Tag auf einem Mac mini. Eine Woche für das ganze Archiv, ohne Cloud-Kosten.
  • SQLite-Packing schlägt Dateisysteme — 267.000 Dateien: 60 Minuten. Eine SQLite-DB: 35 Sekunden. Faktor 103.
  • Metadaten sind nicht Beiwerk — sie sind die App — Ohne GPS keine Karte, ohne Datum keine Timeline, ohne Kameramodell kein Filter. Die 12 Stunden Indexierung erzeugen die Grundlage für alles.

Die beste Archiv-Software ist die, die man tatsächlich benutzt. Meine Fotos lagen 15 Jahre verteilt auf drei NAS-Volumes. Jetzt sind sie in einer App, die in 35 Sekunden auf dem iPad landet.