Construcción automática del álbum de rostros a partir de vídeo en reComputer
Un proyecto de archivado automático de álbumes de rostros basado en Hailo-8 / SCRFD / ArcFace en el dispositivo.

Puedes consultar el código fuente de este proyecto en GitHub: https://github.com/doublelf/person_face_sign
1. Descripción del proyecto
Person Face Sign es un proyecto para construir sin conexión un "álbum de rostros" en un dispositivo edge (reComputer + NPU Hailo-8). El objetivo principal es asignar automáticamente todos los rostros que aparecen en un vídeo de vigilancia o de actividad a sus respectivos "álbumes de identidad", para facilitar su consulta, contabilización o archivo posterior.
Problemas típicos que resuelve
- Registro de visitas / gestión de visitantes: entrega al script varias horas de grabación de la cámara de recepción y genera automáticamente "hoy vinieron 23 personas, cada una con N imágenes".
- Apoyo a la asistencia a clase / reuniones: las grabaciones de clase se archivan por persona, como base para el pase de lista electrónico.
- Organización automática de fotos de eventos: fusiona y agrupa grabaciones multicámara y multiángulo del lugar para generar automáticamente álbumes agrupados por persona.
- Búsqueda secundaria en material de vídeo: buscar por persona, no por marca de tiempo.
Pipeline principal
Vídeo (data/*.mp4)
│
▼
[VideoSampler] —— 1 fotograma por segundo (1280×720)
│
▼
[YOLOv8n] —— Detección de personas (640×640)
│
▼
[IoUTracker] —— Seguimiento de personas + muestreo de rostros limitado (desactivable)
│
▼
[SCRFD-10G] —— Detección de rostros + 5 landmarks
│
▼
[FaceAlign 5pt] —— Alineación afín a 112×112
│
▼
[ArcFace MobileFaceNet] —— Embedding L2-norm de 512-d
│
▼
[AlbumCluster] —— pHash preferente / Faiss HNSW como respaldo → asignación de person_id
│
▼
[AlbumStorage] —— Escritura de archivos + doble dedup + actualización de manifest.json
│
▼
[Web Browser :8080] —— Navegar / buscar el resultado2. Inicio rápido
A continuación se muestra un flujo de extremo a extremo que, ejecutado en orden, permite construir un álbum de rostros navegable en el reComputer.
2.1 Clonar el repositorio
git clone https://github.com/doublelf/person_face_sign.git
cd person_face_sign2.2 Crear el entorno virtual
Se recomienda usar venv (incluido en Python, sin instalación adicional).
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip2.3 Instalar dependencias de Python
pip install -r requirements.txt2.4 Instalar las dependencias previas a nivel de sistema
build_album.py invoca ffmpeg / ffprobe a través de subprocess para el muestreo de fotogramas, por lo que deben instalarse a nivel de sistema:
# Debian / Ubuntu (incluido reComputer)
sudo apt update
sudo apt install -y ffmpeg
# Verificar
ffmpeg -version | head -n 1
ffprobe -version | head -n 12.5 Preparar los archivos del modelo HEF
person_face_sign/
└── models/
├── yolov8n.hef # ~4.3 MB
├── scrfd_10g.hef # ~10 MB
└── arcface_mobilefacenet.hef # ~5 MB| Uso | Modelo | Ruta predeterminada (hard-coded en build_album.py) | Origen real |
|---|---|---|---|
| Detección de personas | YOLOv8n | <raíz del proyecto>/models/yolov8n.hef | Integrado en la imagen Docker oficial, ruta en contenedor /app/model/yolov8n.hef |
| Detección de rostros | SCRFD-10G | <raíz del proyecto>/models/scrfd_10g.hef | Incluido en el paquete .deb de HailoRT (puede copiarse a models/) |
| Embedding de rostros | ArcFace MobileFaceNet | <raíz del proyecto>/models/arcface_mobilefacenet.hef | Incluido en el paquete .deb de HailoRT (puede copiarse a models/) |
2.5.1 Obtener SCRFD + ArcFace (lo más sencillo)
Los HEF de SCRFD-10G y ArcFace MobileFaceNet son modelos precompilados oficialmente por Hailo. Se instalan en /usr/local/hailo/resources/models/hailo8/ con el paquete hailort — solo cópialos al directorio models/ de este proyecto:
sudo apt install hailort
ls /usr/local/hailo/resources/models/hailo8/
# Deberías ver:
# arcface_mobilefacenet.hef
# scrfd_10g.hef
# además de otros modelos de ejemplo oficiales
# Copiar al directorio models/ local del proyecto
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 Obtener YOLOv8n
Consulta https://sensecraft.seeed.cc/ai-lab/en/models/yolov8/hailo-8. El HEF de YOLOv8n ya está empaquetado en ghcr.io/seeed-projects/recomputer-r20-cv/yolov8:latest. La ruta dentro del contenedor es /app/model/yolov8n.hef.
mkdir -p models
# 1. Descargar la imagen oficial
docker pull ghcr.io/seeed-projects/recomputer-r20-cv/yolov8:latest
# 2. Crear una instancia de contenedor que no se ejecuta con `create` (solo para obtener el archivo)
docker create --name yolov8-temp ghcr.io/seeed-projects/recomputer-r20-cv/yolov8:latest
# 3. Copiar yolov8n.hef desde el contenedor al directorio models/ del proyecto
docker cp yolov8-temp:/app/model/yolov8n.hef models/yolov8n.hef
# 4. Limpiar el contenedor temporal
docker rm yolov8-temp
# 5. Verificar
ls -lh models/yolov8n.hef2.6 Construir el álbum de rostros (paso principal)
El repositorio incluye un vídeo de demostración data/tex.mp4 (unos 11 MB). Ejecuta el siguiente comando para generar el álbum:
python apps/build_album.py \
--video data/tex.mp4 \
--out data/album \
--no-tracker \
--scrfd-score 0.6 \
--cluster-thresh 0.85 \
--skip-dedupParámetros:
| Parámetro | Valor | Descripción |
|---|---|---|
--video | data/tex.mp4 | Vídeo de entrada; puede especificarse varias veces o separarse por comas |
--out | data/album | Directorio de salida del álbum |
--no-tracker | flag | Desactiva el IoU tracker; toma cada rostro en cada fotograma (recomendado para vídeos cortos) |
--scrfd-score | 0.6 | Umbral de confianza de detección de rostros SCRFD (predeterminado 0.60) |
--cluster-thresh | 0.85 | Agrupamiento laxo: umbral de distancia coseno; ver sección 4.2 para más detalles |
--skip-dedup | flag | Salta el doble dedup pHash/embedding al escribir (mantiene el clustering con prioridad pHash) |
Tras la compilación, en data/album/ aparecerá:
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 Iniciar el servicio web de navegación del álbum
Tras la compilación, abre otra terminal e inicia el servicio web minimalista:
python apps/web_browser.py --port 8080Accede en el navegador a http://<reComputer-IP>:8080/:
- La barra superior de estadísticas muestra el número total de personas, de imágenes y de vídeos de origen;
- La cuadrícula principal muestra, según "1 imagen = 1 persona", las miniaturas (procedentes de
representative.jpg, ordenadas de forma descendente por número de imágenes); - Haz clic en una persona para acceder a la página de detalle con todas sus
img_*.jpg; - Haz clic en cualquier imagen para abrir el visor a pantalla completa.
3. Interpretación del resultado
3.1 Estructura del directorio de datos
data/album/
├── manifest.json # Estadísticas globales + lista de personas (fuente de datos de la Web UI)
├── thumbnails/
│ └── person_<id>.jpg # Miniatura (copia de representative.jpg)
└── person_<NNNN>/ # Un directorio por identidad; NNNN es un ID con relleno de ceros de 4 dígitos
├── info.json # Metadatos por persona: count / first_ts / last_ts / best_score / source_videos
├── embeddings.npz # Top-N embeddings de esta persona (predeterminado N=5, ordenados por score)
├── representative.jpg # "Foto de portada" con mejor puntuación, también escrita en thumbnails/
└── img_<NNNN>_ts_<ts>s_score_<score>.jpg # Cada rostro retenido (tras alineación 112×112)Ejemplos de convención de nombres:
person_0001/img_0001_ts_2.00s_score_0.78.jpg: 1.ª imagen, 2.00 segundos, SCRFD score = 0.78person_0001/img_0014_ts_18.50s_score_0.82.jpg: 14.ª imagen, 18.50 segundos
3.2 Campos clave 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"
},
...
]
}Significado de los campos:
| Campo | Significado |
|---|---|
total_persons | Número total de personas agrupadas (crece monotónicamente con builds incrementales) |
total_images | Número total de imágenes de rostros (tras deduplicación) |
config | Snapshot de los parámetros de este build (se reescribe en el álbum para trazabilidad) |
persons[].count | Número de imágenes de rostro de esta persona |
persons[].first_ts / last_ts | Marca de tiempo de la primera / última aparición (segundos) |
persons[].best_score | Mayor puntuación de detección SCRFD |
3.3 Interacciones de la UI web
El frontend usa solo http.server + HTML/CSS/JS estático, sin base de datos; todos los datos se leen en tiempo real desde manifest.json y el sistema de archivos.
Flujos principales de interacción:
- Página de inicio (
/): carga/manifest.jsony lista las miniaturas de todas las personas en orden descendente porcount; - Detalle de persona: haz clic en una miniatura → el backend hace glob de
person_<id>/img_*.jpg→ lista todas las imágenes; - Vista previa de imagen: haz clic en una imagen →
<dialog>modal a pantalla completa; - Actualizar: haz clic en
↻arriba para volver a cargarmanifest.json.