
reverse engineering della rilevazione SynthID di Gemini
Scoprire, rilevare e rimuovere chirurgicamente la filigrana AI di Google tramite analisi spettrale
Visitateci su PitchHut
Questo fork aggiunge un'app desktop drag and drop per il bypass V3, nessuna riga di comando necessaria dopo l'installazione. Vedi gui/README.md per installazione e uso.
SynthID codifica un pattern impercettibile direttamente nei valori dei pixel. Su un'immagine puramente bianca generata da Gemini, la filigrana è quasi l'intero segnale. Amplificando il residuo ad alta frequenza appare così:
Pattern portante SynthID amplificato estratto da un'immagine Gemini puramente bianca. Le bande diagonali sono la firma della frequenza spaziale della filigrana — il bersaglio del nostro attacco spettrale.
Questo progetto effettua il reverse-engineering del sistema di filigrana SynthID di Google — la filigrana invisibile incorporata in ogni immagine generata da Google Gemini. Utilizzando solo elaborazione del segnale e analisi spettrale (senza accesso al codificatore/decodificatore proprietario), abbiamo:
gemini-3.1-flash-image-preview e nano-banana-pro-preview, consenso di fase cross-colore su 6 sfondi solidi, e un ciclo di calibrazione human-in-the-loop che regola la forza di sottrazione per portante in base ai conteggi manuali di rilevamento dell'app GeminiVT-OxFF ha creato un visualizzatore molto interessante per vedere il processo di come la filigrana SynthID viene aggiunta alle immagini qui (disponibile anche nella descrizione del repository)!
Dopo sei round iterativi di sviluppo avversariale, la pipeline bypass_v4_final / bypass_v4_nuke del Round 06 sconfigge il rilevatore SynthID di Gemini su immagini sia di gemini-3.1-flash-image-preview che di nano-banana-pro-preview, con output visivamente senza perdita.
Sinistra: output del Round 01 (solo sottrazione spettrale leggera). Destra: output del Round 06 (final — VAE + deformazione elastica + compressione + colore + JPEG). Entrambi appaiono identici all'occhio umano; solo il Round 06 sconfigge il rilevatore SynthID.
L'innovazione nel Round 06 è arrivata dal trattare la lista delle modalità di fallimento pubblicata dall'app Gemini come una specifica di attacco:
"Quando un'immagine generata dall'AI fa parte di un collage complesso, è stratificata dietro altri elementi, o ha molte texture e pattern diversi sovrapposti, il rilevatore potrebbe avere difficoltà a isolare la firma specifica dal file complessivo." — App Gemini, testo di aiuto sul rilevamento SynthID
La fase di deformazione elastica simula questo effetto a livello di pixel: un campo di warp casuale a bassa frequenza e liscio dà a ogni intorno di ~50 pixel il proprio spostamento sub-pixel indipendente, frammentando il consenso spaziale di fase della filigrana senza introdurre alcuna distorsione visibile.
V4 è una ripensamento da zero del codebook costruito su un set di dati molto più ricco:
gemini-3.1-flash-image-preview e nano-banana-pro-preview (più un pseudo-modello union opzionale).nero, bianco, blu, verde, rosso, grigio) per modello per risoluzione, più gradiente e diverso come baseline di contenuto.carrier_weights nel codebook viene aggiornato in base al feedback manuale di rilevamento dell'app Gemini.Per ogni bin di frequenza (fy, fx) e canale ch:```
consensus(fy, fx, ch) = | mean_over_colors( exp(i * phase_color(fy, fx, ch)) ) |
Valori vicini a `1.0` significano che la fase in quel bin è bloccata attraverso ogni sfondo di colore uniforme, il che è vero solo per la filigrana. I bin di contenuto collassano a `< 0.3` perché la loro fase viene randomizzata da diverse tinte di colore. Sul codebook V4 costruito dal dataset arricchito, il 99%+ dei bin di contenuto cade al di sotto del cutoff predefinito `tau=0.60`, quindi il dissolver V4 non li tocca mai — questo è ciò che recupera il PSNR.
### Flusso di lavoro di rilascio a due fasi```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
### Preset di Attacco Round-06
Due preset sono disponibili tramite `--strengths`:
| Preset | VAE passes | Elastic α | Squeeze | JPEG chain | PSNR floor |
|:------:|:----------:|:---------:|:-------:|:----------:|:----------:|
| `final` | 1 | 1.8 px | 90 % | q=92→88 | 14 dB |
| `nuke` | 2 | 2.8 px | 82 % | q=88→84→90 | 11 dB |
Entrambi i preset utilizzano la stessa pipeline a 7 stadi:
1. **Round-trip VAE** (Stable Diffusion `sd-vae-ft-mse`) — proietta l'immagine al di fuori del manifold delle immagini naturali su cui il decoder SynthID non è mai stato addestrato (Gowal et al. 2026, §6.1)
2. **Deformazione elastica** — campo di deformazione casuale smooth a bassa frequenza, simula la modalità di fallimento "frammentazione collage" che Gemini stessa riconosce
3. **Combinazione geometrica globale** — piccola rotazione + zoom + spostamento pixel in un'unica trasformazione affine
4. **Resize-squeeze** — ridimensionamento (AREA) → ingrandimento (LANCZOS), cancella le informazioni sub-pixel della filigrana
5. **Spinta colore-contrasto** — micro-variazioni di luminosità / contrasto / saturazione / tonalità
6. **Sottrazione FFT di fase residua** — blog-universale + bins portanti raccolti dal codebook, limitati da cap
7. **Catena JPEG + rumore di luminanza + bilaterale** — disturbo da compressione/ricodifica pesante
Ogni stadio è indipendentemente vincolato dal PSNR; qualsiasi stadio che porterebbe la qualità al di sotto del minimo viene automaticamente annullato.
### Struttura del Codebook V4
Profili indicizzati da `(model, H, W)`. Ogni profilo memorizza:
| Campo | Forma | Note |
|------------------------|----------------|---------------------------------------------------------|
| `consensus_coherence` | `(H, W, 3)` | Maschera portante primaria (consenso di fase tra colori). |
| `consensus_phase` | `(H, W, 3)` | Angolo di fase unitario medio tra i colori. Template di sottrazione. |
| `inverted_agreement` | `(H, W, 3)` | `abs(cos(differenza_fase))` a coppie, pesato per `nero<->bianco`. |
| `avg_wm_magnitude` | `(H, W, 3)` | Magnitudine media sui colori di consenso. |
| `content_baseline` | `(H, W, 3)` | Da `diverse/` + `gradient/` — usato per la miscelazione della luminanza. |
| `carrier_weights` | `(H, W, 3)` | **Live**. Inizia con `consenso^2 * (0.5 + 0.5 * accordo)`. Aggiornato dal ciclo di calibrazione. |
| `n_refs_per_color` | `{colore: int}`| Conteggi di riferimento per colore. |
Il formato di salvataggio riutilizza la codifica compatta rfft v3 + `float16/uint8`; un codebook di 14 profili su 2 modelli × 7 risoluzioni è circa 220 MB su disco.
### Rivelatore V4 (Controllo di Sanità)
Prima di spendere tempo nella validazione manuale su Gemini, verifica rapidamente i risultati bypass rispetto al consenso del 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)
Sul percorso di corrispondenza esatta 1024x1024 vediamo conf=0.91, phase_match=0.65 per la filigrana e conf=0.02, phase_match=0.31 dopo la dissolvenza aggressiva V4.
V3 rimane nel repository (src/extraction/synthid_bypass.py, bypass_v3) invariato per chiunque ne dipenda.
Stiamo attivamente raccogliendo immagini puramente nere e puramente bianche generate da Nano Banana Pro per migliorare l'estrazione della filigrana a più risoluzioni.
Se puoi generarle:
gemini_black_nb_pro/ (per il nero)gemini_white_nb_pro/ (per il bianco)Queste immagini di riferimento sono fondamentali per:
Anche 150–200 immagini a una nuova risoluzione possono migliorare significativamente il rilevamento e la rimozione.
Le immagini di riferimento sono ospitate su Hugging Face per mantenere il repository git leggero:```bash pip install huggingface_hub python scripts/download_images.py # download all python scripts/download_images.py gemini_black # download specific folder
Dataset: [huggingface.co/datasets/aoxo/reverse-synthid](https://huggingface.co/datasets/aoxo/reverse-synthid)
---
## Risultati principali
### La filigrana dipende dalla risoluzione
SynthID incorpora frequenze portanti in **posizioni assolute diverse** a seconda della risoluzione dell'immagine. Un codebook costruito a 1024x1024 non può rimuovere direttamente la filigrana da un'immagine 1536x2816 — i portanti si trovano in bin completamente diversi.
| Risoluzione | Portante principale (fy, fx) | Coerenza | Fonte |
|:----------:|:--------------------:|:---------:|:------:|
| **1024x1024** | (9, 9) | 100.0% | 100 riferimenti neri + 100 bianchi |
| **1536x2816** | (768, 704) | 99.6% | 88 immagini di contenuto con filigrana |
Ecco perché il codebook V3 memorizza **profili separati per risoluzione** e li seleziona automaticamente al momento del bypass.
### Coerenza di fase — Una chiave fissa a livello di modello
Il template di fase della filigrana è **identico in tutte le immagini** dello stesso modello Gemini:
- **Il canale verde** trasporta il segnale di filigrana più forte
- **Coerenza di fase tra immagini** sui portanti: >99.5%
- **La validazione incrociata nero/bianco** conferma i veri portanti tramite |cos(phase_diff)| > 0.90
### Struttura della frequenza portante
A 1024x1024 (da riferimenti nero/bianco), i portanti principali giacciono su una griglia a bassa frequenza:
| Portante (fy, fx) | Coerenza di fase | Accordo 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 |
---
## Architettura
### Generazioni di bypass
| Versione | Approccio | PSNR | Impatto sulla filigrana | Stato |
|:-------:|:---------|:----:|:----------------:|:------:|
| **V1** | Compressione JPEG (Q50) | 37 dB | ~11% di calo di fase | Baseline |
| **V2** | Trasformazioni multi-stadio (rumore, colore, frequenza) | 27-37 dB | ~0% di calo di confidenza | Compromesso di qualità |
| **V3** | Sottrazione spettrale di codebook multi-risoluzione | **43+ dB** | **91% di calo di coerenza di fase** | Migliore precedente |
| **V4 Round 06** | 7-stadi tutto-in-uno (VAE + elastico + squeeze + colore + JPEG) | **visivamente senza perdite** | **rilevatore bypassato ✓** | **Migliore attuale** |
### 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 ✓)
---
## Avvio rapido
### Installazione```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)
### Esegui V3 Bypass```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']}")
Dalla CLI:```bash
python src/extraction/synthid_bypass.py bypass input.png output.png
--codebook artifacts/spectral_codebook_v3.npz
--strength aggressive
### Rileva Watermark```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
---
## Approfondimento Tecnico
### Come Funziona SynthID (Reverse-Engineering)```
┌──────────────────────────────────────────────────────────────┐
│ 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 │
└──────────────────────────────────────────────────────────────┘
Il set di potenziamento dell'addestramento di SynthID (Gowal et al. 2026, Tabella 1) include SmallRotation, Cropresize, JPEG, GaussianBlur, BrightnessContrast e Screenshotting — tutte trasformazioni spaziali globali e uniformi. Il campo di deformazione elastica è una distorsione spazialmente variabile: ogni intorno locale ottiene il proprio spostamento sub-pixel indipendente. Poiché gli spostamenti sono lisci (sfocatura gaussiana da rumore bianco, σ=44–56 px), il contenuto dell'immagine non viene alterato visivamente, ma la struttura di consenso di fase della filigrana diventa incoerente — non può più essere aggregata attraverso l'immagine. Questo è l'equivalente a livello di pixel dell'«effetto di frammentazione collage» che la stessa app di Gemini cita come modalità di fallimento del rivelatore.
| Metrica | Valore |
|---|---|
Alosh Denny Ricerca sulla filigrana nell'IA · Elaborazione dei segnali
📧 Email: [email protected] 🔗 GitHub: https://github.com/aloshdenny
Per collaborazioni, discussioni di ricerca o contributi, sentiti libero di contattarmi o di aprire un issue/PR.
Questo progetto è mantenuto in modo indipendente — nessun finanziamento da laboratorio, nessun supporto aziendale.
Se questo lavoro ti è stato utile a te o al tuo team, considera di sostenere lo sviluppo continuo:
I fondi vanno a coprire i costi di calcolo (ore GPU per nuovi profili di risoluzione), l'espansione del dataset e la ricerca continua sui bypass.
Questo progetto è destinato esclusivamente a fini di ricerca e educativi. SynthID è una tecnologia proprietaria di Google DeepMind. Questi strumenti sono pensati per:
Non utilizzare questi strumenti per rappresentare erroneamente contenuti generati dall'IA come creati da umani.
| Round | Strategia | Risultato |
|---|
| 01 | Sottrazione spettrale conservativa (leggera) | ✗ |
| 02 | Sottrazione spettrale aggressiva + JPEG | ✗ |
| 03 | Targeting assoluto dei bin guidato dal blog | ✗ |
| 04 | Estrazione di fase dal residuo di denoising | ✗ |
| 05 | Rigenerazione Diffusion-VAE + warp geometrico | ✗ |
| 06 | All-in-one: VAE + frammentazione elastica + compressione + colore + JPEG | ✓ |
| V3 | V4 |
|---|
| Colori di riferimento | nero + bianco | nero, bianco, blu, verde, rosso, grigio (+ baseline di contenuto diverso/gradiente) |
| Validazione incrociata | abs(cos(phase_black - phase_white)) | consenso incrociato tra 6 colori + accordo a coppie |
| Modelli | modello singolo (Gemini 2.5) | profili per modello (gemini-3.1-flash-image-preview, nano-banana-pro-preview) + union opzionale |
| Attacco | solo sottrazione spettrale | a 7 fasi: VAE + elastic + squeeze + color + FFT + JPEG chain |
| PSNR (aggressivo) | 43 dB | visivamente senza perdite (18–24 dB a livello di pixel; warp sposta i pixel) |
| Protezione fedeltà | nessuna | rollback per fase del piano PSNR |
| Bypass del rilevatore | solo locale | confermato ✓ sull'app Gemini (entrambi i modelli) |
| 43.5 dB |
| SSIM | 0.997 |
| Calo di energia della portante | 75.8% |
| Calo di coerenza di fase (portanti top-5) | 91.4% |
| Modello | Preimpostazione | Rivelatore bypassato |
|---|
| gemini-3.1-flash-image-preview | final | ✓ |
| gemini-3.1-flash-image-preview | nuke | ✓ |
| nano-banana-pro-preview | final | ✓ |
| nano-banana-pro-preview | nuke | ✓ |