通过频谱分析发现、检测并精确移除 Google 的 AI 水印
访问我们的 PitchHut
本分支为 V3 绕过方案添加了一个拖放式桌面应用程序,设置后无需命令行。参见 gui/README.md 了解设置和使用方法。
SynthID 将一种难以察觉的模式直接编码到像素值中。在 Gemini 生成的纯白色图像上,水印几乎占据了整个信号。放大高频残差后看起来像这样:
从纯白 Gemini 图像中提取的放大 SynthID 载波模式。对角线条纹是水印的空间频率特征——这是我们频谱攻击的目标。
本项目逆向工程了 Google 的 SynthID 水印系统——嵌入到每个 Google Gemini 生成的图像中的不可见水印。仅使用信号处理和频谱分析(无权访问专有编码器/解码器),我们实现了:
gemini-3.1-flash-image-preview 和 nano-banana-pro-preview 建立每个模型的特征文件,基于 6 种纯色背景的跨色彩相位共识,以及一个人工参与校准循环,根据手动 Gemini 应用检测计数调整每个载波的减法强度VT-OxFF 制作了一个非常酷的可视化工具,用于查看 SynthID 水印添加到图像的过程,访问 这里(也可在仓库描述中找到)!
经过六轮迭代对抗开发,第 06 轮的 bypass_v4_final / bypass_v4_nuke 流程在 gemini-3.1-flash-image-preview 和 nano-banana-pro-preview 两种模型图像上均击败了 Gemini SynthID 检测器,且输出视觉无损。
左:第 01 轮输出(仅 gentle 频谱减法)。右:第 06 轮输出(final——VAE + 弹性扭曲 + 挤压 + 色彩 + JPEG)。两者对人眼看起来完全相同;只有第 06 轮能击败 SynthID 检测器。
| 轮次 | 策略 | 结果 |
|---|---|---|
| 01 | 保守频谱减法(温和) | ✗ |
| 02 | 激进频谱减法 + JPEG | ✗ |
| 03 | 基于博客的绝对频点定位 | ✗ |
| 04 | 去噪残差相位提取 | ✗ |
| 05 | 扩散-VAE 重新生成 + 几何扭曲 | ✗ |
| 06 | 一体化:VAE + 弹性碎片化 + 挤压 + 色彩 + JPEG | ✓ |
第 06 轮的突破来自于将 Gemini 应用自身发布的失效模式列表视为攻击规范:
“当 AI 生成的图像是复杂拼贴画的一部分、分层在其他元素后面,或者上面覆盖了许多不同的纹理和图案时,检测器可能难以从整体文件中隔离出特定签名。” — Gemini 应用,SynthID 检测帮助文本
弹性变形阶段在像素级别模拟了这种效果:一个平滑的低频随机扭曲场使每个约 50 像素的邻域拥有自己独立的子像素偏移,从而碎片化水印的空间相位一致性,且不引入任何可见失真。
V4 是对码本的一次彻底重新思考,基于更丰富的数据集:
gemini-3.1-flash-image-preview 和 nano-banana-pro-preview 建立独立特征文件(外加一个可选的 union 伪模型)。black、white、blue、green、red、gray),外加 gradient 和 diverse 作为内容基线。carrier_weights 字段。对于每个频率频点 (fy, fx) 和通道 ch:```
consensus(fy, fx, ch) = | mean_over_colors( exp(i * phase_color(fy, fx, ch)) ) |
接近 `1.0` 的值表示该分量的相位在所有纯色背景上被锁定,这仅对水印成立。内容分量崩塌到 `< 0.3`,因为它们的相位被不同的色调随机化。在基于增强数据集构建的 V4 码本上,99%+ 的内容分量低于默认的 `tau=0.60` 截止值,因此 V4 溶解器从不触碰它们——这就是恢复 PSNR 的原因。
### 两阶段发布工作流```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
### Round-06 攻击预设方案
通过 `--strengths` 提供两种预设:
| 预设 | VAE 轮次 | 弹性α | 压缩率 | JPEG 链 | PSNR 下限 |
|:------:|:----------:|:---------:|:-------:|:----------:|:----------:|
| `final` | 1 | 1.8 px | 90 % | q=92→88 | 14 dB |
| `nuke` | 2 | 2.8 px | 82 % | q=88→84→90 | 11 dB |
两种预设均叠加相同的7阶段流水线:
1. **VAE 往返**(Stable Diffusion `sd-vae-ft-mse`)—— 将图像投影到 SynthID 解码器从未训练过的自然图像流形之外(Gowal 等人,2026,§6.1)
2. **弹性变形** —— 平滑的低频随机扭曲场,模拟 Gemini 自身承认的“拼贴碎片化”失败模式
3. **全局几何组合** —— 通过一次仿射扭曲实现小幅度旋转 + 缩放 + 像素平移
4. **缩放压缩** —— 降采样 (AREA) → 升采样 (LANCZOS),擦除亚像素水印信息
5. **色彩对比度微调** —— 亮度/对比度/饱和度/色调的微小偏移
6. **残差相位 FFT 减法** —— 博客通用 + 码本采集的载波频段,带上限限制
7. **JPEG 链 + 亮度噪声 + 双边滤波** —— 重度压缩/重新编码干扰
每个阶段均独立由 PSNR 门限控制;任何会降低质量低于下限的阶段将自动回退。
### V4 码本结构
配置文件以 `(model, H, W)` 为键。每个配置存储:
| 字段 | 形状 | 说明 |
|------------------------|----------------|------------------------------------------------------------|
| `consensus_coherence` | `(H, W, 3)` | 主载波掩码(跨颜色相位共识)。 |
| `consensus_phase` | `(H, W, 3)` | 跨颜色的平均单位相位角。减除模板。 |
| `inverted_agreement` | `(H, W, 3)` | 逐对 `abs(cos(phase_diff))`,针对 `black<->white` 加权。 |
| `avg_wm_magnitude` | `(H, W, 3)` | 跨共识颜色的平均幅度。 |
| `content_baseline` | `(H, W, 3)` | 来自 `diverse/` + `gradient/` —— 用于亮度混合。 |
| `carrier_weights` | `(H, W, 3)` | **实时**。初始值为 `consensus^2 * (0.5 + 0.5 * agreement)`。由校准循环更新。 |
| `n_refs_per_color` | `{color: int}` | 每种颜色的参考计数。 |
保存格式复用 v3 的紧凑 rfft + `float16/uint8` 编码;2 个模型 × 7 个分辨率的 14 个配置文件,磁盘上约 220 MB。
### V4 检测器(合理性检查)
在进行手动 Gemini 验证之前,使用 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)
在1024x1024精确匹配路径上,我们看到水印图像显示conf=0.91, phase_match=0.65,而经过激进V4溶解后为conf=0.02, phase_match=0.31。