481 lines
8.9 KiB
Markdown
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
|
|
```
|