· Nolwen Brosson · Blog · 7 min read
Reconnaissance Faciale: Construire un système complet simple et léger
Introduction
La reconnaissance faciale fait partie du vaste domaine de la vision par ordinateur. Grâce aux progrès spectaculaires du Deep Learning ces dix dernières années, ce domaine, et toutes ses applications pratiques, a atteint un niveau de maturité impressionnant.
Mais la bonne nouvelle, c’est que comprendre comment fonctionne réellement un système de reconnaissance faciale, et même en construire un soi-même, n’a jamais été aussi accessible.
Dans cette article, je vous présente un petit projet que nous avons construit, étape par étape, afin que vous puississiez visualiser très concrètement la mécanique derrière ces algorithmes. L’objectif n’est pas d’aller dans la complexité, mais de fournir une explication claire, illustrée, et accompagnée de code simple à comprendre.

📌 Le code complet (assez vieux) du projet est disponible ici: https://github.com/nbrosson/tt-face-recognition
(J’ai essayé de mettre du code un peu plus propre dans l’article, car celui-ci date de plusieurs années…)
C’est quoi, exactement, la reconnaissance faciale ?
La reconnaissance faciale est la capacité d’un système informatique à identifier ou vérifier une personne à partir d’une image ou d’une vidéo de son visage.
Il existe deux usages principaux :
- Identification (one-to-many) : déterminer qui est la personne parmi un ensemble d’identités connues. C’est ce que nous allons utiliser dans ce projet.
- Vérification (one-to-one) : vérifier qu’une personne est bien celle qu’elle prétend être.
TL;DR en version simple
Supposons que vous vouliez repérer si Julia, Thomas ou Rémy apparaissent dans une photo.
Notre modèle doit :
- Analyser plusieurs images de Julia, Thomas et Rémy.
- Extraire uniquement leur visage.
- Transformer chaque visage en un vecteur numérique.
- Faire la même chose sur la photo que vous envoyez à l’API.
- Mesurer la similarité entre les vecteurs.
- Identifier lequel du trio ressemble le plus à la personne sur l’image.
Et si la personne n’est dans aucune catégorie, le modèle retournera la personne la plus ressemblante.
Voici le résumé des étapes :
- On applique une détection de visage sur toutes les images du groupe de référence (Julia, Thomas, Rémy).
- On stocke ces résultats localement.
- On applique la même détection sur l’image envoyée par l’utilisateur.
- On transforme chaque visage en vecteur 1D via un modèle pré-entraîné (VGGFace).
- On compare les vecteurs via la distance euclidienne.
- Celui avec la plus petite distance est considéré comme le plus ressemblant.
Le projet
Voici la structure du dossier :
├── data/
│ ├── images/
│ │ ├── images_array/ # Résultats du face detection
│ │ ├── raw_images/ # Images brutes par personne
│ │ | ├── player_1/
│ │ | ├── player_2/
│ │ | ├── .../
├── face_detector/
│ ├── *.py
├── templates/
│ ├── *.html
Nous voulons :
➡️ Construire un algorithme complet de reconnaissance faciale
➡️ Accessible via une simple application Flask
Le projet se découpe en 4 étapes :
1. Définir le groupe de référence
Images brutes dans data/images/raw_images/<person_name>.
2. Appliquer la détection de visage (benchmark)
C’est la partie coûteuse.
Nous la faisons hors ligne, une fois pour toutes.
On appelle ça le pipeline d’entraînement.
3. Appliquer l’embedding
Transformer les visages (images) en vecteurs 1-dimension via VGGFace / ResNet50.
Cette étape est légère, on peut la faire à chaque requête.
4. Faire la prédiction
Comparer les vecteurs → identifier la personne la plus ressemblante.
Le pipeline complet (détection + embedding + comparaison) constitue le pipeline de prédiction.
Ci-dessous un petit schéma récapitulatif

