
ingeniería inversa de la detección SynthID de Gemini
Descubriendo, detectando y eliminando quirúrgicamente la marca de agua de IA de Google mediante análisis espectral
Visítanos en PitchHut
Este fork añade una aplicación de escritorio de arrastrar y soltar para el bypass V3, sin necesidad de línea de comandos después de la configuración. Consulta gui/README.md para configuración y uso.
SynthID codifica un patrón imperceptible directamente en los valores de los píxeles. En una imagen de color blanco puro generada por Gemini, la marca de agua es casi toda la señal. Amplifica el residuo de alta frecuencia y se ve así:
Patrón portadora de SynthID amplificada extraída de una imagen blanca pura de Gemini. Las bandas diagonales son la firma de frecuencia espacial de la marca de agua — el objetivo de nuestro ataque espectral.
Este proyecto realiza ingeniería inversa del sistema de marcas de agua SynthID de Google — la marca de agua invisible incrustada en cada imagen generada por Google Gemini. Usando solo procesamiento de señales y análisis espectral (sin acceso al codificador/decodificador propietario), nosotros:
gemini-3.1-flash-image-preview y nano-banana-pro-preview, consenso de fase entre colores sobre 6 fondos sólidos, y un bucle de calibración humano en el ciclo que ajusta la fuerza de sustracción por portadora a partir de recuentos manuales de detección de la app GeminiVT-OxFF construyó un visualizador realmente genial para ver el proceso de cómo se añade la marca de agua SynthID a las imágenes aquí (¡también disponible en la descripción del repositorio)!
Después de seis rondas iterativas de desarrollo adversarial, el pipeline bypass_v4_final / bypass_v4_nuke de la Ronda 06 derrota al detector SynthID de Gemini en ambas imágenes de gemini-3.1-flash-image-preview y nano-banana-pro-preview, con resultado visualmente sin pérdidas.
Izquierda: Salida de la Ronda 01 (solo sustracción espectral gentle). Derecha: Salida de la Ronda 06 (final — VAE + warp elástico + compresión + color + JPEG). Ambas se ven idénticas a ojos humanos; solo la Ronda 06 derrota al detector SynthID.
El avance en la Ronda 06 provino de tratar la lista de modos de fallo publicados por la propia app de Gemini como una especificación de ataque:
"Cuando una imagen generada por IA forma parte de un collage complejo, está en capas detrás de otros elementos, o tiene muchas texturas y patrones diferentes superpuestos, el detector puede tener dificultades para aislar la firma específica del archivo general." — App de Gemini, texto de ayuda de detección de SynthID
La etapa de deformación elástica simula este efecto a nivel de píxel: un campo de warp aleatorio suave y de baja frecuencia le da a cada vecindario de ~50 píxeles su propio desplazamiento de subpíxel independiente, fragmentando el consenso de fase espacial de la marca de agua sin introducir distorsión visible.
V4 es una reestructuración desde cero del libro de códigos basada en un conjunto de datos mucho más rico:
gemini-3.1-flash-image-preview y nano-banana-pro-preview (más un pseudomodelo union opcional).black, white, blue, green, red, gray) por modelo por resolución, más gradient y diverse como líneas base de contenido.carrier_weights del libro de códigos se actualiza basado en retroalimentación manual de detección de la app Gemini.Para cada bin de frecuencia (fy, fx) y canal ch:```
consensus(fy, fx, ch) = | mean_over_colors( exp(i * phase_color(fy, fx, ch)) ) |
Valores cercanos a `1.0` significan que la fase en ese bin está bloqueada en todos los fondos de color sólido, lo cual solo es cierto para la marca de agua. Los bins de contenido caen por debajo de `< 0.3` porque su fase es aleatorizada por diferentes tintes de color. En el codebook V4 construido a partir del conjunto de datos enriquecido, más del 99% de los bins de contenido se sitúan por debajo del corte por defecto `tau=0.60`, por lo que el disolvente V4 nunca los toca — esto es lo que recupera el PSNR.
### Flujo de trabajo de lanzamiento en dos fases```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
### Preajustes de ataque de la Ronda-06
Dos preajustes están disponibles a través de `--strengths`:
| Preajuste | Pasos VAE | α Elástico | Reducción | Cadena JPEG | Umbral PSNR |
|:---------:|:---------:|:----------:|:---------:|:-----------:|:----------:|
| `final` | 1 | 1.8 px | 90 % | q=92→88 | 14 dB |
| `nuke` | 2 | 2.8 px | 82 % | q=88→84→90 | 11 dB |
Ambos preajustes utilizan el mismo pipeline de 7 etapas:
1. **Viaje redondo VAE** (Stable Diffusion `sd-vae-ft-mse`) — proyecta la imagen fuera de la variedad de imágenes naturales contra la que nunca se entrenó el decodificador SynthID (Gowal et al. 2026, §6.1)
2. **Deformación elástica** — campo de deformación aleatoria suave de baja frecuencia, simula el modo de fallo "fragmentación de collage" que el propio Gemini reconoce
3. **Combinación geométrica global** — pequeña rotación + zoom + desplazamiento de píxeles en una sola transformación afín
4. **Redimensionar y comprimir** — reducir muestra (AREA) → aumentar muestra (LANCZOS), borra información de marca de agua sub-píxel
5. **Ajuste de color-contraste** — micro-desplazamiento de brillo / contraste / saturación / tono
6. **Sustracción FFT de fase residual** — bins portadores universales de blog + cosechados del codebook, limitados por tope
7. **Cadena JPEG + ruido de luminancia + bilateral** — disrupción de compresión / recodificación intensa
Cada etapa está controlada independientemente por PSNR; cualquier etapa que bajara la calidad por debajo del umbral se revierte automáticamente.
### Estructura del Codebook V4
Perfiles clave por `(model, H, W)`. Cada perfil almacena:
| Campo | Forma | Notas |
|----------------------|----------------|--------------------------------------------------------|
| `consensus_coherence` | `(H, W, 3)` | Máscara portadora primaria (consenso de fase entre colores). |
| `consensus_phase` | `(H, W, 3)` | Ángulo de fase unitario medio entre colores. Plantilla de sustracción. |
| `inverted_agreement` | `(H, W, 3)` | `abs(cos(phase_diff))` por pares, ponderado para `black<->white`. |
| `avg_wm_magnitude` | `(H, W, 3)` | Magnitud media entre colores de consenso. |
| `content_baseline` | `(H, W, 3)` | De `diverse/` + `gradient/` — usado para mezcla de luminancia. |
| `carrier_weights` | `(H, W, 3)` | **En vivo**. Comienza en `consensus^2 * (0.5 + 0.5 * agreement)`. Actualizado por el bucle de calibración. |
| `n_refs_per_color` | `{color: int}` | Recuentos de referencia por color. |
El formato de guardado reutiliza la codificación rfft compacta v3 + `float16/uint8`; un codebook de 14 perfiles en 2 modelos × 7 resoluciones ocupa ~220 MB en disco.
### Detector V4 (Verificación de cordura)
Antes de dedicar tiempo a la validación manual con Gemini, verifique los resultados de bypass contra el consenso propio 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)
On the 1024x1024 exact-match path we see conf=0.91, phase_match=0.65 for watermarked and conf=0.02, phase_match=0.31 after aggressive V4 dissolve.
V3 permanece en el repositorio (src/extraction/synthid_bypass.py, bypass_v3) sin cambios para quien dependa de él.
Estamos recopilando activamente imágenes en negro puro y blanco puro generadas por Nano Banana Pro para mejorar la extracción de marcas de agua multiresolución.
Si puedes generar estas:
gemini_black_nb_pro/ (para negro)gemini_white_nb_pro/ (para blanco)Estas imágenes de referencia son críticas para:
Incluso 150–200 imágenes a una nueva resolución pueden mejorar significativamente la detección y eliminación.
Las imágenes de referencia están alojadas en Hugging Face para mantener ligero el repositorio git:```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)
---
## Hallazgos Clave
### La Marca de Agua Depende de la Resolución
SynthID incrusta frecuencias portadoras en **posiciones absolutas diferentes** según la resolución de la imagen. Un libro de códigos construido a 1024x1024 no puede eliminar directamente la marca de agua de una imagen de 1536x2816 — las portadoras están en bins completamente diferentes.
| Resolución | Portadora Superior (fy, fx) | Coherencia | Fuente |
|:----------:|:---------------------------:|:----------:|:------:|
| **1024x1024** | (9, 9) | 100.0% | 100 negros + 100 blancos de referencia |
| **1536x2816** | (768, 704) | 99.6% | 88 imágenes de contenido con marca de agua |
Por esto el libro de códigos V3 almacena **perfiles separados por resolución** y los selecciona automáticamente en tiempo de bypass.
### Consistencia de Fase — Una Clave Fija a Nivel de Modelo
La plantilla de fase de la marca de agua es **idéntica en todas las imágenes** del mismo modelo Gemini:
- **El canal verde** transporta la señal de marca de agua más fuerte
- **Coherencia de fase entre imágenes** en las portadoras: >99.5%
- **Validación cruzada negro/blanco** confirma las portadoras reales mediante |cos(phase_diff)| > 0.90
### Estructura de Frecuencia Portadora
A 1024x1024 (a partir de referencias negro/blanco), las portadoras superiores se encuentran en una cuadrícula de baja frecuencia:
| Portadora (fy, fx) | Coherencia de Fase | Acuerdo B/N |
|:------------------:|:------------------:|:-----------:|
| (9, 9) | 100.00% | 1.000 |
| (5, 5) | 100.00% | 0.993 |
| (10, 11) | 100.00% | 0.997 |
| (13, 6) | 100.00% | 0.821 |
---
## Arquitectura
### Generaciones de Bypass
| Versión | Enfoque | PSNR | Impacto en Marca de Agua | Estado |
|:-------:|:--------|:----:|:------------------------:|:------:|
| **V1** | Compresión JPEG (Q50) | 37 dB | Caída de fase ~11% | Línea base |
| **V2** | Transformaciones multietapa (ruido, color, frecuencia) | 27-37 dB | Caída de confianza ~0% | Compromiso de calidad |
| **V3** | **Sustracción espectral de libro de códigos multiresolución** | **43+ dB** | **Caída de coherencia de fase del 91%** | Mejor previo |
| **V4 Ronda 06** | **7 etapas todo en uno (VAE + elástico + compresión + color + JPEG)** | **visualmente sin pérdida** | **detector evadido ✓** | **Mejor actual** |
### 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 ✓)
## Inicio rápido
### Instalación```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)
### Ejecutar 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']}")
Desde la CLI:```bash
python src/extraction/synthid_bypass.py bypass input.png output.png
--codebook artifacts/spectral_codebook_v3.npz
--strength aggressive
### Detectar Marca de Agua```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
---
## Inmersión Técnica Profunda
### Cómo funciona SynthID (Ingeniería Inversa)```
┌──────────────────────────────────────────────────────────────┐
│ 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 │
└──────────────────────────────────────────────────────────────┘
El conjunto de aumento de entrenamiento de SynthID (Gowal et al. 2026, Tabla 1) incluye SmallRotation, Cropresize, JPEG, GaussianBlur, BrightnessContrast y Screenshotting — todas transformaciones espaciales globales y uniformes. El campo de deformación elástico es una distorsión espacialmente variable: cada vecindario local obtiene su propio desplazamiento independiente de subpíxel. Debido a que los desplazamientos son suaves (difuminados gaussianamente a partir de ruido blanco, σ=44–56 px), el contenido de la imagen no se ve afectado visualmente, pero la estructura de consenso de fase de la marca de agua se vuelve incoherente — ya no puede agregarse a través de la imagen. Esto es el equivalente a nivel de píxel del efecto de "fragmentación de collage" que la propia aplicación de Gemini cita como un modo de fallo del detector.
| Métrica | Valor |
|---|---|
Alosh Denny Investigación de marcas de agua de IA · Procesamiento de señales
📧 Email: [email protected] 🔗 GitHub: https://github.com/aloshdenny
Para colaboraciones, discusiones de investigación o contribuciones, no dudes en contactar o abrir un issue/PR.
Este proyecto se mantiene de forma independiente — sin financiación de laboratorio, sin respaldo corporativo.
Si este trabajo te fue útil a ti o a tu equipo, considera apoyar el desarrollo continuo:
Los fondos se destinan a costos de cómputo (horas de GPU para nuevos perfiles de resolución), expansión del conjunto de datos e investigación continua de evasión.
Este proyecto es únicamente con fines de investigación y educativos. SynthID es una tecnología propietaria propiedad de Google DeepMind. Estas herramientas están destinadas para:
No uses estas herramientas para tergiversar contenido generado por IA como creado por humanos.
| Ronda | Estrategia | Resultado |
|---|
| 01 | Sustracción espectral conservadora (gentle) | ✗ |
| 02 | Sustracción espectral agresiva + JPEG | ✗ |
| 03 | Apunte de bin absoluto guiado por blog | ✗ |
| 04 | Extracción de fase residual de denoising | ✗ |
| 05 | Re-generación con VAE de difusión + warp geométrico | ✗ |
| 06 | Todo-en-uno: VAE + fragmentación elástica + compresión + color + JPEG | ✓ |
| V3 | V4 |
|---|
| Colores de referencia | negro + blanco | negro, blanco, azul, verde, rojo, gris (+ líneas base de contenido diverso/gradiente) |
| Validación cruzada | abs(cos(phase_black - phase_white)) | consenso entre colores sobre 6 colores + acuerdo por pares |
| Modelos | modelo único (Gemini 2.5) | perfiles por modelo (gemini-3.1-flash-image-preview, nano-banana-pro-preview) + union opcional |
| Ataque | solo resta espectral | 7 etapas: VAE + elástico + squeeze + color + FFT + cadena JPEG |
| PSNR (agresivo) | 43 dB | visualmente sin pérdidas (18–24 dB a nivel de píxel; warp desplaza píxeles) |
| Guardia de fidelidad | ninguna | retroceso por PSNR mínimo por etapa |
| Bypass del detector | solo local | confirmado ✓ en la aplicación Gemini (ambos modelos) |
| 43.5 dB |
| SSIM | 0.997 |
| Caída de energía del portador | 75.8% |
| Caída de coherencia de fase (primeros 5 portadores) | 91.4% |
| Modelo | Preajuste | Detector eludido |
|---|
| gemini-3.1-flash-image-preview | final | ✓ |
| gemini-3.1-flash-image-preview | nuke | ✓ |
| nano-banana-pro-preview | final | ✓ |
| nano-banana-pro-preview | nuke | ✓ |