Analyse forensique d'images par IA
Quand l'utiliser
Lorsqu'un utilisateur demande « cette image est-elle générée par IA ? retouchée ? déjà utilisée ? », ou pour auditer en masse des images (photos de livraison, avis clients, images sources).
Une commande
python3 scripts/probe.py chemin_image... # Analyse métadonnées uniquement
python3 scripts/probe.py --dir répertoire --lib bibliothèque # Avec détection des doublons
python3 scripts/probe.py --dir répertoire --model <rép_poids> # Avec couche pixel, optionnel
Quatre conclusions possibles, ne pas dire que NO-EVIDENCE signifie « vraie image » :
| Conclusion | Signification | Action possible |
|---|---|---|
AI-SIGNED | Signature AIGC conforme (TC260) ou C2PA présente | Fiable, auto-déclaration du générateur |
AI-LIKELY | Empreinte générative détectée ou score pixel élevé | Indice uniquement, vérification humaine requise |
DUPLICATE | Distance pHash ≤ 10 avec une image de la bibliothèque | Duplication confirmée |
NO-EVIDENCE | Aucune des trois couches n'a trouvé de preuve | Ne signifie pas « vrai », seulement « aucune preuve » |
Capacités réelles des trois couches (test local, ne pas promettre sans vérification)
Couche 1 : Métadonnées – la plus fiable et la moins coûteuse (millisecondes, pur Python).
- Les images générées conformément à la réglementation chinoise intègrent la signature implicite AIGC TC260 :
{"AIGC":{"Label":"1","ContentProducer":...,"ProduceID":...,"ReservedCode1":"...sm3...sm2..."}}. Elle se trouve souvent dans le champeXIfdu chunk PNG (UserComment 0x9286), pas danstEXt, donc les outils qui ne scannent que les blocs texte la manquent. - Mais une réenregistrement la supprime : si Pillow sauvegarde en JPG ou PNG, les champs disparaissent. Cette couche ne détecte que les images originales ou transmises sans modification, pas les captures d'écran ou retouchées.
- La présence de
Make/Model/DateTimeOriginalindique une photo directe d'un appareil ; leur absence signale souvent un passage via un réseau social ou une capture.
Couche 2 : Détection des doublons – pHash + dHash, 20 ms par image.
- Compression de plateforme, redimensionnement, capture : distance 0~2, quasiment insensible.
- Recadrage de 7.5 % des bords : distance 8~24, certains doublons sont manqués.
- Distance minimale entre images différentes : 24, donc le seuil de 10 est sûr.
Couche 3 : Modèle pixel – lent et instable, à n'activer que si nécessaire.
- Community-Forensics ViT-S/384 (CVPR 2025) : CPU 0.2
0.6 s par image. Très efficace sur les générateurs anciens de sa distribution d'entraînement (5/5 pour les 5 images de test officielles de DALL·E 2), mais totalement inefficace sur les générateurs récents (7 images de 2026 testées, toutes classées comme vraies avec une confiance de 0.00000.0019). - La couche pixel est donc fiable uniquement lorsque le résultat est positif ; un résultat négatif ne signifie rien.
- Les poids ne sont pas dans le dépôt ; téléchargez-les depuis
OwensLab/commfor-model-384(HF, 87 Mo) et placez-les dansmodels/commfor384/.
Répartition des trois modèles (à ne pas confondre)
| Script | Modèle | Alimentation correcte | Alimentation interdite |
|---|---|---|---|
scripts/run_commfor.py | ViT-S/16 384 (CVPR'25, détecteur d'images générées) | Images générées générales, --size 384 par défaut | Ne pas utiliser le poids 224 (tests : 3/5 seulement) |
scripts/run_raiw.py | RAIW source-classify (10 fabricants + inconnu) | Pour savoir « quel générateur a produit cette image » | Ne pas l'utiliser comme détecteur (tests : 17/17 abstentions) |
scripts/run_xai.py | EfficientNetV2-B0 5 classes (FF++ deepfakes) | Uniquement des visages | Images de produits/photos de livraison (tests : 5/10 vraies images classées comme fausses) |
python3 scripts/run_commfor.py image.jpg --size 384 --weights /chemin/model.safetensors
python3 scripts/run_raiw.py image.jpg --weights /chemin/multi-provider-source-v3.npz
python3 scripts/run_xai.py visage.jpg --weights /chemin/ff_attribution.ckpt
Les trois scripts supportent --weights pour spécifier le chemin des poids, --out pour sauvegarder (sinon affichage uniquement). Ordre de recherche des poids :
--weights > variable d'environnement > scripts/../models/<nom_modèle>/. Les poids ne sont pas dans le dépôt, téléchargez-les :
| Modèle | Source | Taille |
|---|---|---|
| commfor384 | HF OwensLab/commfor-model-384 | 87 Mo |
| commfor224 | HF OwensLab/commfor-model-224 | 86 Mo |
| RAIW | HF wiltodelta/raiw-source-classify | 2,03 Go |
| XAI-Deepfakes | Dépôt model/checkpoint/ff_attribution.ckpt | 70 Mo |
Téléchargement depuis la Chine : miroir HF_ENDPOINT=https://hf-mirror.com ; fichiers volumineux : aria2c -x16 -s16.
Ne pas faire construire la commande de téléchargement à l'utilisateur ; un script prêt à l'emploi existe (par défaut via hf-mirror, aria2c multi-thread, vérification de la taille et SHA256 après téléchargement) :
python3 scripts/fetch_weights.py --list # Liste + état actuel
python3 scripts/fetch_weights.py commfor384 # 87 Mo, suffisant pour la couche pixel
python3 scripts/fetch_weights.py --all --jobs 16 # Les quatre modèles, total 2,2 Go
python3 scripts/fetch_weights.py --all --check # Vérification des fichiers existants
Le script supporte la reprise ; les fichiers tronqués par un proxy sont détectés par taille + SHA256 (test réel : un tarball GitHub manquait 70 Mo et échouait au décompactage avec failed finding central directory).
Les fichiers téléchargés se trouvent dans models/<nom_modèle>/, parfait pour les scripts run_*.py sans paramètre --weights supplémentaire.
Si le téléchargement échoue, indiquer clairement à quelle étape ; ne pas se contenter de « en théorie, ça marche » sans test réel.
Format de sortie (répondre dans ce format)
Sortie réelle de probe.py, un exemple pour chaque niveau :
samples/ai/phone_photo_stock.png -> AI-SIGNED
Raison : fichier contient la signature AIGC conforme (Label=1), auto-déclaration du générateur
Métadonnées : aigc_label | Format PNG 1024x1536
samples/variants/real01__p75.jpg -> DUPLICATE
Raison : distance de hachage perceptuel avec real01.jpg = 0, seuil 10
Métadonnées : aucun marqueur positif | Format JPEG 1280x853
Doublon : plus proche real01.jpg distance 0 ≤10 doublon
samples/real/real02.jpg -> NO-EVIDENCE
Raison : aucune des trois couches n'a trouvé de preuve ; cela ne signifie pas vraie image, besoin d'autres preuves métier
Métadonnées : aucun marqueur positif | Format JPEG 4500x3000
En communiquant à l'utilisateur : donner la conclusion en majuscules sans traduction (ne pas dire « probablement IA ») ; ne pas reformuler la ligne « Raison », c'est la base du jugement ; après NO-EVIDENCE, ajouter immédiatement « ne signifie pas vraie image ».
Seuils de référence
| Critère | Seuil | Justification |
|---|---|---|
| pHash doublon | ≤10 | Distance minimale inter-image 24, marge suffisante |
| Pixel p_fake | >0,6 pour indice | 0,1~0,6 = bruit |
| Signature AIGC | Présence = détection | Champ standard, pas de seuil |
Trois limites (à informer l'utilisateur)
- Absence de signature ne signifie pas vraie image. Un simple réenregistrement la supprime (presse-papiers, WeChat, capture d'écran).
- Le modèle pixel ne peut pas condamner seul. Il devient inopérant face à un générateur inconnu, et peut classer une vraie image comme générée (test : photo de carton prise avec iPhone classée à 0,896 par le modèle 384).
- Pour une traçabilité fiable, utiliser des solutions métier : stocker le hash de l'image originale lors du téléchargement, enregistrer l'empreinte de l'appareil, comparer avec l'historique d'une commande – plus fiable que tout modèle de détection.
Exemples d'échecs
image-provenance(outil web client) : interface agréable, analyse fréquentielle complète, mais aucune mention d'AIGC/TC260 dans le code ; insensible aux signatures chinoises ; l'analyse des métadonnées dépend de CDN exifr, donc hors ligne elle régresse à de l'heuristique de bytes. Une image avec signature AIGC complète affiche « aucun indice d'IA ».XAI-Deepfakes: classifieur multi-classes de deepfakes (FaceForensics++), ne reconnaît que les visages. Appliqué à des photos de livraison, 5 vraies images sur 10 sont classées comme Face2Face/DeepFakes/NeuralTextures.LAID: architecture légère (ShuffleNet 0,34 M paramètres, CPU 20 ms), mais poids sur Google Drive, inaccessible depuis la Chine.