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.