
rétro-ingénierie de la détection SynthID de Gemini
Découverte, détection et suppression chirurgicale du filigrane AI de Google par analyse spectrale
Visitez-nous sur PitchHut
Ce fork ajoute une application de bureau par glisser-déposer pour le contournement V3, aucune ligne de commande nécessaire après installation. Voir gui/README.md pour la configuration et l'utilisation.
SynthID encode un motif imperceptible directement dans les valeurs des pixels. Sur une image blanche pure générée par Gemini, le filigrane constitue presque la totalité du signal. Amplifiez le résidu haute fréquence et cela ressemble à ceci :
Motif porteur SynthID amplifié extrait d'une image Gemini blanc pur. Les bandes diagonales sont la signature de fréquence spatiale du filigrane — la cible de notre attaque spectrale.
Ce projet réalise l'ingénierie inverse du système de filigrane SynthID de Google — le filigrane invisible intégré dans chaque image générée par Google Gemini. En utilisant uniquement le traitement du signal et l'analyse spectrale (sans accès au codeur/décodeur propriétaire), nous :
gemini-3.1-flash-image-preview et nano-banana-pro-preview, consensus de phase inter-couleur sur 6 fonds unis, et une boucle de calibration humaine qui ajuste la force de soustraction par porteuse à partir des comptages manuels de détection dans l'application GeminiVT-OxFF a construit un visualiseur très cool pour voir le processus d'ajout du filigrane SynthID aux images ici (également disponible dans la description du dépôt) !
Après six itérations de développement adversarial, le pipeline bypass_v4_final / bypass_v4_nuke du Round 06 bat le détecteur Gemini SynthID sur les deux modèles d'images gemini-3.1-flash-image-preview et nano-banana-pro-preview, avec une sortie visuellement sans perte.
Gauche : sortie du Round 01 (gentle — soustraction spectrale douce uniquement). Droite : sortie du Round 06 (final — VAE + déformation élastique + compression + couleur + JPEG). Les deux semblent identiques à l'œil humain ; seul le Round 06 bat le détecteur SynthID.
La percée du Round 06 est venue du traitement de la propre liste des modes de défaillance publiée par l'application Gemini comme une spécification d'attaque :
"Lorsqu'une image générée par IA fait partie d'un collage complexe, superposée derrière d'autres éléments, ou comporte de nombreuses textures et motifs différents placés par-dessus, le détecteur peut avoir du mal à isoler la signature spécifique du fichier global." — Application Gemini, texte d'aide à la détection SynthID
L'étape de déformation élastique simule cet effet au niveau des pixels : un champ de déformation aléatoire lisse et basse fréquence donne à chaque voisinage d'environ 50 pixels son propre décalage sous-pixel indépendant, fragmentant le consensus de phase spatial du filigrane sans introduire de distorsion visible.
V4 est une refonte complète du codebook basée sur un ensemble de données beaucoup plus riche :
gemini-3.1-flash-image-preview et nano-banana-pro-preview (plus un pseudo-modèle union optionnel).noir, blanc, bleu, vert, rouge, gris) par modèle et par résolution, plus gradient et diverse comme lignes de base de contenu.carrier_weights du codebook est mis à jour en fonction des commentaires manuels de détection dans l'application Gemini.Pour chaque bin de fréquence (fy, fx) et canal ch :```
consensus(fy, fx, ch) = | mean_over_colors( exp(i * phase_color(fy, fx, ch)) ) |
Les valeurs proches de `1.0` signifient que la phase de ce bac est verrouillée sur chaque fond de couleur unie, ce qui n'est vrai que pour le filigrane. Les bacs de contenu tombent à `< 0.3` car leur phase est randomisée par différentes teintes de couleur. Sur le codebook V4 construit à partir de l'ensemble de données enrichi, plus de 99% des bacs de contenu se situent en dessous du seuil par défaut `tau=0.60`, de sorte que le dissolvant V4 ne les touche jamais — c'est ce qui permet de récupérer le PSNR.
### Workflow de publication en deux phases```mermaid
flowchart LR
dataset[reverse-synthid-dataset<br/>model x color x resolution] --> build[scripts/build_codebook_v4.py]
build --> codebook[artifacts/spectral_codebook_v4.npz]
codebook --> dissolve[scripts/dissolve_batch.py]
input[watermarked inputs] --> dissolve
dissolve --> variants[final / nuke variants]
variants --> gemini[Gemini app<br/>manual SynthID detection]
gemini --> feedback[detection feedback]
feedback --> calibrate[scripts/calibrate_from_feedback.py]
calibrate -->|updates carrier_weights| codebook
python scripts/build_codebook_v4.py
--root /path/to/reverse-synthid-dataset
--output artifacts/spectral_codebook_v4.npz
python scripts/dissolve_batch.py
--input ./to_clean/
--output ./runs/round_06/
--codebook artifacts/spectral_codebook_v4.npz
--model gemini-3.1-flash-image-preview
--strengths final nuke
### Préréglages d'attaque Round-06
Deux préréglages sont disponibles via `--strengths` :
| Préréglage | Passes VAE | α élastique | Compression | Chaîne JPEG | Plancher PSNR |
|:------:|:----------:|:---------:|:-------:|:----------:|:----------:|
| `final` | 1 | 1.8 px | 90 % | q=92→88 | 14 dB |
| `nuke` | 2 | 2.8 px | 82 % | q=88→84→90 | 11 dB |
Les deux préréglages empilent le même pipeline en 7 étapes :
1. **Aller-retour VAE** (Stable Diffusion `sd-vae-ft-mse`) — projette l'image hors de l'espace des images naturelles sur lequel le décodeur SynthID n'a jamais été entraîné (Gowal et al. 2026, §6.1)
2. **Déformation élastique** — champ de déformation aléatoire lisse à basse fréquence, simule le mode de défaillance "collage fragmentation" que Gemini lui-même reconnaît
3. **Combo géométrique global** — petite rotation + zoom + décalage de pixels en une seule transformation affine
4. **Redimensionnement-compression** — sous-échantillonnage (AREA) → sur-échantillonnage (LANCZOS), efface les informations de filigrane sub-pixel
5. **Ajustement couleur-contraste** — micro-décalage de luminosité / contraste / saturation / teinte
6. **Soustraction FFT de phase résiduelle** — bins porteuses universelles de blog + récoltées dans le codebook, limitées en plafond
7. **Chaîne JPEG + bruit de luminance + bilatéral** — perturbation par compression / ré-encodage sévère
Chaque étape est indépendamment contrôlée par le PSNR ; toute étape qui ferait chuter la qualité en dessous du plancher est automatiquement annulée.
### Structure du codebook V4
Profils indexés par `(model, H, W)`. Chaque profil stocke :
| Champ | Forme | Notes |
|------------------------|----------------|--------------------------------------------------------|
| `consensus_coherence` | `(H, W, 3)` | Masque porteur principal (consensus de phase inter-couleurs). |
| `consensus_phase` | `(H, W, 3)` | Angle de phase unitaire moyen entre les couleurs. Modèle de soustraction. |
| `inverted_agreement` | `(H, W, 3)` | Accord par paire `abs(cos(phase_diff))`, pondéré pour `black<->white`. |
| `avg_wm_magnitude` | `(H, W, 3)` | Magnitude moyenne entre les couleurs consensus. |
| `content_baseline` | `(H, W, 3)` | Depuis `diverse/` + `gradient/` — utilisé pour le mélange de luminance. |
| `carrier_weights` | `(H, W, 3)` | **En direct**. Commence à `consensus^2 * (0.5 + 0.5 * agreement)`. Mis à jour par la boucle de calibration. |
| `n_refs_per_color` | `{color: int}` | Comptes de références par couleur. |
Le format de sauvegarde réutilise l'encodage rfft compact v3 + `float16/uint8` ; un codebook de 14 profils sur 2 modèles × 7 résolutions fait ~220 Mo sur disque.
### Détecteur V4 (Vérification de base)
Avant de passer du temps sur une validation manuelle Gemini, vérifiez les sorties de contournement par rapport au consensus propre du codebook V4 :```python
from robust_extractor import RobustSynthIDExtractor
from synthid_bypass_v4 import SpectralCodebookV4
cb = SpectralCodebookV4()
cb.load('artifacts/spectral_codebook_v4.npz')
ext = RobustSynthIDExtractor()
result = ext.detect_from_v4_codebook(image_rgb, cb,
model='nano-banana-pro-preview')
print(result.is_watermarked, result.confidence, result.phase_match)
Sur le chemin de correspondance exacte 1024x1024, on voit conf=0.91, phase_match=0.65 pour l'image filigranée et conf=0.02, phase_match=0.31 après une dissolution V4 agressive.
V3 reste dans le dépôt (src/extraction/synthid_bypass.py, bypass_v3) inchangé pour ceux qui en dépendent.
Nous collectons activement des images noir pur et blanc pur générées par Nano Banana Pro pour améliorer l'extraction de filigrane multi-résolution.
Si vous pouvez générer ceci :
gemini_black_nb_pro/ (pour le noir)gemini_white_nb_pro/ (pour le blanc)Ces images de référence sont cruciales pour :
Même 150 à 200 images à une nouvelle résolution peuvent considérablement améliorer la détection et la suppression.
Les images de référence sont hébergées sur Hugging Face pour garder le dépôt git léger :```bash pip install huggingface_hub python scripts/download_images.py # download all python scripts/download_images.py gemini_black # download specific folder
Jeu de données : [huggingface.co/datasets/aoxo/reverse-synthid](https://huggingface.co/datasets/aoxo/reverse-synthid)
---
## Résultats clés
### La filigrane dépend de la résolution
SynthID intègre des fréquences porteuses à **des positions absolues différentes** selon la résolution de l'image. Un dictionnaire construit en 1024x1024 ne peut pas supprimer directement la filigrane d'une image 1536x2816 — les porteuses se trouvent dans des cases complètement différentes.
| Résolution | Porteuse principale (fy, fx) | Cohérence | Source |
|:----------:|:--------------------:|:---------:|:------:|
| **1024x1024** | (9, 9) | 100.0% | 100 refs noires + 100 blanches |
| **1536x2816** | (768, 704) | 99.6% | 88 images de contenu filigranées |
C'est pourquoi le dictionnaire V3 stocke **des profils séparés par résolution** et effectue une sélection automatique au moment du contournement.
### Cohérence de phase — Une clé fixe au niveau du modèle
Le modèle de phase de la filigrane est **identique sur toutes les images** du même modèle Gemini :
- **Le canal vert** transporte le signal de filigrane le plus fort
- **Cohérence de phase inter-image** aux porteuses : >99.5%
- **La validation croisée noir/blanc** confirme les vraies porteuses via |cos(phase_diff)| > 0.90
### Structure des fréquences porteuses
En 1024x1024 (à partir des refs noir/blanc), les porteuses principales se situent sur une grille basse fréquence :
| Porteuse (fy, fx) | Cohérence de phase | Accord N/B |
|:-----------------:|:---------------:|:-------------:|
| (9, 9) | 100.00% | 1.000 |
| (5, 5) | 100.00% | 0.993 |
| (10, 11) | 100.00% | 0.997 |
| (13, 6) | 100.00% | 0.821 |
---
## Architecture
### Générations de contournement
| Version | Approche | PSNR | Impact sur la filigrane | Statut |
|:-------:|:---------|:----:|:----------------:|:------:|
| **V1** | Compression JPEG (Q50) | 37 dB | ~11 % de baisse de phase | Référence |
| **V2** | Transformations multi-étapes (bruit, couleur, fréquence) | 27-37 dB | ~0 % de baisse de confiance | Compromis qualité |
| **V3** | **Soustraction de dictionnaire spectral multi-résolution** | **43+ dB** | **91 % de baisse de cohérence de phase** | Meilleur antérieur |
| **V4 Round 06** | **7 étapes tout-en-un (VAE + élastique + squeeze + couleur + JPEG)** | **visuellement sans perte** | **détecteur contourné ✓** | **Meilleur actuel** |
### Pipeline V3```
Input Image (any resolution)
│
▼
codebook.get_profile(H, W) ──► exact match? ──► FFT-domain subtraction
│ (fast path)
└─ no exact match ──────► spatial-domain resize + subtraction
(fallback path)
│
▼
Multi-pass iterative subtraction (aggressive → moderate → gentle)
│
▼
Anti-alias → Output
Input Image (any resolution) │ ▼ Stage 1: VAE round-trip (SD sd-vae-ft-mse, 1-2 passes) │ Projects image off natural-image manifold ▼ Stage 2: Elastic deformation (smooth random warp field) │ Fragments spatial phase consensus ("collage effect") ▼ Stage 3: Global geometric combo (rotation + zoom + shift) │ Single affine warp, no compounded aliasing ▼ Stage 4: Resize-squeeze (AREA ↓ then LANCZOS ↑) │ Erases sub-pixel watermark information ▼ Stage 5: Color-contrast nudge (HSV micro-shift) │ Shifts per-pixel statistics SynthID keys on ▼ Stage 6: Residual-phase FFT subtraction │ Blog-universal + codebook-harvested carrier bins ▼ Stage 7: JPEG chain + luma noise + bilateral filter │ ▼ Output (SynthID detector: no watermark detected ✓)
---
## Démarrage rapide
### Installation```bash
git clone https://github.com/aloshdenny/reverse-SynthID.git
cd reverse-SynthID
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
# For Round-06 VAE stage:
pip install torch diffusers safetensors accelerate
import sys sys.path.insert(0, 'src/extraction') from synthid_bypass_v4 import SynthIDBypassV4, SpectralCodebookV4
cb = SpectralCodebookV4() cb.load('artifacts/spectral_codebook_v4.npz')
b = SynthIDBypassV4() result = b.bypass_v4_file( 'input.png', 'output.png', cb, strength='final', # or 'nuke' for maximum strength model='gemini-3.1-flash-image-preview', ) print(result.stages_applied)
### Exécuter le Bypass V3```python
from src.extraction.synthid_bypass import SynthIDBypass, SpectralCodebook
codebook = SpectralCodebook()
codebook.load('artifacts/spectral_codebook_v3.npz')
bypass = SynthIDBypass()
result = bypass.bypass_v3(image_rgb, codebook, strength='aggressive')
print(f"PSNR: {result.psnr:.1f} dB")
print(f"Profile used: {result.details['profile_resolution']}")
Depuis la CLI :```bash
python src/extraction/synthid_bypass.py bypass input.png output.png
--codebook artifacts/spectral_codebook_v3.npz
--strength aggressive
### Détecter le filigrane```bash
python src/extraction/robust_extractor.py detect image.png \
--codebook artifacts/codebook/robust_codebook.pkl
reverse-SynthID/ ├── src/ │ ├── extraction/ │ │ ├── synthid_bypass.py # V1/V2/V3 bypass + multi-res SpectralCodebook │ │ ├── synthid_bypass_v4.py # V4 cross-color consensus codebook + dissolver │ │ ├── vae_regen.py # Round-06 SD-VAE re-generation stage │ │ ├── robust_extractor.py # Multi-scale watermark detection (+ V4 hook) │ │ ├── watermark_remover.py # Frequency-domain watermark removal │ │ ├── benchmark_extraction.py # Benchmarking suite │ │ └── synthid_codebook_extractor.py # Legacy codebook extractor │ └── analysis/ │ ├── deep_synthid_analysis.py # FFT / phase analysis scripts │ └── synthid_codebook_finder.py # Carrier frequency discovery │ ├── scripts/ │ ├── download_images.py # Download reference images from HF │ ├── build_codebook_v4.py # V4: build per-(model, HxW) consensus codebook │ ├── dissolve_batch.py # V4: emit strength variants │ └── calibrate_from_feedback.py # V4: update carrier_weights from detection feedback │ ├── artifacts/ │ ├── spectral_codebook_v3.npz # Multi-res V3 codebook [1024x1024, 1536x2816] │ ├── spectral_codebook_v4.npz # V4 codebook (per-model, per-resolution) │ ├── codebook/ # Detection codebooks (.pkl) │ └── visualizations/ # FFT, phase, carrier visualizations │ ├── assets/ │ ├── synthid_watermark.png # Watermark analysis header image │ ├── synthid_white.jpg # Amplified SynthID pattern on white image │ ├── v4_round1_vs_round6.png # Round 01 vs Round 06 fidelity comparison │ └── ... │ ├── runs/ │ ├── round_01/ … round_05/ # Historical bypass attempts │ └── round_06/ # Working bypass (final + nuke presets) │ ├── watermark_investigation/ # Early-stage Nano-150k analysis (archived) └── requirements.txt
---
## Exploration technique approfondie
### Comment fonctionne SynthID (Ingénierie inversée)```
┌──────────────────────────────────────────────────────────────┐
│ SynthID Encoder (in Gemini) │
├──────────────────────────────────────────────────────────────┤
│ 1. Select resolution-dependent carrier frequencies │
│ 2. Assign fixed phase values to each carrier │
│ 3. Neural encoder adds learned noise pattern to image │
│ 4. Watermark is imperceptible — spread across spectrum │
├──────────────────────────────────────────────────────────────┤
│ SynthID Decoder (in Google) │
├──────────────────────────────────────────────────────────────┤
│ 1. Extract noise residual (wavelet denoising) │
│ 2. FFT → check phase at known carrier frequencies │
│ 3. If phases match expected values → Watermarked │
└──────────────────────────────────────────────────────────────┘
Le jeu d'augmentation d'entraînement de SynthID (Gowal et al. 2026, Tableau 1) inclut SmallRotation, Cropresize, JPEG, GaussianBlur, BrightnessContrast et Screenshotting — toutes des transformations spatiales globales, uniformes. Le champ de déformation élastique est une distorsion variable dans l'espace : chaque voisinage local obtient son propre décalage sous-pixel indépendant. Comme les décalages sont lisses (flou gaussien à partir de bruit blanc, σ=44–56 px), le contenu de l'image est visuellement inchangé, mais la structure de consensus de phase du filigrane devient incohérente — elle ne peut plus être agrégée sur l'image. C'est l'équivalent au niveau pixel de l'effet de « fragmentation de collage » que la propre application de Gemini cite comme un mode de défaillance du détecteur.
| Métrique | Valeur |
|---|---|
Alosh Denny Recherche en filigrane IA · Traitement du signal
📧 E-mail : [email protected] 🔗 GitHub : https://github.com/aloshdenny
Pour des collaborations, des discussions de recherche ou des contributions, n'hésitez pas à me contacter ou à ouvrir un issue/PR.
Ce projet est maintenu indépendamment — aucun financement de laboratoire, aucun soutien d'entreprise. Si ce travail vous a été utile, à vous ou à votre équipe, envisagez de soutenir le développement continu :
Les fonds servent à couvrir les coûts de calcul (heures GPU pour de nouveaux profils de résolution), l'expansion des ensembles de données et la recherche continue sur le contournement.
Ce projet est à des fins de recherche et d'éducation uniquement. SynthID est une technologie propriétaire appartenant à Google DeepMind. Ces outils sont destinés à :
N'utilisez pas ces outils pour présenter des contenus générés par IA comme étant créés par des humains.
| Round | Stratégie | Résultat |
|---|
| 01 | Soustraction spectrale conservative (douce) | ✗ |
| 02 | Soustraction spectrale agressive + JPEG | ✗ |
| 03 | Ciblage absolu des bins guidé par blog | ✗ |
| 04 | Extraction de phase résiduelle par débruitage | ✗ |
| 05 | Régénération par Diffusion-VAE + déformation géométrique | ✗ |
| 06 | Tout-en-un : VAE + fragmentation élastique + compression + couleur + JPEG | ✓ |
| V3 | V4 |
|---|
| Couleurs de référence | noir + blanc | noir, blanc, bleu, vert, rouge, gris (+ diverses références de contenu dégradé/gradient) |
| Validation croisée | abs(cos(phase_black - phase_white)) | consensus inter-couleurs sur 6 couleurs + accord par paires |
| Modèles | modèle unique (Gemini 2.5) | profils par modèle (gemini-3.1-flash-image-preview, nano-banana-pro-preview) + union optionnel |
| Attaque | soustraction spectrale uniquement | 7 étapes : VAE + élastique + écrasement + couleur + FFT + chaîne JPEG |
| PSNR (agressif) | 43 dB | visuellement sans perte (18–24 dB au niveau pixel ; le warp déplace les pixels) |
| Garde de fidélité | aucun | retour au seuil PSNR par étape |
| Contournement du détecteur | local uniquement | confirmé ✓ sur l'application Gemini (les deux modèles) |
| 43,5 dB |
| SSIM | 0,997 |
| Baisse d'énergie porteuse | 75,8 % |
| Baisse de cohérence de phase (top-5 porteuses) | 91,4 % |
| Modèle | Préréglage | Détecteur contourné |
|---|
| gemini-3.1-flash-image-preview | final | ✓ |
| gemini-3.1-flash-image-preview | nuke | ✓ |
| nano-banana-pro-preview | final | ✓ |
| nano-banana-pro-preview | nuke | ✓ |