ARCHITECTURE

Arquitectura

Tres contenedores principales, más un servicio mDNS, sin un intermediario de mensajes aparte ni servicios en la nube obligatorios. Las fotos se suben o se importan desde carpetas del NAS y luego se procesan para obtener metadatos, miniaturas, vectores semánticos, caras y texto. El resultado es una fototeca que se puede buscar por lenguaje natural, persona y lugar.

3 + 1contenedores

Backend en Go, servicio de IA y PostgreSQL, más un servicio mDNS que anuncia el dominio local

5tipos de tareas

metadata, thumbnail, clip, face, ocr

512–1024dimensiones

Vectores imagen-texto de Chinese-CLIP (512, 768 o 1024 según el nivel de modelos); los rasgos faciales de ArcFace tienen 512 dimensiones

HNSWíndice vectorial

Índice de distancia coseno de pgvector; la búsqueda y la comparación de caras ocurren en la base de datos

SYSTEM

Arquitectura del sistema

El navegador solo habla con el backend en Go, que se encarga de las subidas, la planificación de tareas, las miniaturas y los permisos, envía las vistas previas al servicio de IA para la inferencia y escribe todos los resultados en PostgreSQL. El servicio de IA no tiene estado: nunca accede a la base de datos ni guarda archivos, así que puede ejecutarse en un equipo más potente.

