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>