Face Blurring - RGPD Compliance¶
Overview¶
Le système de floutage automatique des visages anonymise les photos uploadées pour respecter le RGPD. Ce traitement est effectué de manière asynchrone après l'upload des photos.
Architecture¶
- FaceBlurringService: Service de détection et floutage des visages
- BlurFacesJob: Job asynchrone exécuté après chaque upload de photo
- Base de données: Colonnes
face_blurredetfaces_countdansphotos_incident
Méthodes de Détection¶
Le service supporte plusieurs méthodes de détection (par ordre de préférence):
1. Extension PHP facedetect (Recommandé)¶
# Installation sur Debian/Ubuntu
apt-get update
apt-get install -y php-pear php-dev libopencv-dev
pecl install facedetect
echo "extension=facedetect.so" > /etc/php/8.2/mods-available/facedetect.ini
phpenmod facedetect
service php8.2-fpm restart
2. Haar Cascade XML (Fallback)¶
Si l'extension n'est pas disponible, le service cherche les fichiers Haar Cascade OpenCV:
# Télécharger Haar Cascade
mkdir -p /srv/urbafix/resources
wget https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_default.xml \
-O /srv/urbafix/resources/haarcascade_frontalface_default.xml
Note: Cette méthode nécessite une implémentation supplémentaire (Python/OpenCV externe ou php-opencv).
3. Mode Dégradé (No-op)¶
Si aucune méthode n'est disponible:
- Le job s'exécute sans erreur
- Les photos ne sont pas modifiées
- La colonne face_blurred est mise à 0
Configuration Docker¶
Ajouter dans le Dockerfile:
# Installation des dépendances de détection de visages
RUN apt-get update && apt-get install -y \
php-pear \
php-dev \
libopencv-dev \
&& pecl install facedetect \
&& docker-php-ext-enable facedetect \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
Migration Base de Données¶
Exécuter la migration pour ajouter les colonnes requises:
# Via Docker
docker-compose exec db mysql -u urbafix_user -p urbafix < database/migration_face_blurring.sql
# Ou via l'interface
docker-compose exec db bash
mysql -u urbafix_user -p urbafix < /var/www/html/database/migration_face_blurring.sql
Activation¶
Le floutage est activé par défaut. Pour le désactiver:
Fonctionnement¶
- Upload photo →
submit_incident.php - Insertion BDD →
photos_incidentavecface_blurred=0 - Dispatch job →
BlurFacesJobvia queue (sync ou async) - Détection visages → FaceBlurringService
- Floutage → Pixellisation + Gaussian blur
- Remplacement fichier → Image floutée écrase l'originale
- Update BDD →
face_blurred=1,faces_count=N
Algorithme de Floutage¶
Le service applique un floutage en 2 étapes pour une anonymisation efficace:
- Pixellisation: Réduction puis agrandissement de la zone (facteur 10)
- Gaussian Blur: 3 passes de flou gaussien
La zone floutée inclut une marge de 20% autour du visage détecté.
Monitoring¶
-- Photos traitées
SELECT face_blurred, COUNT(*) as total
FROM photos_incident
GROUP BY face_blurred;
-- Photos avec visages détectés
SELECT faces_count, COUNT(*) as total
FROM photos_incident
WHERE face_blurred = 1
GROUP BY faces_count;
-- Photos en attente de traitement
SELECT COUNT(*) as pending
FROM photos_incident
WHERE face_blurred = 0;
Logs¶
Le service log toutes les opérations:
# Voir les logs de floutage
docker-compose logs -f web | grep BlurFacesJob
# Exemples de logs
# BlurFacesJob: Photo 123 - 2 visage(s) flouté(s)
# BlurFacesJob: Détection de visages non disponible - photo 124 ignorée
# BlurFacesJob: Photo 125 - Aucun visage détecté
Performance¶
- Détection: 200-500ms par photo (selon résolution et nombre de visages)
- Floutage: 50-100ms par visage
- Recommandation: Utiliser
QUEUE_DRIVER=redisen production pour traitement asynchrone
EXIF et floutage — extraction préalable (v3.1, 2026-08-25)¶
Le floutage réencode l'image via GD (imagecreatefromjpeg/imagejpeg), ce qui supprime systématiquement les métadonnées EXIF (date de prise de vue, GPS). Toute photo passée par ce pipeline perd donc ses EXIF, qu'elle contienne un visage détecté ou non.
Solution : src/ExifHelper.php extrait date/heure + GPS depuis les octets bruts de l'image avant chiffrement et avant floutage, dans les points d'upload (api/submit_incident.php, api/add_photos.php, declare_incident.php). Le résultat est persisté en base, indépendamment du fichier :
ALTER TABLE photos_incident
ADD COLUMN exif_taken_at DATETIME NULL AFTER encrypted,
ADD COLUMN exif_latitude DECIMAL(10,7) NULL AFTER exif_taken_at,
ADD COLUMN exif_longitude DECIMAL(10,7) NULL AFTER exif_latitude;
// Avant chiffrement/floutage — sur les octets bruts en mémoire
$exif = ExifHelper::extract($imageData);
$db->execute(
"INSERT INTO photos_incident (..., exif_taken_at, exif_latitude, exif_longitude) VALUES (..., ?, ?, ?)",
[..., $exif['taken_at'], $exif['latitude'], $exif['longitude']]
);
incident_detail.php affiche ces colonnes directement (badge date/heure/GPS sur les photos) au lieu de re-parser l'EXIF du fichier stocké — qui n'en contient plus après floutage. Voir Interface Administration.
Non couvert : les photos de réponse mairie (type='mairie', upload via incident_detail.php) et les photos d'intervention agent (api/agents/incidents/add_photo.php) n'extraient pas encore l'EXIF.
Conformité ANSSI¶
- ✅ Anonymisation automatique des données biométriques
- ✅ Pas de stockage des visages originaux (écrasement immédiat)
- ✅ Traçabilité via colonnes BDD
- ✅ Permissions fichiers 0644 (lecture seule pour groupe/autres)
Troubleshooting¶
L'extension facedetect ne se charge pas¶
# Vérifier l'installation
php -m | grep facedetect
# Vérifier les logs PHP
docker-compose logs web | grep facedetect
# Tester manuellement
php -r "var_dump(extension_loaded('facedetect'));"
Les visages ne sont pas détectés¶
- Vérifier la résolution de l'image (min 200x200px recommandé)
- Vérifier l'éclairage de la photo
- Tester avec Haar Cascade si extension facedetect échoue
- Les visages de profil peuvent ne pas être détectés (normal)
Performance dégradée¶
- Activer
QUEUE_DRIVER=redispour traitement asynchrone - Réduire la résolution des photos avant upload (app mobile)
- Augmenter les ressources Docker (CPU/RAM)
API Info¶
Endpoint pour vérifier la configuration:
// TODO: Créer /backend/api/face_blurring_info.php
require_once '../../../src/Services/FaceBlurringService.php';
$service = new FaceBlurringService();
echo json_encode($service->getInfo());
// Retourne:
// {
// "face_detection_available": true,
// "facedetect_extension": true,
// "haar_cascade_path": null,
// "gd_available": true
// }