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

245 lines
4.5 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. Ho guardato direttamente il repository e adesso possiamo progettare l'integrazione sul codice reale, non su ipotesi.
Il repository conferma che la pipeline produce esattamente ciò che ci serve: SCRFD → landmark → alignment 112×112 → SFace → 128 float L2-normalizzati. La Face attuale contiene già score, bbox, 5 landmark e std::vector<float> embedding.
La cosa più importante
Non cambierei il modello RKNN.
Il formato ideale per il DB è già quello che abbiamo:
float embedding[128]
Il codice attuale fa anche già la L2 normalization prima di restituire l'embedding.
Quindi il flusso diventa:
photos.id
│
▼
SCRFD
│
├── bbox
├── confidence
└── 5 landmarks
│
▼
alignment
│
▼
SFace
│
▼
float[128]
│
▼
faces
│
├── HDBSCAN
│
└── recognition
E farei una piccola modifica all'architettura del repository
Attualmente face_recognition.cc è un programma di test: prende una/due immagini e stampa i risultati. Il main() stesso conferma che l'attuale uso è principalmente il confronto fra immagini.
Non lo trasformerei direttamente nel processo DB.
Creerei invece qualcosa del genere:
face-rknn/
├── src/
│ ├── face_recognition.cc ← lasciamo il test attuale
│ ├── face_engine.cc
│ ├── face_engine.h
│ └── face_scan.cc ← nuovo scanner DB
dove FaceEngine espone qualcosa tipo:
struct FaceResult {
float score;
float x1, y1, x2, y2;
Point kps[5];
std::array<float, 128> embedding;
};
class FaceEngine {
public:
bool init(
const char* scrfd_model,
const char* sface_model
);
std::vector<FaceResult> process(
const Image& image
);
};
Così il codice RKNN resta riutilizzabile.
---
DB: ora possiamo definirlo con precisione
Dato che sei su SQLite/better-sqlite3, userei:
faces.embedding → BLOB
con esattamente:
128 * sizeof(float)
= 512 byte
per volto.
E non salverei FP16.
Il modello restituisce FP16, ma il codice attuale li converte già in float e poi normalizza.
Quindi salviamo il risultato finale:
float32[128]
che è esattamente quello che poi il tuo OpenCL può consumare.
Per 350.000 volti:
350000 × 512
≈ 179.2 MB
assolutamente gestibile.
---
Una correzione rispetto a quello che ti avevo detto prima
Avendo visto il codice vero, non introdurrei subito prototypes nel DB.
Prima farei funzionare questa pipeline:
photos
↓
faces
↓
OpenCL kNN
↓
HDBSCAN
↓
clusters
↓
utente assegna persona
↓
persons
Poi, quando implementiamo il riconoscimento incrementale, decidiamo se i prototype debbano essere:
una tabella persistente;
un subset di faces;
oppure un rappresentante/calcolo derivato.
Non voglio aggiungere complessità prima che serva.
---
E c'è un dettaglio molto utile
Il repository usa già:
cosine(a, b)
su embedding L2-normalizzati.
Quindi il tuo benchmark OpenCL che stiamo facendo è perfettamente allineato:
cosine(a,b)
=
dot(a,b)
per vettori normalizzati.
Questa è una buona notizia: non dobbiamo cambiare metrica.
Il tuo OpenCL può quindi ricevere direttamente i BLOB float32[128] e fare il dot product.
---
Quindi io procederei così
Passo 1 — DB
Aggiungiamo a init.js:
faces
face_clusters
persons
senza toccare photos.
Passo 2 — FaceEngine
Separiamo detector + SFace dal main() di test.
Passo 3 — scanner
Prende:
photos
e per ogni foto non ancora processata:
image
↓
faces
↓
embedding
↓
INSERT faces
Passo 4 — GPU
Solo quando abbiamo gli embedding nel DB:
SQLite
↓
float32 array
↓
OpenCL
↓
kNN TOPK=32
Passo 5 — HDBSCAN
Usiamo quel kNN per costruire:
core distance
mutual reachability
MST
condensed tree
cluster
Passo 6 — UI
L'utente vede i cluster e assegna:
cluster 123 → "Mario"
che significa:
faces.cluster_id = 123
faces.person_id = Mario
Poi il sistema incrementale può riconoscere direttamente Mario senza rieseguire HDBSCAN globale.
---
A questo punto non ti chiederei altro codice alla cieca: abbiamo già DB + backend + face engine + formato embedding.
Il prossimo passo concreto è che , compatibile con il tuo better-sqlite3, e subito dopo invece di fare soltanto il test da riga di comando. ti preparo **la modifica di `db/init.js` per le tre tabelle**adattiamo `face-rknn` per produrre i record `faces`