Automatic Face Album Construction from Video on the reComputer
An on-device project for automatically archiving face albums based on Hailo-8 / SCRFD / ArcFace.

The project source is available on GitHub: https://github.com/doublelf/person_face_sign
1. Project Overview
Person Face Sign is a project that builds a "face album" entirely offline on an edge device (reComputer + Hailo-8 NPU). Its core goal is to automatically group every face that appears in a piece of surveillance or activity video into individual "identity albums", so they can be browsed, counted, or archived afterwards.
Typical problems it solves
- Visitor registration / visitor management: hand a few hours of front-desk camera footage to the script and get a result like "23 people visited today, N images per person".
- Classroom / meeting attendance assistance: archive course recordings by person as the basis for electronic roll-call.
- Automatic organization of event photos: merge and cluster multi-camera, multi-angle event footage and automatically produce an album grouped by person.
- Secondary retrieval of video assets: find people by face instead of by timestamp.
Core pipeline
Video (data/*.mp4)
│
▼
[VideoSampler] —— 1 frame every 1 second (1280×720)
│
▼
[YOLOv8n] —— Pedestrian detection (640×640)
│
▼
[IoUTracker] —— Pedestrian tracking + throttled face sampling (optional)
│
▼
[SCRFD-10G] —— Face detection + 5-point landmarks
│
▼
[FaceAlign 5pt] —— Affine alignment to 112×112
│
▼
[ArcFace MobileFaceNet] —— 512-d L2-normalized embedding
│
▼
[AlbumCluster] —— pHash-first / Faiss HNSW fallback → assign person_id
│
▼
[AlbumStorage] —— Write files + dual deduplication + update manifest.json
│
▼
[Web Browser :8080] —— Browse / search the output2. Quick Start
The following is an end-to-end reproduction procedure. Execute the steps in order to build a browsable face album on the reComputer.
2.1 Clone the repository
git clone https://github.com/doublelf/person_face_sign.git
cd person_face_sign2.2 Create a virtual environment
venv is recommended (built into Python, no extra install required).
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip2.3 Install Python dependencies
pip install -r requirements.txt2.4 Install system-level prerequisites
build_album.py calls ffmpeg / ffprobe via subprocess for frame-skipping sampling; these must be installed at the system level:
# Debian / Ubuntu (including the reComputer)
sudo apt update
sudo apt install -y ffmpeg
# Verify
ffmpeg -version | head -n 1
ffprobe -version | head -n 12.5 Prepare the HEF model files
person_face_sign/
└── models/
├── yolov8n.hef # ~4.3 MB
├── scrfd_10g.hef # ~10 MB
└── arcface_mobilefacenet.hef # ~5 MB| Purpose | Model | Default path (hardcoded in build_album.py) | Actual source |
|---|---|---|---|
| Pedestrian detection | YOLOv8n | <project_root>/models/yolov8n.hef | Bundled in the official Docker image, container path /app/model/yolov8n.hef |
| Face detection | SCRFD-10G | <project_root>/models/scrfd_10g.hef | Bundled with the HailoRT deb package (can be copied to models/) |
| Face embedding | ArcFace MobileFaceNet | <project_root>/models/arcface_mobilefacenet.hef | Bundled with the HailoRT deb package (can be copied to models/) |
2.5.1 Get SCRFD + ArcFace (easiest)
The SCRFD-10G and ArcFace MobileFaceNet HEFs are official pre-compiled Hailo models, installed to /usr/local/hailo/resources/models/hailo8/ by the hailort deb package. Just copy them into this project's models/ directory:
sudo apt install hailort
ls /usr/local/hailo/resources/models/hailo8/
# You should see:
# arcface_mobilefacenet.hef
# scrfd_10g.hef
# plus several other official sample models
# Copy them to the project's local models/ directory
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 Get YOLOv8n
See https://sensecraft.seeed.cc/ai-lab/en/models/yolov8/hailo-8. The YOLOv8n HEF is bundled inside ghcr.io/seeed-projects/recomputer-r20-cv/yolov8:latest; the container path is /app/model/yolov8n.hef.
mkdir -p models
# 1. Pull the official image
docker pull ghcr.io/seeed-projects/recomputer-r20-cv/yolov8:latest
# 2. Create (but do not start) a temporary container instance just to grab the file
docker create --name yolov8-temp ghcr.io/seeed-projects/recomputer-r20-cv/yolov8:latest
# 3. Copy yolov8n.hef from the container into the project's models/ directory
docker cp yolov8-temp:/app/model/yolov8n.hef models/yolov8n.hef
# 4. Clean up the temporary container
docker rm yolov8-temp
# 5. Verify
ls -lh models/yolov8n.hef2.6 Build the face album (core step)
The repository ships a demo video data/tex.mp4 (about 11 MB). Run the command below to generate the album:
python apps/build_album.py \
--video data/tex.mp4 \
--out data/album \
--no-tracker \
--scrfd-score 0.6 \
--cluster-thresh 0.85 \
--skip-dedupParameter reference:
| Parameter | Value | Description |
|---|---|---|
--video | data/tex.mp4 | Input video; can be specified multiple times or comma-separated |
--out | data/album | Album output directory |
--no-tracker | flag | Disable the IoU tracker; sample every face in every frame (recommended for short sample videos) |
--scrfd-score | 0.6 | SCRFD face detection confidence threshold (default 0.60) |
--cluster-thresh | 0.85 | Loose clustering: cosine distance threshold; see section 4.2 |
--skip-dedup | flag | Skip the pHash / embedding dual deduplication on write (pHash-first clustering is still retained) |
After the build completes, the following layout appears under data/album/:
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 Start the web album browser
After the build finishes, open another terminal and start the minimal web service:
python apps/web_browser.py --port 8080Open http://<reComputer-IP>:8080/ in a browser:
- The top stats bar shows total persons, total images, and source video count.
- The main grid displays thumbnails, one per person (the thumbnail is the
representative.jpg, sorted by image count descending). - Click a person to enter the detail page, which shows all
img_*.jpgof that person. - Click any image to open a fullscreen viewer.
3. Reading the Output
3.1 Data directory structure
data/album/
├── manifest.json # Global stats + person list (data source for the Web UI)
├── thumbnails/
│ └── person_<id>.jpg # Thumbnail (a copy of representative.jpg)
└── person_<NNNN>/ # One directory per identity; NNNN is a 4-digit zero-padded id
├── info.json # Per-person metadata: count / first_ts / last_ts / best_score / source_videos
├── embeddings.npz # Top-N embeddings for this person (default N=5, sorted by score)
├── representative.jpg # Highest-scoring cover image, also written to thumbnails/
└── img_<NNNN>_ts_<ts>s_score_<score>.jpg # Each retained face (after 112×112 alignment)File naming convention examples:
person_0001/img_0001_ts_2.00s_score_0.78.jpg: 1st image, 2.00 s, SCRFD score = 0.78person_0001/img_0014_ts_18.50s_score_0.82.jpg: 14th image, 18.50 s
3.2 Key fields in 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"
},
...
]
}Field descriptions:
| Field | Description |
|---|---|
total_persons | Total number of clustered persons (monotonically increasing on continued builds) |
total_images | Total number of face images (after deduplication) |
config | Snapshot of the build parameters (written back to the album for traceability) |
persons[].count | Number of face images for this person |
persons[].first_ts / last_ts | First / last appearance timestamp (seconds) |
persons[].best_score | Highest SCRFD detection score |
3.3 Web UI interaction
The web side is built on plain http.server + static HTML/CSS/JS with no database dependency; all data is read live from manifest.json and the file system.
Main interaction paths:
- Home page (
/): fetches/manifest.jsonand lists thumbnails for all persons sorted bycountdescending; - Person detail: click a thumbnail → the backend globs
person_<id>/img_*.jpg→ all images are listed; - Full-size preview: click an image → fullscreen
<dialog>popup; - Refresh: click the top
↻to refetchmanifest.json.