galleria_photo_face_rknn_op.../faces.md
2026-10-03 12:16:58 +02:00

154 lines
3.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Perfetto. Ora abbiamo il quadro completo: SQLite tramite better-sqlite3 + Knex.
Per il tuo caso farei una piccola estensione del DB esistente, senza modificare la struttura di photos.
Schema che proporrei
photos
│
└── faces
│
├── embedding BLOB ← 128 × float32
├── cluster_id
└── person_id
│
└── persons
e separatamente:
clusters
Quindi:
photos 1 ─── N faces N ─── 1 clusters
│
└──── N ─── 1 persons
Con una distinzione fondamentale:
cluster_id = risultato dell'algoritmo HDBSCAN
person_id = identità assegnata dall'utente
un person_id può avere più cluster_id
un nuovo volto riconosciuto può avere person_id ma cluster_id = NULL
faces
Per esempio:
faces
-----
id
photo_id
x
y
width
height
embedding
cluster_id
person_id
created_at
updated_at
embedding sarebbe un BLOB SQLite contenente:
128 × float32 = 512 byte
Questo è adatto al tuo motore OpenCL: leggiamo il BLOB e lo trasformiamo direttamente in float*.
clusters
clusters
--------
id
run_id
label
size
created_at
run_id è utile perché HDBSCAN potrebbe essere rieseguito più volte durante la fase iniziale.
persons
persons
-------
id
user
name
created_at
updated_at
Poi aggiungerei una struttura per i prototype embedding, invece di obbligarci ad avere un solo embedding rappresentativo per persona.
Ad esempio:
person 7
├── prototype 1 → volto frontale
├── prototype 2 → profilo
├── prototype 3 → occhiali
└── prototype 4 → barba
Questo diventa molto utile per il riconoscimento incrementale.
---
Una scelta importante: niente embedding dei cluster
Non salverei inizialmente un unico:
cluster.embedding
come rappresentazione obbligatoria.
Abbiamo già tutti i face embedding:
faces.embedding
e possiamo calcolare i rappresentanti quando servono.
Per esempio, dopo che l'utente associa un cluster a una persona, possiamo selezionare alcuni embedding rappresentativi del cluster e inserirli nei prototype della persona.
---
Quindi il flusso completo diventa
┌──────────────┐
│ photos │
└──────┬───────┘
│
face detector
│
▼
┌──────────────┐
│ faces │
│ bbox │
│ embedding │
└──────┬───────┘
│
┌──────────┴──────────┐
│ │
inizializzazione nuove foto
│ │
▼ ▼
HDBSCAN prototype search
│ │
▼ ▼
clusters person_id
│
UI utente
│
▼
persons
E soprattutto questa struttura è compatibile con il benchmark OpenCL che stiamo facendo: il DB rimane il persistente, mentre indices[N×32], distanze, MST ecc. rimangono dati di lavoro GPU/CPU e non devono necessariamente finire in SQLite.
Prima di scrivere init.js
Mi manca solo una verifica: hai già codice che estrae i volti / genera gli embedding RKNN?
Se sì, mandami il file che attualmente gestisce quella parte. Così possiamo progettare faces attorno al formato reale dell'output RKNN e non creare una seconda rappresentazione incompatibile.