ARCHITECTURE

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.

3 + 1Container

Go-Backend, KI-Dienst und PostgreSQL sowie ein mDNS-Dienst, der die LAN-Domain bekannt gibt

5Job-Typen

metadata, thumbnail, clip, face, ocr

512–1024Dimensionen

Chinese-CLIP-Bild-Text-Vektoren (je nach Modellstufe 512, 768 oder 1024); ArcFace-Gesichtsmerkmale haben 512 Dimensionen

HNSWVektorindex

pgvector-Index für Kosinusdistanz; Suche und Gesichtsabgleich laufen in der Datenbank

SYSTEM

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.

CLIENTSDesktop-BrowserWeb-App im Go-Programm eingebettetMobiler BrowseriPhone · Androidwanmory.localLAN-Domain, keine IP merkenHTTP :8080DOCKER COMPOSEGo-Backend · serverAPI, Uploads, Job-Warteschlange, Vorschaubilder, Rechte, Web-AppREST APItus-UploadsJob-WorkerKonten · Loginslibvips-VorschauExifTool-MetadatenFFmpeg-VideoGeocoding (optional)Alben · IntelligentSerien · ÄhnlicheKI-Auswahl · HeuteScan alle 15 Min.mdnsGibt wanmory.local bekanntKI-Dienst · mlFastAPI + ONNX Runtime / PyTorch, zustandslosSCRFD-GesichtserkennungArcFace-MerkmaleChinese-CLIPPP-OCR-TexterkennungPostgreSQL 17 + pgvectorApp-Daten, Job-Warteschlange und Vektoren in einer DB, kein Redis nötigassetsfaces · peoplejobs-QueuesettingsHNSW-IndexSQL/predict · 1440px-VorschauSPEICHER · ERWEITERUNG/dataHochgeladene Originale, Vorschaubilder, 1440px-Vorschauen/photos/* (auch schreibgeschützt :ro)Vorhandene Fotoordner auf dem NAS, beliebig viele./modelsModelldateien, in den Web-Einstellungen geladenEntfernter KI-Dienst (optional)Mac (CoreML / MPS) · NVIDIA CUDAToken-Authentifizierung, nur 1440px-Vorschauen, nie Originalelesen/schreibenscannen · nur lesenladenHTTP + Token
Das Backend ist ein einziges Go-Programm

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.

KI-Dienst an anderer Stelle betreiben

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.

Eine Datenbank für alles

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.

PIPELINE

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.

01ingest
Import
  • Web-Uploads per tus, fortsetzbar nach Abbrüchen
  • /photos-Ordner alle 15 Minuten gescannt
  • SHA-1-Prüfsumme berechnet, in assets gespeichert
Backend
02metadata
Metadaten
  • ExifTool liest Abmessungen und Aufnahmezeit
  • GPS, Kamera, Objektiv, Blende und Belichtung
  • Zeiten ohne Zone nutzen die eingestellte Zeitzone
Backend
03thumbnail
Vorschaubilder
  • libvips erzeugt Vorschaubilder
  • Dazu eine 1440px-Vorschau
  • FFmpeg extrahiert Bilder und transkodiert Videos
Backend
04clip
Vektoren
  • Chinese-CLIP berechnet 1024-d-Vektoren
  • Suche in natürlicher Sprache, Kategorien
  • Ästhetikbewertung, ähnliche Fotos
KI · parallel
05face
Gesichter
  • SCRFD findet Gesichter, richtet an 5 Punkten aus
  • ArcFace berechnet 512-d-Merkmale
  • Schrittweise Personen zugeordnet
KI · parallel
06ocr
Text
  • PP-OCR liest Text in Fotos
  • Fotos nach Text durchsuchen
  • Erkennt Ausweise, Dokumente, Screenshots
KI · parallel
SCHEDULING

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.

pendingwartetrunningvon Worker geholtdoneerledigtfailedVersuche aufgebrauchtHolen mit SKIP LOCKEDErfolgWiederholungen aufgebrauchtFehler: neuer Versuch nach n² × 30 sKI nicht erreichbar: nach 15 s erneut, zählt nicht als Fehler10-Minuten-Lease: Unerledigtes wird alle 30 s zurückgeholt und auf pending gesetzt
jobs/worker.go · claim
-- 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;
Regeln der Steuerung
  • 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.
Vorrang und Drosselung
  • 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.
AI SCHEDULING

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.

KI-Job-Warteschlangeclip · face · ocrParallelität laut Einstellung, Drosselung anpassbarGo-Backend · KI-ClientWählt Dienst · Stufe · prüft model_ids· Prüft /health alle 15 s· Nicht erreichbar → Jobs warten· Stufe automatisch → nach HardwareholenIntegrierter KI-DienstInternes Docker-Netz · CPUEntfernter KI-DienstMac oder GPU-Rechner zu HauseEntfernter Worker-KnotenHolt Jobs selbst (geplant)eins von beidenCPUONNX RuntimeCoreML / MPS · CUDAWählt verfügbare BeschleunigungOpenVINOIntel-iGPU
Drei Modellstufen

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“.

StufePassende HardwareGesichterSemantische Suche
quality · Hohe GenauigkeitMac mit Apple Silicon, NVIDIA-GPUSCRFD-10G + ArcFace R100 (antelopev2)Chinese-CLIP ViT-H/14 · 1024-d
balanced · AusgewogenNAS der Intel-i5-KlasseSCRFD-10G + ArcFace R100 (antelopev2)Chinese-CLIP ViT-L/14 · 768-d
lite · LeichtN100, Raspberry Pi 5SCRFD + ArcFace R50 (buffalo_l)Chinese-CLIP ViT-B/16 · 512-d
KI-Dienst-API · /predict
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"}
}
ALGORITHMS

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.

Suche in natürlicher Sprache

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.

sim(q, p) = q · p / (‖q‖ ‖p‖)
  • 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.
Gesichtsgruppierung

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.

d(a, b) = 1 − a · b gutes Gesicht d < 0.55 · unscharf/Profil d < 0.45
  • 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.
Serienbilder und ähnliche Fotos
gleiche Kamera Δt ≤ 2s oder Δt ≤ 60s und sim ≥ 0.93
  • 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.
Ästhetikwert der KI-Auswahl

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.

score(p) = p · (μ_Werk − μ_Schnappschuss)
  • 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.
TECH STACK

Eingesetzte Technik

Ausgereifte Open-Source-Komponenten ohne proprietäre Dienste; für jeden Baustein gibt es Alternativen.

BACKEND
  • Go
    API, Job-Warteschlange, Vorschau-Steuerung
  • libvips
    Vorschaubilder und Vorschauen
  • FFmpeg
    Video-Einzelbilder, Transkodierung
  • ExifTool
    Foto-Metadaten lesen und schreiben
  • tus
    Fortsetzbare Uploads
KI-DIENST
  • Python + FastAPI
    Zustandsloser HTTP-Dienst
  • ONNX Runtime
    Inferenz für Gesichter und Text
  • PyTorch
    Chinese-CLIP
  • InsightFace
    SCRFD + ArcFace
  • RapidOCR
    PP-OCR-Texterkennung
DATEN
  • PostgreSQL 17
    App-Daten und Job-Warteschlange
  • pgvector
    Vektorspeicher
  • HNSW
    Index für Kosinusdistanz
  • ip2region
    IP-Standort im Anmeldeverlauf (offline)
  • Nominatim
    Reverse Geocoding (abschaltbar)
FRONTEND & BETRIEB
  • React + TypeScript
    Web-App
  • Vite + Tailwind CSS
    Build und Styling
  • MapLibre GL
    Satelliten-Globus
  • Docker Compose
    amd64-/arm64-Images
  • mDNS
    LAN-Domain wanmory.local
PRIVACY

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.

Datensparsamkeit

Der KI-Dienst erhält nur Vorschauen mit 1440 Pixeln, speichert keine Dateien und berührt weder Datenbank noch Namen noch Originale.

Authentifizierung

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.

Berechtigungen

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.

Originale schreibgeschützt

NAS-Ordner lassen sich mit :ro schreibgeschützt einbinden, sodass die App darin nichts ändern oder löschen kann.

Funktioniert offline

Nach dem Modell-Download laufen Erkennung und Suche lokal; Funktionen, die Internet brauchen, wie Reverse Geocoding und Satellitenbilder, lassen sich abschalten.

Modelllizenzen

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.