Plongée dans le code
Dépendances
Python ≥ 3.6.
Si OpenCV vous pose problème, utilisez opencv-python-headless.
opencv-python==4.2.0.34
Flask==1.1.1
mtcnn==0.1.0
Pillow==7.2.0
keras-vggface==0.6
tensorflow==2.2Face Detection
import os
import cv2
import numpy as np
from mtcnn import MTCNN
from PIL import Image
from keras_vggface.vggface import VGGFace
def extract_face(img, required_size=(224, 224)):
"""
Extrait le visage principal d'une image lors du pré-traitement du benchmark
:param img: image sous forme numpy array (BGR, comme renvoyé par cv2)
:return: tableau numpy (1, 224, 224, 3) ou None si aucun visage n'est trouvé
"""
detector = MTCNN()
results = detector.detect_faces(img)
if len(results) == 0:
return None # Aucun visage détecté
x1, y1, w, h = results[0]['box']
# Sécuriser les bornes
x1 = max(0, x1)
y1 = max(0, y1)
x2 = x1 + max(0, w)
y2 = y1 + max(0, h)
face = img[y1:y2, x1:x2]
# Redimensionnement
face_img = Image.fromarray(face)
face_img = face_img.resize(required_size)
face_array = np.asarray(face_img)
return face_array.reshape(1, required_size[0], required_size[1], 3)
def load_image_from_bytes(file_bytes):
"""Convertit des bytes en image numpy utilisable par OpenCV
quand tu reçois une image via une API (upload), sous forme de bytes
"""
nparr = np.frombuffer(file_bytes, np.uint8)
return cv2.imdecode(nparr, cv2.IMREAD_COLOR)
Embedding
On initialise le modèle une seule fois :
from keras_vggface.vggface import VGGFace
model = VGGFace(
model='resnet50',
include_top=False,
input_shape=(224, 224, 3),
pooling='avg'
)
Embedding & Aggregation
def compute_embedding(face_array):
"""
Appelée après extract_face, sur le visage extrait.
"""
return model.predict(face_array)[0] # shape (2048,)
def compute_benchmark_embeddings(face_arrays_dict):
"""
Appelée une fois que tu as tous les visages extraits pour chaque personne du benchmark.
:param face_arrays_dict: {"personA": [face_array1, face_array2], ...}
:return: {"personA": embedding_vector, ...}
"""
embeddings = {}
for person, arrays in face_arrays_dict.items():
if len(arrays) == 0:
continue
vectors = [compute_embedding(arr) for arr in arrays]
embeddings[person] = np.mean(vectors, axis=0)
return embeddings
Comparaison des visages
La distance euclidienne:
def euclidean_distance(a, b):
"""
Appelée pendant la prédiction, pour comparer l’embedding de l’image uploadée à chaque embedding du benchmark.
"""
return np.linalg.norm(a - b)
Script final
def load_benchmark_faces(raw_images_root):
"""
Parcourt le dossier raw_images_root et extrait les visages.
Structure attendue :
raw_images_root/
person_1/
img1.jpg
img2.jpg
person_2/
img3.jpg
:return: dict {"person_name": [face_array1, face_array2, ...], ...}
"""
benchmark_faces = {}
for person_name in os.listdir(raw_images_root):
person_dir = os.path.join(raw_images_root, person_name)
if not os.path.isdir(person_dir):
continue
face_arrays = []
print(f"\\n👤 Traitement de la personne : {person_name}")
for filename in os.listdir(person_dir):
filepath = os.path.join(person_dir, filename)
if not os.path.isfile(filepath):
continue
# On charge l'image avec OpenCV
img = cv2.imread(filepath)
if img is None:
print(f" ⚠️ Impossible de lire le fichier {filepath}")
continue
# Étape : face detection + extraction du visage
face_array = extract_face(img)
if face_array is not None:
face_arrays.append(face_array)
print(f" ✅ Visage extrait depuis {filename}")
else:
print(f" ❌ Pas de visage détecté dans {filename}")
benchmark_faces[person_name] = face_arrays
return benchmark_faces
# =========================
# 4. Pipeline de prédiction
# =========================
def predict_person_from_image(image_path, benchmark_embeddings):
"""
Prend une image d'entrée et renvoie la personne la plus ressemblante.
:param image_path: chemin vers l'image à tester
:param benchmark_embeddings: dict {"person_name": embedding_vector}
:return: (best_match_name, best_distance) ou (None, None) si échec
"""
img = cv2.imread(image_path)
if img is None:
print("⚠️ Impossible de lire l'image d'entrée.")
return None, None
# 1) Extraction du visage
face_array = extract_face(img)
if face_array is None:
print("⚠️ Aucun visage détecté dans l'image d'entrée.")
return None, None
# 2) Embedding de l'image uploadée
uploaded_embedding = compute_embedding(face_array)
# 3) Comparaison avec tous les embeddings du benchmark
best_name = None
best_distance = None
for person, emb in benchmark_embeddings.items():
dist = euclidean_distance(uploaded_embedding, emb)
print(f"Distance avec {person} : {dist:.4f}")
if best_distance is None or dist < best_distance:
best_distance = dist
best_name = person
return best_name, best_distance
if __name__ == "__main__":
RAW_IMAGES_ROOT = "data/images/raw_images"
print("🔹 Chargement et extraction des visages du benchmark...")
face_arrays_dict = load_benchmark_faces(RAW_IMAGES_ROOT)
print("\\n🔹 Calcul des embeddings du benchmark...")
benchmark_embeddings = compute_benchmark_embeddings(face_arrays_dict)
TEST_IMAGE_PATH = "data/images/test_image.jpg"
print(f"\\n🔹 Prédiction sur l'image : {TEST_IMAGE_PATH}")
best_match, distance = predict_person_from_image(TEST_IMAGE_PATH, benchmark_embeddings)
if best_match is not None:
print(f"\\n✅ Personne la plus ressemblante : {best_match} (distance = {distance:.4f})")
else:
print("\\n❌ Impossible de trouver une correspondance (aucun visage ou erreur de lecture).")
Exécution du projet
Assurez-vous avant tout :
- que chaque personne du benchmark a son dossier dans
raw_images - que
images_array/existe et est vide au départ
Lancer le projet
pip install -r requirements.txt
# Appliquer la face detection hors ligne
python main.py apply-face-detection-on-benchmark-people
# Lancer l’app Flask
export FLASK_APP=app.py
flask run --port=5000
Vous pouvez maintenant aller sur :
Uploader une image, et vérifier que le modèle retrouve la bonne personne.
Conclusion
Nous avons construit un pipeline complet de reconnaissance faciale :
- détection du visage
- extraction
- embedding via VGGFace
- comparaison vectorielle
- API Flask pour tester l’ensemble
