face-recognition-rk3588-opencl/README.md

481 lines
8.9 KiB
Markdown

# Face Recognition RK3588 OpenCL
Motore C++ per rilevamento facciale, generazione di embedding e riconoscimento kNN accelerato su Rockchip RK3588.
Il progetto usa:
- NPU tramite RKNN per SCRFD e SFace;
- GPU Mali-G610 tramite OpenCL per kNN;
- CPU C++ come riferimento numerico e fallback.
## Funzionalità
- rilevamento volti con SCRFD;
- confidence, bounding box e cinque landmark;
- non-maximum suppression;
- allineamento volto 112 x 112;
- embedding SFace float32 a 128 dimensioni;
- normalizzazione L2;
- cosine similarity tramite prodotto scalare;
- kNN CPU e OpenCL;
- conteggi dinamici, TOPK configurabile e chunking;
- leave-one-out tramite self-map;
- JSON su stdout e diagnostica su stderr.
## Architettura
```text
Immagine
|
v
SCRFD tramite RKNN / NPU
|
v
Bounding box e landmark
|
v
Allineamento volto 112 x 112
|
v
SFace tramite RKNN / NPU
|
v
Embedding float32[128] normalizzato L2
|
+-----------------------------+
| |
v v
kNN CPU C++ kNN OpenCL / Mali-G610
| |
+-------------+---------------+
|
v
TOPK prototype
|
v
Leave-one-out opzionale
|
v
Soglia di riconoscimento
```
## Formato degli embedding
```text
Dimensione: 128
Tipo: float32
Dimensione binaria: 512 byte
Ordine byte: little-endian
Normalizzazione: L2
Norma attesa: circa 1.0
```
Poiché gli embedding sono normalizzati:
```text
cosine_similarity(a, b) = dot(a, b)
```
## Componenti
### Face engine RKNN
```text
include/face_config.h
include/face_engine.h
include/rknn_api.h
src/face_engine.cc
src/face_recognition.cc
src/face_scan.cc
```
### Motore kNN
```text
include/knn_opencl.h
src/knn_opencl.cc
src/knn_opencl_backend.cc
src/face_knn.cc
kernels/knn_float4_topk.cl
kernels/knn_reference.cl
```
Il kernel produttivo viene compilato a runtime, ad esempio con:
```text
-cl-fast-relaxed-math
-D DIM=128
-D TOPK=5
-D QUERY_COUNT=21
-D LANES_PER_QUERY=8
```
## Requisiti
- Linux AArch64;
- Rockchip RK3588 o piattaforma compatibile;
- compilatore C++17 e GNU Make;
- runtime RKNN;
- OpenCL ICD loader e driver Mali;
- modelli SCRFD e SFace in formato RKNN.
Su Debian:
```bash
sudo apt-get install \
build-essential \
opencl-headers \
ocl-icd-opencl-dev
```
Verifica OpenCL:
```bash
clinfo 2>/dev/null \
| grep -E 'Platform Name|Device Name|Device Type|Device Version'
```
## File locali richiesti
Non sono versionati:
```text
models/SCRFD_500M_KPS_640.rknn
models/face_recognition_sface_2021dec.rknn
lib/librknnrt.so
```
## Compilazione
Dalla root del repository:
```bash
make clean
make -j"$(nproc)"
```
Binari previsti:
```text
bin/face_recognition
bin/face_scan
bin/face_knn
```
Verifica del binario OpenCL:
```bash
file bin/face_knn
ldd bin/face_knn | grep -Ei 'opencl|stdc|not found'
```
## Utilizzo dei binari
### face_recognition
Programma dimostrativo con output destinato alla lettura umana.
Analisi di una singola fotografia:
```bash
./bin/face_recognition test/test.jpg
```
Confronto tra due fotografie:
```bash
./bin/face_recognition \
test/test.jpg \
test/test3f.jpg
```
Usarlo per verificare modelli e runtime RKNN, detection, landmark, embedding e similarità. Non è il binario consigliato per integrazioni applicative.
### face_scan
Elabora una fotografia e produce JSON con immagine, volti, confidence, bounding box, landmark ed embedding.
```bash
./bin/face_scan test/test3f.jpg
```
Separazione di risultato e diagnostica:
```bash
./bin/face_scan test/test3f.jpg \
> /tmp/face-scan.json \
2> /tmp/face-scan.stderr
```
Validazione:
```bash
python3 -m json.tool /tmp/face-scan.json > /dev/null \
&& echo "OK: JSON valido"
```
Exit code:
```text
0 scansione completata
2 argomenti non validi
3 inizializzazione FaceEngine fallita
4 caricamento o elaborazione immagine fallita
5 risultato facciale o embedding non valido
```
Usarlo per integrare il face engine con Node.js, Python o altri linguaggi e per costruire query destinate a `face_knn`.
### face_knn
Confronta embedding query con un database di prototype. Backend disponibili:
```text
cpu
opencl
```
Il programma va normalmente eseguito dalla root del repository, perché il kernel viene caricato da:
```text
kernels/knn_float4_topk.cl
```
Help:
```bash
./bin/face_knn --help
```
Backend CPU:
```bash
./bin/face_knn \
--queries queries.bin \
--database prototypes.bin \
--query-count 21 \
--database-count 5 \
--dimension 128 \
--top-k 5 \
--threshold 0.50 \
--backend cpu
```
Backend OpenCL:
```bash
./bin/face_knn \
--queries queries.bin \
--database prototypes.bin \
--query-count 21 \
--database-count 5 \
--dimension 128 \
--top-k 5 \
--threshold 0.50 \
--backend opencl
```
Argomenti:
```text
--queries file binario degli embedding query
--database file binario degli embedding prototype
--query-count numero di query
--database-count numero di prototype
--dimension componenti per embedding, 128 per SFace
--top-k numero massimo di vicini
--threshold soglia minima di riconoscimento
--self-map esclusioni leave-one-out opzionali
--backend cpu oppure opencl
```
Il TOPK effettivo è:
```text
effective_top_k = min(top_k, database_count)
```
#### Leave-one-out
Quando una query è anche un prototype, il confronto con sé stessa va escluso. La self-map contiene una riga per query:
```text
-1
1
3
0
4
-1
```
`-1` indica nessuna esclusione; un valore da zero in su indica l'indice del prototype da escludere.
```bash
./bin/face_knn \
--queries queries.bin \
--database prototypes.bin \
--query-count 21 \
--database-count 5 \
--dimension 128 \
--top-k 5 \
--threshold 0.50 \
--self-map self-map.txt \
--backend opencl
```
#### Output e diagnostica
```bash
./bin/face_knn \
--queries queries.bin \
--database prototypes.bin \
--query-count 21 \
--database-count 5 \
--dimension 128 \
--top-k 5 \
--threshold 0.50 \
--self-map self-map.txt \
--backend opencl \
> /tmp/face-knn.json \
2> /tmp/face-knn.stderr
```
```text
stdout → JSON valido
stderr → diagnostica OpenCL e driver Mali
```
Validazione:
```bash
python3 -m json.tool /tmp/face-knn.json > /dev/null \
&& echo "OK: JSON valido"
```
Campi principali del JSON:
```text
backend, device_name, query_count, database_count, dimension, top_k,
threshold, leave_one_out, elapsed_ms, results, recognized_count,
below_threshold_count, coverage_percent
```
Per ogni query:
```text
query_index, excluded_index, recognized, best_index, best_score, neighbors
```
### test_knn_cpu
Sorgente:
```text
test/knn/test_knn_cpu.cc
```
Esecuzione, se compilato:
```bash
./bin/test_knn_cpu
```
Output atteso:
```text
OK: backend CPU dinamico
query_count=2
database_count=3
top_k=2
```
## Scelta del backend
Usare CPU per database piccoli, riferimento numerico o fallback. Usare OpenCL per database grandi o molte query. Con cinque prototype l'overhead OpenCL può superare il costo del calcolo; con 350000 prototype e 256 query è stato misurato uno speedup di circa 7 volte.
## Flusso completo
```text
face_scan
→ estrae gli embedding
→ applicazione seleziona i prototype
→ face_knn calcola TOPK
→ applicazione associa gli indici alle identità
→ applicazione applica soglia e margine
→ applicazione salva il risultato
```
## Prestazioni sintetiche validate
```text
Database: 350000
Query: 256
Dimensione: 128
TOPK: 32
Chunk: 50000
GPU: Mali-G610 r0p0
CPU: 9663.226 ms
OpenCL: 1373.797 ms
Speedup: 7.034x
TOPK identico: 8192 / 8192
```
Variante float4 min-replace:
```text
Kernel: 1244.576 ms
Throughput: 71.992 M vector/s
Dot rate: 18.430 GFLOP/s
Traffico effettivo: 36.860 GB/s
```
## Validazione reale
```text
Query: 21
Prototype: 5
Dimensione: 128
Soglia: 0.50
Leave-one-out: attivo
Riconosciuti: 20 / 21
Copertura: 95.2%
Caso sotto soglia: IMG_0183.JPG
Score: 0.466428
Miglior prototype: IMG_0143.JPG
```
Confronti:
```text
Python vs C++ CPU: 21 / 21 query coerenti
CPU vs OpenCL dopo leave-one-out: 100 / 100 posizioni
CPU vs OpenCL TOPK grezzo: 105 / 105 posizioni
Errore massimo CPU/OpenCL: 3.58e-7
```
Dettagli:
```text
VALIDATION_JESSICA.md
README1.md
modules/knn-opencl/reference/VALIDATION.md
```
## Integrazione applicativa
Un'applicazione deve leggere e validare i prototype, generare i file binari e i metadati, invocare `face_knn`, associare gli indici alle identità, applicare soglia e margine e salvare il risultato. La prima integrazione dovrebbe funzionare in modalità shadow, confrontando il risultato corrente con OpenCL prima dell'attivazione definitiva.
## Stato
```text
Face engine RKNN: validato
Backend CPU: validato
Backend OpenCL: validato
Mali-G610: validata
TOPK dinamico: validato
Chunking: validato
Leave-one-out: validato
Output JSON puro: validato
Dataset reale: validato
Integrazione applicativa: da completare
```