Architektur
Drei Kerncontainer plus ein mDNS-Dienst – ohne separaten Message Broker und ohne zwingend nötige Cloud-Dienste. Fotos werden hochgeladen oder aus NAS-Ordnern importiert und dann auf Metadaten, Vorschaubilder, semantische Vektoren, Gesichter und Text hin verarbeitet. Das Ergebnis ist eine Fotobibliothek, die sich nach natürlicher Sprache, Person und Ort durchsuchen lässt.
Go-Backend, KI-Dienst und PostgreSQL sowie ein mDNS-Dienst, der die LAN-Domain bekannt gibt
metadata, thumbnail, clip, face, ocr
Chinese-CLIP-Bild-Text-Vektoren (je nach Modellstufe 512, 768 oder 1024); ArcFace-Gesichtsmerkmale haben 512 Dimensionen
pgvector-Index für Kosinusdistanz; Suche und Gesichtsabgleich laufen in der Datenbank
Systemarchitektur
Browser sprechen nur mit dem Go-Backend. Es übernimmt Uploads, Job-Steuerung, Vorschaubilder und Berechtigungen, schickt Vorschaubilder zur Inferenz an den KI-Dienst und schreibt alle Ergebnisse in PostgreSQL. Der KI-Dienst ist zustandslos: Er greift nie auf die Datenbank zu und speichert keine Dateien, kann also auf einem leistungsstärkeren Rechner laufen.
Die Web-App ist ins Programm eingebettet, die Installation braucht also kein Node. Datenbankmigrationen laufen beim Start automatisch, ein Update heißt einfach: Image austauschen.
Der integrierte KI-Dienst läuft im internen Docker-Netzwerk und rechnet auf der CPU. Tragen Sie in den Web-Einstellungen eine entfernte Adresse ein, um auf einen Rechner mit Grafikkarte oder einen Mac zu Hause zu wechseln.
Fotodaten, Personen, Einstellungen, Job-Warteschlange und Vektoren liegen alle in PostgreSQL, sodass sich Anwendungsdaten und Job-Status gemeinsam sichern lassen. Die Fotodateien brauchen eine eigene Sicherung.
Foto-Verarbeitungspipeline
Nach dem Import werden zuerst Metadaten ausgelesen und Vorschaubilder erzeugt, danach laufen semantische Vektoren, Gesichts- und Texterkennung parallel. Jeder Schritt wird einzeln eingereiht, sodass bei Neustarts oder Ausfällen des KI-Dienstes nichts verloren geht; die Verarbeitung geht danach weiter.
ingest- Web-Uploads per tus, fortsetzbar nach Abbrüchen
- /photos-Ordner alle 15 Minuten gescannt
- SHA-1-Prüfsumme berechnet, in assets gespeichert
metadata- ExifTool liest Abmessungen und Aufnahmezeit
- GPS, Kamera, Objektiv, Blende und Belichtung
- Zeiten ohne Zone nutzen die eingestellte Zeitzone
thumbnail- libvips erzeugt Vorschaubilder
- Dazu eine 1440px-Vorschau
- FFmpeg extrahiert Bilder und transkodiert Videos
clip- Chinese-CLIP berechnet 1024-d-Vektoren
- Suche in natürlicher Sprache, Kategorien
- Ästhetikbewertung, ähnliche Fotos
face- SCRFD findet Gesichter, richtet an 5 Punkten aus
- ArcFace berechnet 512-d-Merkmale
- Schrittweise Personen zugeordnet
ocr- PP-OCR liest Text in Fotos
- Fotos nach Text durchsuchen
- Erkennt Ausweise, Dokumente, Screenshots
Steuerung der Hintergrund-Jobs
Es gibt keinen separaten Message Broker: Die Job-Warteschlange ist eine jobs-Tabelle in PostgreSQL, die in derselben Transaktion wie die Fotodaten geschrieben wird – ein gespeichertes Foto hat also immer seine Jobs. Worker holen Jobs mit SKIP LOCKED, Leases verhindern hängende Jobs, und Fehlschläge werden mit quadratischem Backoff wiederholt.
-- Einen Job holen (server/internal/jobs/worker.go)
UPDATE jobs SET status = 'running', attempts = attempts + 1,
locked_by = $1, locked_until = now() + $2::interval
WHERE id = (
SELECT id FROM jobs
WHERE status = 'pending' AND run_after <= now() AND type = ANY($3)
ORDER BY priority DESC, run_after
FOR UPDATE SKIP LOCKED -- parallele Worker erwischen nie denselben Job
LIMIT 1
)
RETURNING id, type, asset_id, payload, attempts, max_attempts;- Ein Job desselben Typs für dasselbe Foto wird nicht erneut eingereiht, solange einer wartet oder läuft.
- Eine leere Warteschlange wird jede Sekunde geprüft; Jobs werden nach priority (höchste zuerst) und Fälligkeit (früheste zuerst) geholt.
- Die Lease jedes Jobs (zugleich sein Timeout) beträgt 10 Minuten; startet ein Prozess neu, gibt er seine unerledigten Jobs an die Warteschlange zurück.
- Während jemand Fotos hochlädt, werden keine KI-Jobs geholt, damit Bandbreite und CPU zuerst dem Upload zugutekommen.
- Ist der KI-Dienst nicht erreichbar, pausiert der gesamte KI-Worker, statt Jobs einzeln zu holen, scheitern zu lassen und Wiederholungen zu verschwenden.
- Die Stufe „Automatisch“ erkennt zuerst die Hardware des KI-Servers und wählt passende Modelle, bevor Jobs geholt werden. Das hilft zu vermeiden, dass auf einem Raspberry Pi oder ähnlichen Gerät Modelle geladen werden, die den verfügbaren Speicher übersteigen.
KI-Steuerung
Der KI-Dienst empfängt nur Vorschaubilder und liefert Gesichter, Vektoren und Text zurück. Das Backend bestimmt Dienstadresse, Modelle und Parallelität; der KI-Dienst wählt je nach Hardware CUDA, CoreML / MPS, OpenVINO oder CPU.
Mit „Automatisch“ wird nach der Hardware des KI-Servers gewählt: mit nutzbarer GPU „Hohe Genauigkeit“; nur mit CPU unter 6 GB Arbeitsspeicher „Leicht“, ab 6 GB „Ausgewogen“.
| Stufe | Passende Hardware | Gesichter | Semantische Suche |
|---|---|---|---|
| quality · Hohe Genauigkeit | Mac mit Apple Silicon, NVIDIA-GPU | SCRFD-10G + ArcFace R100 (antelopev2) | Chinese-CLIP ViT-H/14 · 1024-d |
| balanced · Ausgewogen | NAS der Intel-i5-Klasse | SCRFD-10G + ArcFace R100 (antelopev2) | Chinese-CLIP ViT-L/14 · 768-d |
| lite · Leicht | N100, Raspberry Pi 5 | SCRFD + ArcFace R50 (buffalo_l) | Chinese-CLIP ViT-B/16 · 512-d |
POST /predict (multipart: image + tasks)
Authorization: Bearer <Token>
tasks = {
"face": {"model": "antelopev2", "min_score": 0.5},
"clip": {"model": "chinese-clip-vit-h-14"},
"ocr": {}
}
→ {
"face": [{"box": [x, y, w, h], "score": 0.93,
"embedding": [512 Zahlen], "yaw": 0.12, "blur": 340}],
"clip": {"embedding": [1024 Zahlen]},
"ocr": {"text": "...", "lines": [...]},
"model_ids": {"face": "antelopev2", "clip": "chinese-clip-vit-h-14"}
}Kernalgorithmen
Die Modelle, Algorithmen und Schwellenwerte hinter Suche, Personen, Serienbildern und KI-Auswahl. Manches stützt sich auf KI-Modelle, anderes auf Vektorrechnung und Regeln.
Chinese-CLIP legt Fotos und Text in denselben Vektorraum. Bildvektoren werden beim Import berechnet; bei der Suche wird ein Satz wie „dritter Geburtstag des Babys“ zum Textvektor, und die nächstgelegenen Fotos werden per Kosinusdistanz im HNSW-Index von pgvector gefunden.
- Drei Ergebnisquellen werden zusammengeführt: Fotos, in denen die gesuchten Wörter erkannt wurden, zuerst, dann an einem passenden Ort aufgenommene, zuletzt die inhaltlich ähnlichsten.
- Nur eindeutige Treffer: Ein Ergebnis muss fast so ähnlich sein wie der beste Treffer und deutlich ähnlicher als ein durchschnittliches Foto. Das hilft, schwache Treffer auszufiltern, statt lose verwandte Fotos zu liefern.
- Ist der Suchbegriff genau ein Kategoriename (Landschaft, Essen, Screenshots …), wird direkt diese Kategorie angezeigt.
SCRFD findet jedes Gesicht und seine 5 Merkmalspunkte, richtet es aus und schneidet es auf 112×112 zu; ArcFace berechnet ein 512-d-Merkmal (nach Normierung ist das Skalarprodukt die Kosinus-Ähnlichkeit). Gruppiert wird schrittweise nach jedem fertigen Foto, ohne auf die ganze Bibliothek zu warten.
- Scharfe, frontale und ausreichend große „gute Gesichter“ suchen ihre nächsten Treffer: Gehören welche schon zu einer Person, entscheidet die Mehrheit; gehört keiner dazu, sind aber mindestens 3 ähnlich, entsteht eine neue Person.
- Unscharfe, seitliche oder winzige Gesichter legen nie eine neue Person an; sie schließen sich nur an, wenn sie jemandem sehr ähneln. Passanten, die einmal auftauchen, werden nicht erfasst.
- Parallele Gruppierungen teilen sich eine Advisory Lock der Datenbank, sodass eine Person nie doppelt entsteht. Als Titelbild dienen scharfe (Laplace-Varianz), frontale (Yaw), ausreichend große Einzelgesichter.
- Aufeinanderfolgende Aufnahmen derselben Szene haben meist eine CLIP-Ähnlichkeit von 0,94–0,98, eine neue Szene oder Pose liegt typischerweise bei 0,7–0,89; ein Ähnlichkeitsschwellenwert von 0,93 hilft, wahrscheinliche Serien zu erkennen.
- Das empfohlene Foto: mit Gesichtern das, auf dem Gesichter scharf sind und mit offenen Augen in die Kamera blicken; ohne Gesichter die größere Datei (bei gleicher Kamera und gleichen Einstellungen haben unscharfe Bilder weniger Details und werden kleiner komprimiert).
- Mehrfach gespeicherte identische Dateien landen unter „Duplikate“, ohne Auswahl. „Behalten“ verschiebt den Rest nach „Zuletzt gelöscht“, wo er sich wiederherstellen lässt, bis er endgültig gelöscht wird.
Es läuft kein zusätzliches Modell; die CLIP-Vektoren werden wiederverwendet. Aus Beschreibungen von „fotografischen Werken“ und von „alltäglichen Schnappschüssen“ wird je ein Mittelwertvektor gebildet; ihre Differenz ergibt eine „sieht gut aus“-Richtung. Die Projektion eines Fotovektors auf diese Richtung ist sein Wert.
- Dient nur der Rangfolge, nie als absoluter Wert; das Highlight-Karussell der Startansicht wählt Fotos danach aus.
- Ein eigener Satz Beschreibungen erkennt misslungene Aufnahmen (unscharf, überbelichtet usw.), unabhängig davon bewertet, wie gut ein Foto aussieht.
Eingesetzte Technik
Ausgereifte Open-Source-Komponenten ohne proprietäre Dienste; für jeden Baustein gibt es Alternativen.
- GoAPI, Job-Warteschlange, Vorschau-Steuerung
- libvipsVorschaubilder und Vorschauen
- FFmpegVideo-Einzelbilder, Transkodierung
- ExifToolFoto-Metadaten lesen und schreiben
- tusFortsetzbare Uploads
- Python + FastAPIZustandsloser HTTP-Dienst
- ONNX RuntimeInferenz für Gesichter und Text
- PyTorchChinese-CLIP
- InsightFaceSCRFD + ArcFace
- RapidOCRPP-OCR-Texterkennung
- PostgreSQL 17App-Daten und Job-Warteschlange
- pgvectorVektorspeicher
- HNSWIndex für Kosinusdistanz
- ip2regionIP-Standort im Anmeldeverlauf (offline)
- NominatimReverse Geocoding (abschaltbar)
- React + TypeScriptWeb-App
- Vite + Tailwind CSSBuild und Styling
- MapLibre GLSatelliten-Globus
- Docker Composeamd64-/arm64-Images
- mDNSLAN-Domain wanmory.local
Datenschutz & Sicherheit
Standardmäßig bleiben Fotos und KI-Ergebnisse in Ihrem privaten Netzwerk. Wenn Sie einen entfernten KI-Dienst einrichten, werden Vorschaubilder zur Verarbeitung an diesen Server gesendet.
Der KI-Dienst erhält nur Vorschauen mit 1440 Pixeln, speichert keine Dateien und berührt weder Datenbank noch Namen noch Originale.
Der integrierte KI-Dienst existiert nur im internen Docker-Netz und ist allein für das Backend erreichbar; ist für einen entfernten KI-Dienst ein Token gesetzt, muss jede Anfrage ein Bearer-Token mitführen.
Mitglieder können nur ihre eigenen Uploads löschen, Admins verwalten alles; ausgeblendete Fotos sieht nur ihr Eigentümer; der Anmeldeverlauf zeigt einen ungefähren Standort anhand der IP.
NAS-Ordner lassen sich mit :ro schreibgeschützt einbinden, sodass die App darin nichts ändern oder löschen kann.
Nach dem Modell-Download laufen Erkennung und Suche lokal; Funktionen, die Internet brauchen, wie Reverse Geocoding und Satellitenbilder, lassen sich abschalten.
Die InsightFace-Gesichtsmodelle sind nur für nicht kommerzielle Nutzung freigegeben; Modelle sind nicht im Image enthalten, und vor dem Download zeigt die Einstellungsseite die Lizenz jedes Modells.