This repo now contains two detectors:
ai_detector/— learned, generator-agnostic (recommended). A CLIP + linear probe that answers "is this AI-generated?" for one image and generalizes to generators it never saw (held-out AUC ~0.99). Seeai_detector/README.md.detect.py— watermark-template matcher (research/broken). The original idea below. It does not work on new images — keep reading.
Validation (leave-one-out and a 110-image held-out test) showed
detect.pyonly scores high on the exact images used to build each template — self-correlation. On a new AI image it returns ~0, the same as a real photo: it detected 0 of 33 held-out AI images. The high scores in the demo come entirely from testing on the template's own training images. Useai_detector/for real detection, or Google's SynthID Detector / OpenAI Verify for authoritative watermark reads.
Detects invisible watermarks in AI-generated images from:
- Google Gemini Nano Banana Pro (SynthID)
- Google Gemini Nano Banana 2 (SynthID)
- OpenAI GPT-Image2
Does NOT rely on metadata or EXIF — works on screenshots, re-saved images, and images with stripped metadata. Caveat (measured): only matches images already in its templates; see the notice above.
Watermark keys change with model releases, so each template only matches the model version it was built from.
Even for the models below, "covered" only means the template was built from that
model — it does not mean new images are detected (see the
| Model | API ID | Template built from it? |
|---|---|---|
| Nano Banana Pro | gemini-3-pro-image |
yes (but no held-out detection) |
| Nano Banana 2 | gemini-3.1-flash-image |
yes (but no held-out detection) |
| GPT-Image2 | gpt-image-2 |
yes, pre–May 19 2026 images (OpenAI added SynthID + C2PA after) |
| Nano Banana 2 Lite | gemini-3.1-flash-lite-image |
no (released June 30, 2026) |
| Nano Banana (legacy) | gemini-2.5-flash-image |
no |
Each AI image model embeds a fixed noise-level watermark pattern in every generated image. This pattern is imperceptible to humans but statistically consistent across all images from the same model.
- Build phase: Average the noise residuals of many AI-generated images from the same source. Random content cancels out; the fixed watermark signal reinforces.
- Detect phase: Extract noise residual from a test image (subtract Gaussian-denoised version), then compute normalized correlation with known templates. High correlation = watermark present.
Based on research from reverse-SynthID.
pip install -r requirements.txtYou need 10+ images per AI source. More images = better template.
python detect.py build \
--synthid path/to/nano_banana_pro_images/ \
--gptimage path/to/gpt_image2_images/ \
--nb2 path/to/nano_banana_2_images/# Basic detection
python detect.py check image.jpg
# Verbose output with all scores
python detect.py check -v image.jpg
# Multiple images
python detect.py check -v img1.png img2.jpg img3.webp
# JSON output
python detect.py check --json image.jpg
# Custom threshold (default: 0.08)
python detect.py check -t 0.05 image.jpgimage.jpg: WATERMARK DETECTED - synthid (score=0.283)
photo.png: no watermark detected
Exit codes:
0— no watermark detected1— file error2— SynthID detected (Nano Banana Pro)3— GPT-Image2 detected4— Nano Banana 2 detected
- ~46ms per image (CPU only, no GPU needed)
- Tested accuracy: AI images score 0.15-0.55, real photos score < 0.01
- Dependencies: numpy, Pillow, scipy
| Transformation | Survives? |
|---|---|
| JPEG compression | Yes |
| Resize | Yes (resized to 512x512 internally) |
| Screenshot (no crop) | Yes |
| Format conversion | Yes |
| Metadata stripping | Yes |
| Crop | No |
| Rotation | No |
- Not 100% accurate — false negatives can occur on heavily compressed images.
- Heavy JPEG compression kills the watermark — if the image file is unusually small (e.g. 35KB for a 1024x1024 image), the watermark signal may be too degraded to detect. Check the file size before trusting a "no watermark" result.
- Only detects covered model versions — each AI model version uses a different watermark key. Templates built from May 2026 images only work for those versions. Older Nano Banana versions (original, 1.5, early Pro) and newer models like Nano Banana 2 Lite (released June 30, 2026) will NOT be detected. See the coverage table above.
- GPT-Image2 changed after templates were built — on May 19, 2026 OpenAI started embedding Google SynthID + C2PA manifests in every ChatGPT/API image. The
gptimage2template was built from earlier images; detection of post-change images is unverified. - Third-party platforms may strip watermarks — images generated via API proxies or shared through social platforms (QQ, WeChat, Twitter, etc.) may have their watermark destroyed by re-processing, re-compression, or resizing.
- Requires 10+ reference images per AI source to build templates
- Different model versions may need separate templates
- Template quality improves with more reference images (50+ ideal)
- Threshold may need tuning per deployment
Templates go stale whenever a model version ships with a new watermark key. To refresh or add coverage:
- Collect 10+ reference images from the target model. Options:
python gen_reference.py --model gemini-3.1-flash-lite-image --outdir refs/nb2lite --count 16— requires a billed Gemini API key inGOOGLE_API_KEY(the free tier has zero quota for image-generation models)- Download full-resolution originals from a model-labeled gallery (e.g. opennana.com — the source of the current templates)
- Build the template:
python detect.py build --source nb2lite=refs/nb2lite(the--source NAME=FOLDERflag accepts any new model name; repeatable) - Export for the web UI:
python export_web.py(regeneratesdocs/*.bin+docs/templates.json) - Validate: held-out images from the same model should score > 0.1; real photos and other models' images should stay < 0.02
For authoritative checks, prefer the vendors' own detectors — they read the actual watermark payload rather than a statistical fingerprint:
- Google SynthID Detector — portal for images/audio/video/text (early access via waitlist); SynthID verification is also built into the Gemini app, Chrome, and Google Search
- OpenAI Verify — checks C2PA manifests and SynthID watermarks in OpenAI-generated images (all OpenAI images carry both since May 19, 2026)
This tool remains useful for batch/offline checking, when those services are unavailable, and for images whose metadata was stripped.
This is an experimental research tool. It should NOT be used as the sole basis for legal decisions, journalism fact-checking, or content moderation. False positives and false negatives can occur. Always verify results with multiple methods.
- Build a Nano Banana 2 Lite template (blocked: needs a billed Gemini API key, or a Lite-labeled image source — opennana.com has no Lite category yet)
- Re-validate the
gptimage2template against post–May 19, 2026 GPT-Image2 outputs (OpenAI added SynthID + C2PA) - Build stronger templates with 200+ images per model (currently 12-14)
- Test against 50+ diverse real photos (phone cameras, DSLR, memes, screenshots) to validate false positive rate
- Evaluate other generators for fixed watermark patterns (Grok Imagine, Seedance 2.0, Kakao — the latter adopted SynthID in 2026)
- Investigate frequency-domain carrier detection for crop robustness
detect.py— main detector (build + check)gen_reference.py— generate reference images via the Gemini API (for template building)export_web.py— exporttemplates/*.npyto the web UI format indocs/templates/— extracted watermark templates (.npy files)docs/— client-side web UI (GitHub Pages) with quantized templatesrequirements.txt— Python dependenciesgemini-synthid.jpg— reference fingerprint visualizationgptimage2-fingerprint.jpg— reference fingerprint visualization