3.9 KiB
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.