Blindly subtracting the codebook's carrier pattern from an image that
never had the watermark would imprint one instead of removing it. Now
each image is run through RobustSynthIDExtractor first, and the V3
bypass only runs when a watermark is actually detected; otherwise the
image passes through untouched (still eligible for metadata stripping).
Small Tkinter app: drop images in, get SynthID-cleaned copies out,
with an option to strip EXIF/XMP/IPTC metadata from the output.
Runs offline on the existing V3 codebook, no PyTorch required.
- Add 'What the Watermark Looks Like' section with synthid_white.jpg
- Add 'Round 06 — It Works' section with fidelity comparison image and
a table of all six attack rounds
- Document the 7-stage all-in-one pipeline and elastic deformation rationale
- Add Round-06 preset table (final vs nuke) and updated pipeline diagram
- Update architecture table and results summary to reflect confirmed bypass
- Remove all CSV file references
Made-with: Cursor
New module vae_regen.py wraps stabilityai/sd-vae-ft-mse for image
round-trips, exploiting the regeneration-attack weakness conceded in
Gowal et al. 2026 §6.1. Supports MPS, CUDA, and CPU with tiled
encode/decode for high-resolution images.
Made-with: Cursor
Introduces SpectralCodebookV4 and SynthIDBypassV4 with bypass_v4,
bypass_v4_universal, bypass_v4_regen (Round 05), and bypass_v4_final /
bypass_v4_nuke (Round 06). The Round-06 7-stage pipeline (VAE +
elastic deformation + resize-squeeze + color nudge + residual FFT +
JPEG chain) defeats the SynthID detector on both gemini-3.1 and
nano-banana-pro. Includes FINAL_PRESETS, REGEN_PRESETS, and
_elastic_deform / _resize_squeeze / _color_nudge helpers.
SpectralCodebookV4 save/load uses format_version 5: LZMA compression,
int8 phase, uint8 mag/cw, sparse-zeroed below cons<0.55 — reduces a
221 MB codebook to ~24 MB with no bypass-relevant information loss.
Also updates RobustSynthIDExtractor with a detect_from_v4_codebook hook.
Made-with: Cursor
The hardcoded carrier positions (48,0), (96,0), (0,88) etc. had low phase
coherence on actual Gemini images (~0.16-0.55). Detection was 80% on
reference images with 100% false positive rate on non-watermarked images.
Root cause analysis across 291 watermarked + 16 non-watermarked images
revealed:
1. The watermark is content-adaptive — dark images use diagonal-grid
carriers at (±3,±4), (±5,±3) etc. while white images use horizontal-
axis carriers at (0,±7), (0,±8), (0,±9) etc.
2. Both sets have >0.95 intra-set phase coherence and >0.5 discriminative
gap vs non-watermarked images.
3. Previous metrics (noise correlation, structure ratio bounds, raw carrier
magnitude) had heavy overlap between watermarked and non-watermarked
content images and were not discriminative.
Changes:
- Replace carrier list with empirically verified dark + white carrier sets
- Add per-set reference phase templates to codebook (carrier_refs)
- Rewrite detect_array to try both carrier sets and take best phase match
- Use phase agreement as primary signal (WM: 0.92-0.99 vs non-WM: 0.47-0.71)
- Add noise-domain carrier-vs-random ratio as supporting signal
- Skip expensive multi-scale consistency computation (phase match is decisive)
Results on full dataset:
- Watermarked: 99.0% detection (was ~80%)
- Non-watermarked: 0% false positives (was 100%)
- Overall: 98.7% accuracy (was ~80% with no FP testing)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Images are now hosted at https://huggingface.co/datasets/aoxo/reverse-synthid
to keep the git repo lightweight (~1.3GB of images removed).
Changes:
- Remove gemini_black/, gemini_white/, gemini_random/, gemini_*_nb_pro/ from git
- Add these folders to .gitignore
- Add scripts/download_images.py to fetch images from HF
- Update README: contribution guide points to HF dataset, add download instructions
Relates to #15
The extractor's load_codebook() was called with the .npz bypass codebook
path, but it only handles .pkl files. pickle.load() on an .npz file throws
a cryptic "persistent IDs" error, causing the extractor to silently fail.
This meant users got no before/after watermark verification during bypass.
Changes:
- load_codebook() now auto-discovers the .pkl codebook when given a .npz path
- Pickle save now uses protocol=4 for wider Python version compatibility
Fixes#10, #9, #11
Generated via Gemini Pro web UI with "Create image" tool enabled,
uploading a pure black (#000000) 1024x1024 image and prompting
"recreate this as it is". All images verified to contain SynthID
watermarks (confidence 0.52-0.67, carrier strength ~9300-9900).
These reference images are critical for carrier frequency discovery,
phase validation, and improving cross-resolution robustness.
4 test images generated via Gemini API with real content:
- 2x 9:16 (cat, mountain) at 1344x768
- 2x 4:3 (coffee, city) at 864x1184
V3 bypass results with expanded codebook:
| Image | Resolution | PSNR | SSIM | Exact Match |
| city (4:3) | 864x1184 | 45.7 dB | 0.9972 | yes |
| coffee (4:3) | 864x1184 | 50.0 dB | 0.9981 | yes |
| cat (9:16) | 1344x768 | 50.2 dB | 0.9978 | yes |
| mountain (9:16)| 1344x768 | 49.1 dB | 0.9971 | yes |
PSNR/SSIM are excellent. Phase coherence drop is near-zero, suggesting
the API-generated images may have weaker watermark embedding than
web-UI outputs, or the carrier extraction needs further tuning.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extends spectral_codebook_v3 from 2 to 4 resolution profiles:
- 1024x1024 (existing, 100b+100w refs)
- 1536x2816 (existing, 88 watermarked refs)
- 1344x768 (new, 154b+364w refs, top carrier coherence 0.946)
- 864x1184 (new, 268b+295w refs, top carrier coherence 0.979)
Key findings at new resolutions:
- 1344x768 carriers sit on the vertical axis (fy=0, fx=3..10)
- 864x1184 carriers are at mid-frequency diagonals (13,-16), (23,-24)
- Both show distinct carrier structures vs existing profiles,
confirming resolution-dependent embedding
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
268 black + 281 white pure-color reference images at 864x1184 (4:3
landscape aspect ratio). Generated via gemini-2.5-flash-image model.
This complements the 9:16 portrait images and covers the classic photo
aspect ratio commonly used in Gemini outputs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
154 black + 359 white pure-color reference images at 1344x768 (9:16
portrait aspect ratio). Generated via gemini-2.5-flash-image model.
This resolution is not covered by the existing codebook and is one of
the most common mobile Gemini output formats.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
generate_references.py automates generating pure-black and pure-white
reference images via the Gemini API at multiple aspect ratios (9:16, 4:3).
Includes rate-limit retry logic and per-resolution output directories.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. bypass_v2() ignores iterations parameter — the function accepted
`iterations` but ran the transform pipeline only once. Now properly
loops, with diminishing strength on subsequent iterations.
2. denoise_bilateral() has identical if/else branches — both 2D and 3D
cases called the same cv2.bilateralFilter(). Removed dead branch.
3. apply_noise_replacement() allows negative sigma — with passes > 5,
the formula `sigma * (1 - i * 0.2)` produces negative values. Added
clamping and early break.
4. Broken import paths — synthid_bypass.py and watermark_remover.py
used bare module imports that fail when scripts are run from outside
their directory. Added sys.path.insert like benchmark_extraction.py.
5. Misleading "Python 3.14 bug" comment — the SSIM gate was disabled
with a comment blaming Python 3.14, but the real reason is that
heavy multi-pass transforms naturally depress SSIM. Updated comment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>