CLIENTESNavegador de escritorioApp web integrada en el binario GoNavegador móviliPhone · Androidwanmory.localDominio local, sin recordar IPHTTP :8080DOCKER COMPOSEBackend Go · serverAPI, subidas, cola de tareas, miniaturas, permisos, webAPI RESTSubidas tusWorkers de tareasCuentas · AccesosMiniaturas libvipsMetadatos ExifToolVídeo FFmpegGeocodificaciónÁlbumes · InteligentesRáfagas · SimilaresSelección IA · HoyEscaneo cada 15 minmdnsAnuncia wanmory.localServicio de IA · mlFastAPI + ONNX Runtime / PyTorch, sin estadoDetección SCRFDRasgos ArcFaceChinese-CLIPTexto PP-OCRPostgreSQL 17 + pgvectorDatos, cola de tareas y vectores en una sola base, sin Redisassetsfaces · peoplecola jobssettingsíndice HNSWSQL/predict · vistas 1440 pxALMACENAMIENTO · EXTENSIONES/dataOriginales subidos, miniaturas, vistas previas de 1440 px/photos/* (puede ser solo lectura :ro)Carpetas de fotos del NAS, tantas como quieras./modelsArchivos de modelos, descargados desde los ajustes webServicio de IA remoto (opcional)Mac (CoreML / MPS) · NVIDIA CUDAAutenticación con token; solo recibe vistas de 1440 px, nunca originaleslectura/escrituraescaneo · solo lecturacargaHTTP + token
El backend es un único binario Go

La app web va incrustada en el binario, así que la instalación no necesita Node. Las migraciones de la base de datos se ejecutan solas al arrancar, y actualizar es cambiar la imagen.

El servicio de IA puede ir en otro equipo

El servicio de IA integrado funciona en la red interna de Docker y calcula con la CPU. Escribe una dirección remota en los ajustes web para pasar a un PC con tarjeta gráfica o a un Mac de casa.

Una sola base de datos para todo

La información de las fotos, las personas, los ajustes, la cola de tareas y los vectores están todos en PostgreSQL, así que los datos de la app y el estado de las tareas se pueden copiar juntos. Los archivos de fotos necesitan una copia de seguridad aparte.

PIPELINE

Flujo de procesamiento de fotos

Tras importar una foto, primero se extraen sus metadatos y se generan las miniaturas; luego se calculan en paralelo los vectores semánticos, las caras y el texto. Cada paso entra en la cola por separado, así que un reinicio o una caída del servicio de IA no pierde trabajo: el procesamiento sigue cuando todo vuelve.

01ingest
Importación
  • Subidas web con tus, se reanudan tras cortes
  • Carpetas /photos escaneadas cada 15 minutos
  • Suma SHA-1 calculada y guardada en assets
Backend
02metadata
Metadatos
  • ExifTool lee dimensiones y fecha de toma
  • GPS, cámara, objetivo, diafragma y velocidad
  • Horas sin zona usan la zona configurada
Backend
03thumbnail
Miniaturas
  • libvips genera las miniaturas
  • Y una vista previa de 1440 px
  • FFmpeg extrae fotogramas y transcodifica vídeo
Backend
04clip
Vectores
  • Chinese-CLIP calcula vectores de 1024-d
  • Búsqueda en lenguaje natural, categorías
  • Puntuación estética, fotos similares
IA · en paralelo
05face
Caras
  • SCRFD detecta y alinea con 5 puntos
  • ArcFace calcula rasgos de 512-d
  • Agrupadas en personas poco a poco
IA · en paralelo
06ocr
Texto
  • PP-OCR lee el texto de las fotos
  • Buscar fotos por su texto
  • Detecta documentos, carnés, capturas
IA · en paralelo
SCHEDULING

Planificación de tareas en segundo plano

No hay un intermediario de mensajes aparte: la cola de tareas es una tabla jobs en PostgreSQL, escrita en la misma transacción que los datos de la foto, así que una foto guardada siempre tiene sus tareas en la cola. Los workers toman tareas con SKIP LOCKED, los arrendamientos evitan bloqueos y los fallos se reintentan con espera cuadrática.

pendingen colarunningtomada por un workerdonecompletadafailedsin intentostomar con SKIP LOCKEDéxitoreintentos agotadosfallo: reintento tras n² × 30 sIA inaccesible: reintento en 15 s, no cuenta como falloArrendamiento de 10 min: sin resultado a tiempo, se recupera cada 30 s y vuelve a pending
jobs/worker.go · claim
-- Tomar una tarea (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   -- workers simultáneos nunca toman la misma
  LIMIT 1
)
RETURNING id, type, asset_id, payload, attempts, max_attempts;
Reglas de planificación
  • Una tarea del mismo tipo para la misma foto nunca se encola dos veces mientras otra espera o se ejecuta.
  • Con la cola vacía se consulta cada segundo; las tareas se toman por prioridad (la más alta primero) y luego por vencimiento (la más temprana primero).
  • El arrendamiento de cada tarea (que también es su tiempo límite) es de 10 minutos; al reiniciarse, un proceso devuelve a la cola sus propias tareas sin terminar.
Ceder el paso y limitar
  • Mientras alguien sube fotos, no se toman tareas de IA, para que el ancho de banda y la CPU vayan primero a la subida.
  • Si el servicio de IA no responde, todo el worker de IA se detiene en lugar de tomar tareas una a una, hacerlas fallar y gastar reintentos.
  • El nivel «Automático» detecta primero el hardware del servidor de IA y elige modelos adecuados antes de tomar tareas, lo que ayuda a no cargar en una Raspberry Pi o similar modelos que superen la memoria disponible.
AI SCHEDULING

Planificación de la IA

El servicio de IA solo recibe vistas previas y devuelve caras, vectores y texto. El backend decide la dirección del servicio, los modelos y la concurrencia; el servicio de IA elige CUDA, CoreML / MPS, OpenVINO o CPU según su hardware.

Cola de tareas de IAclip · face · ocrConcurrencia según ajustes, límite regulableBackend Go · cliente de IAElige servicio y nivel, verifica model_ids· Comprueba /health cada 15 s· Sin conexión → tareas esperan· Nivel automático → según hardwaretomarServicio de IA integradoRed interna de Docker · CPUServicio de IA remotoUn Mac o PC con GPU de casaNodo de trabajo remotoToma tareas por sí mismo (previsto)uno de los dosCPUONNX RuntimeCoreML / MPS · CUDAElige la aceleración disponibleOpenVINOGPU integrada Intel
Tres niveles de modelos

Con «Automático», el nivel se elige según el hardware del servidor de IA: Alta precisión con una tarjeta gráfica utilizable; solo con CPU, Ligero con menos de 6 GB de memoria y Equilibrado con 6 GB o más.

NivelHardware adecuadoCarasBúsqueda semántica
quality · Alta precisiónMac con Apple Silicon, GPU NVIDIASCRFD-10G + ArcFace R100 (antelopev2)Chinese-CLIP ViT-H/14 · 1024-d
balanced · EquilibradoNAS de clase Intel i5SCRFD-10G + ArcFace R100 (antelopev2)Chinese-CLIP ViT-L/14 · 768-d
lite · LigeroN100, Raspberry Pi 5SCRFD + ArcFace R50 (buffalo_l)Chinese-CLIP ViT-B/16 · 512-d
API del servicio de IA · /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 números], "yaw": 0.12, "blur": 340}],
  "clip": {"embedding": [1024 números]},
  "ocr":  {"text": "...", "lines": [...]},
  "model_ids": {"face": "antelopev2", "clip": "chinese-clip-vit-h-14"}
}
ALGORITHMS

Algoritmos clave

Los modelos, algoritmos y umbrales detrás de la búsqueda, las personas, las ráfagas y la selección de la IA. Unos se apoyan en modelos de IA y otros en cálculo vectorial y reglas.

Búsqueda en lenguaje natural

Chinese-CLIP coloca fotos y texto en el mismo espacio vectorial. Los vectores de imagen se calculan al importar; al buscar, una frase como «tercer cumpleaños del bebé» se convierte en un vector de texto y las fotos más cercanas se encuentran por distancia coseno en el índice HNSW de pgvector.

sim(q, p) = q · p / (‖q‖ ‖p‖)
  • Se combinan tres fuentes de resultados: primero las fotos en las que se reconocieron las palabras buscadas, luego las tomadas en un lugar con ese nombre y por último las más parecidas en significado.
  • Solo se devuelven coincidencias claras: un resultado debe estar casi tan cerca como el mejor y claramente más cerca que una foto cualquiera. Así se filtran las coincidencias débiles en lugar de devolver fotos poco relacionadas.
  • Si la búsqueda es exactamente el nombre de una categoría (paisajes, comida, capturas de pantalla…), se devuelve esa categoría directamente.
Agrupación de caras

SCRFD encuentra cada cara y sus 5 puntos de referencia, la alinea y recorta a 112×112, y ArcFace calcula un vector de 512 dimensiones (tras normalizarlo, el producto escalar es la similitud coseno). La agrupación se hace poco a poco al terminar cada foto, sin esperar a toda la fototeca.

d(a, b) = 1 − a · b cara buena d < 0.55 · borrosa/perfil d < 0.45
  • Las «caras buenas», nítidas, de frente y de tamaño suficiente, buscan sus vecinas más parecidas: si algunas ya pertenecen a una persona, se une a ella por mayoría; si ninguna está asignada pero al menos 3 se parecen, se crea una persona nueva.
  • Las caras borrosas, de perfil o muy pequeñas nunca crean una persona; solo se unen a alguien al que se parecen mucho. Los transeúntes que aparecen una vez no se añaden.
  • Las agrupaciones simultáneas comparten un mismo bloqueo consultivo de la base de datos, así que nunca se crea la misma persona dos veces. Las portadas prefieren caras individuales nítidas (varianza del laplaciano), de frente (guiñada) y grandes.
Ráfagas y fotos similares
misma cámara Δt ≤ 2s o Δt ≤ 60s y sim ≥ 0.93
  • Las tomas seguidas de una misma escena suelen tener una similitud CLIP de 0,94 a 0,98, mientras que un cambio de escena o de pose suele quedar entre 0,7 y 0,89; un umbral de similitud de 0,93 ayuda a identificar las probables ráfagas.
  • La foto recomendada: con caras, aquella en la que las caras se ven nítidas, con los ojos abiertos y mirando a cámara; sin caras, el archivo más grande (con la misma cámara y ajustes, las fotos movidas tienen menos detalle y se comprimen más).
  • Las copias repetidas de un mismo archivo van a «Duplicados», sin nada que elegir. «Conservar» mueve el resto a «Eliminadas recientemente», desde donde se puede recuperar hasta que se elimine definitivamente.
Puntuación estética de la selección IA

No se ejecuta ningún modelo adicional: se reutilizan los vectores CLIP. Promediando los vectores de un conjunto de descripciones de «obras fotográficas» y de otro de «fotos cotidianas» y restándolos se obtiene una dirección de «queda bien». La proyección de una foto sobre esa dirección es su puntuación.

score(p) = p · (μ_obra − μ_cotidiana)
  • Solo sirve para ordenar, nunca como nota absoluta; el carrusel de selecciones de la vista principal elige las fotos según ella.
  • Otro conjunto de descripciones identifica las fotos fallidas (movidas, sobreexpuestas y similares), con una puntuación independiente de lo bien que se vea una foto.
TECH STACK

Tecnologías utilizadas

Todo son componentes de código abierto maduros, sin servicios propietarios; cada pieza tiene alternativa.

BACKEND
  • Go
    API, cola de tareas, planificación de miniaturas
  • libvips
    Miniaturas y vistas previas
  • FFmpeg
    Fotogramas de vídeo, transcodificación
  • ExifTool
    Lectura y escritura de metadatos
  • tus
    Subidas reanudables
SERVICIO DE IA
  • Python + FastAPI
    Servicio HTTP sin estado
  • ONNX Runtime
    Inferencia de caras y texto
  • PyTorch
    Chinese-CLIP
  • InsightFace
    SCRFD + ArcFace
  • RapidOCR
    Reconocimiento de texto PP-OCR
DATOS
  • PostgreSQL 17
    Datos de la app y cola de tareas
  • pgvector
    Almacenamiento de vectores
  • HNSW
    Índice de distancia coseno
  • ip2region
    Ubicación por IP de los accesos (sin conexión)
  • Nominatim
    Geocodificación inversa (desactivable)
INTERFAZ E INSTALACIÓN
  • React + TypeScript
    App web
  • Vite + Tailwind CSS
    Compilación y estilos
  • MapLibre GL
    Globo satelital
  • Docker Compose
    Imágenes amd64 / arm64
  • mDNS
    Dominio local wanmory.local
PRIVACY

Privacidad y seguridad

De forma predeterminada, las fotos y los resultados de la IA se quedan dentro de tu red privada. Si configuras un servicio de IA remoto, las vistas previas se envían a ese servidor para procesarlas.

Datos mínimos

El servicio de IA solo recibe vistas previas de 1440 píxeles, no guarda ningún archivo y nunca toca la base de datos, los nombres ni los originales.

Autenticación

El servicio de IA integrado solo existe en la red interna de Docker y únicamente el backend llega a él; cuando se define un token para un servicio de IA remoto, cada petición debe llevar un token Bearer.

Permisos

Los miembros solo pueden borrar las fotos que subieron y los administradores gestionan todo; las fotos ocultas solo las ve su dueño; el historial de accesos muestra una ubicación aproximada por IP.

Originales de solo lectura

Las carpetas del NAS se pueden montar en solo lectura con :ro, así que la app no puede modificar ni borrar sus archivos.

Funciona sin conexión

Tras descargar los modelos, el reconocimiento y la búsqueda se hacen en local; las funciones que necesitan internet, como la geocodificación inversa y las imágenes de satélite, se pueden desactivar.

Licencias de los modelos

Los modelos faciales de InsightFace son solo para uso no comercial; los modelos no van incluidos en la imagen y los ajustes muestran la licencia de cada uno antes de descargarlo.