Construction automatique d'un album de visages à partir de vidéos sur reComputer
Un projet d'archivage automatique d'album de visages basé sur Hailo-8 / SCRFD / ArcFace, exécuté sur l'appareil.

Vous pouvez consulter le code source de ce projet sur GitHub : https://github.com/doublelf/person_face_sign
1. Présentation du projet
Person Face Sign est un projet de construction hors-ligne d'un « album de visages » sur un appareil edge (reComputer + NPU Hailo-8). L'objectif principal est d'affecter automatiquement tous les visages présents dans une vidéo de surveillance ou d'activité à leur « album d'identité » respectif, afin de faciliter la recherche ultérieure, les statistiques ou l'archivage.
Problèmes typiques résolus
- Enregistrement des visiteurs / gestion de l'accueil : transmettez au script plusieurs heures d'enregistrement de la caméra d'accueil, et il génère automatiquement « 23 personnes sont venues aujourd'hui, avec N images chacune ».
- Aide au pointage en classe / réunion : les enregistrements de cours sont archivés par tête, servant de base à l'appel électronique.
- Organisation de photos d'événement : fusionnez et regroupez des prises multi-angle et multivue sur place afin de générer automatiquement des albums par personne.
- Recherche secondaire dans un fichier vidéo : chercher par personne, et non par horodatage.
Pipeline principal
Vidéo (data/*.mp4)
│
▼
[VideoSampler] —— 1 image par seconde (1280×720)
│
▼
[YOLOv8n] —— Détection de personnes (640×640)
│
▼
[IoUTracker] —— Suivi de personnes + échantillonnage de visage limité (désactivable)
│
▼
[SCRFD-10G] —— Détection de visage + 5 landmarks
│
▼
[FaceAlign 5pt] —— Alignement affine à 112×112
│
▼
[ArcFace MobileFaceNet] —— Embedding L2-norm 512-d
│
▼
[AlbumCluster] —— pHash prioritaire / Faiss HNSW en repli → attribution de person_id
│
▼
[AlbumStorage] —— Écriture des fichiers + double dédup + mise à jour de manifest.json
│
▼
[Web Browser :8080] —— Parcourir / rechercher le résultat2. Démarrage rapide
Voici un flux de bout en bout qui, exécuté dans l'ordre, permet de construire un album de visages consultable sur le reComputer.
2.1 Cloner le dépôt
git clone https://github.com/doublelf/person_face_sign.git
cd person_face_sign2.2 Créer l'environnement virtuel
L'utilisation de venv est recommandée (inclus dans Python, aucune installation supplémentaire requise).
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip2.3 Installer les dépendances Python
pip install -r requirements.txt2.4 Installer les dépendances système préalables
build_album.py invoque ffmpeg / ffprobe via subprocess pour l'échantillonnage d'images ; ces outils doivent être installés au niveau du système :
# Debian / Ubuntu (y compris reComputer)
sudo apt update
sudo apt install -y ffmpeg
# Vérifier
ffmpeg -version | head -n 1
ffprobe -version | head -n 12.5 Préparer les fichiers de modèle HEF
person_face_sign/
└── models/
├── yolov8n.hef # ~4.3 MB
├── scrfd_10g.hef # ~10 MB
└── arcface_mobilefacenet.hef # ~5 MB| Usage | Modèle | Chemin par défaut (codé en dur dans build_album.py) | Source réelle |
|---|---|---|---|
| Détection de personnes | YOLOv8n | <racine du projet>/models/yolov8n.hef | Intégré dans l'image Docker officielle, chemin dans le conteneur /app/model/yolov8n.hef |
| Détection de visages | SCRFD-10G | <racine du projet>/models/scrfd_10g.hef | Inclus dans le paquet .deb de HailoRT (peut être copié dans models/) |
| Embedding de visages | ArcFace MobileFaceNet | <racine du projet>/models/arcface_mobilefacenet.hef | Inclus dans le paquet .deb de HailoRT (peut être copié dans models/) |
2.5.1 Obtenir SCRFD + ArcFace (le plus simple)
Les HEF de SCRFD-10G et ArcFace MobileFaceNet sont des modèles précompilés officiellement par Hailo. Ils sont installés par le paquet hailort dans /usr/local/hailo/resources/models/hailo8/ — il suffit de les copier dans le répertoire models/ de ce projet :
sudo apt install hailort
ls /usr/local/hailo/resources/models/hailo8/
# Vous devriez voir :
# arcface_mobilefacenet.hef
# scrfd_10g.hef
# ainsi que d'autres modèles d'exemple officiels
# Copier vers le répertoire models/ local du projet
cp /usr/local/hailo/resources/models/hailo8/scrfd_10g.hef models/
cp /usr/local/hailo/resources/models/hailo8/arcface_mobilefacenet.hef models/
ls -lh models/2.5.2 Obtenir YOLOv8n
Voir https://sensecraft.seeed.cc/ai-lab/en/models/yolov8/hailo-8. Le HEF YOLOv8n est déjà empaqueté dans ghcr.io/seeed-projects/recomputer-r20-cv/yolov8:latest. Le chemin dans le conteneur est /app/model/yolov8n.hef.
mkdir -p models
# 1. Récupérer l'image officielle
docker pull ghcr.io/seeed-projects/recomputer-r20-cv/yolov8:latest
# 2. Créer une instance de conteneur non exécutée via `create` (uniquement pour obtenir le fichier)
docker create --name yolov8-temp ghcr.io/seeed-projects/recomputer-r20-cv/yolov8:latest
# 3. Copier yolov8n.hef depuis le conteneur vers le répertoire models/ du projet
docker cp yolov8-temp:/app/model/yolov8n.hef models/yolov8n.hef
# 4. Nettoyer le conteneur temporaire
docker rm yolov8-temp
# 5. Vérifier
ls -lh models/yolov8n.hef2.6 Construire l'album de visages (étape principale)
Le dépôt inclut une vidéo de démonstration data/tex.mp4 (environ 11 Mo). Exécutez la commande suivante pour générer l'album :
python apps/build_album.py \
--video data/tex.mp4 \
--out data/album \
--no-tracker \
--scrfd-score 0.6 \
--cluster-thresh 0.85 \
--skip-dedupParamètres :
| Paramètre | Valeur | Description |
|---|---|---|
--video | data/tex.mp4 | Vidéo d'entrée ; peut être spécifié plusieurs fois ou séparé par des virgules |
--out | data/album | Répertoire de sortie de l'album |
--no-tracker | flag | Désactive l'IoU tracker ; prend chaque visage sur chaque image (recommandé pour les vidéos courtes) |
--scrfd-score | 0.6 | Seuil de confiance de détection de visage SCRFD (par défaut 0,60) |
--cluster-thresh | 0.85 | Regroupement souple : seuil de distance cosinus ; voir la section 4.2 pour plus de détails |
--skip-dedup | flag | Saute le dédoublonnage pHash / embedding à l'écriture (le regroupement privilégiant pHash reste actif) |
Une fois la construction terminée, sous data/album/ apparaît :
data/album/
├── manifest.json
├── thumbnails/
│ ├── person_0001.jpg
│ ├── person_0002.jpg
│ └── ...
├── person_0001/
│ ├── representative.jpg
│ ├── info.json
│ ├── embeddings.npz
│ └── img_0001_ts_2.00s_score_0.78.jpg
│ └── img_0002_ts_5.00s_score_0.74.jpg
│ └── ...
├── person_0002/
└── ...2.7 Démarrer le service web de navigation de l'album
Après la construction, ouvrez un autre terminal et lancez le service web minimaliste :
python apps/web_browser.py --port 8080Accédez dans le navigateur à http://<reComputer-IP>:8080/ :
- La barre de statistiques en haut affiche le nombre total de personnes, d'images et de vidéos sources ;
- La grille principale affiche, selon « 1 image = 1 personne », les miniatures (provenant de
representative.jpg, triées par nombre d'images décroissant) ; - Cliquez sur une personne pour ouvrir la page de détail listant tous ses
img_*.jpg; - Cliquez sur une image pour ouvrir le visualiseur plein écran.
3. Lecture des résultats
3.1 Structure du répertoire de données
data/album/
├── manifest.json # Statistiques globales + liste des personnes (source de données de l'UI Web)
├── thumbnails/
│ └── person_<id>.jpg # Miniature (copie de representative.jpg)
└── person_<NNNN>/ # Un répertoire par identité ; NNNN est un identifiant à 4 chiffres avec zéros à gauche
├── info.json # Métadonnées par personne : count / first_ts / last_ts / best_score / source_videos
├── embeddings.npz # Top-N embeddings de cette personne (par défaut N=5, triés par score)
├── representative.jpg # « Photo de couverture » au meilleur score, également copiée dans thumbnails/
└── img_<NNNN>_ts_<ts>s_score_<score>.jpg # Chaque visage conservé (après alignement 112×112)Exemples de convention de nommage :
person_0001/img_0001_ts_2.00s_score_0.78.jpg: 1ʳᵉ image, 2,00 secondes, score SCRFD = 0,78person_0001/img_0014_ts_18.50s_score_0.82.jpg: 14ᵉ image, 18,50 secondes
3.2 Champs clés de manifest.json
{
"version": "1.0",
"created_at": "2026-09-26T10:23:11",
"updated_at": "2026-09-26T10:24:48",
"total_persons": 6,
"total_images": 142,
"source_videos": ["tex"],
"config": {
"interval_s": 1.0,
"cosine_distance_threshold": 0.85,
"min_face_size": [40, 40],
"min_face_score": 0.6,
"best_n_embeddings": 5,
"phash_max_dist": 4
},
"persons": [
{
"id": 1,
"count": 28,
"first_ts": 2.0,
"last_ts": 82.0,
"best_score": 0.85,
"source_videos": ["tex"],
"thumbnail": "thumbnails/person_0001.jpg"
},
...
]
}Signification des champs :
| Champ | Signification |
|---|---|
total_persons | Nombre total de personnes regroupées (croît de façon monotone avec les builds incrémentiels) |
total_images | Nombre total d'images de visages (après déduplication) |
config | Instantané des paramètres de ce build (réécrit dans l'album pour la traçabilité) |
persons[].count | Nombre d'images de visage pour cette personne |
persons[].first_ts / last_ts | Horodatage de la première / dernière apparition (secondes) |
persons[].best_score | Meilleur score de détection SCRFD |
3.3 Interactions de l'UI Web
Le front-end s'appuie uniquement sur http.server + HTML/CSS/JS statique, sans base de données ; toutes les données sont lues en temps réel depuis manifest.json et le système de fichiers.
Principaux flux d'interaction :
- Page d'accueil (
/) : récupère/manifest.json, liste les miniatures de toutes les personnes parcountdécroissant ; - Détail d'une personne : cliquer sur une miniature → le backend fait un glob de
person_<id>/img_*.jpg→ liste toutes les images ; - Aperçu d'image : cliquer sur une image → modale plein écran
<dialog>; - Rafraîchir : cliquer sur
↻en haut pour rechargermanifest.json.