GitHub 上出现了开源项目 Remove–AI–Watermarks,提供命令行工具与库两种形式,可自动识别并移除图像中由 AI 生成的隐藏水印。该项目旨在提供便捷的技术方案,以应对 AI 内容标识带来的编辑与再利用限制,目前在开发者社区已获得较高关注。
原文 · 未翻译
Remove-AI-Watermarks
Remove visible and invisible AI watermarks from images generated by Google Gemini (Nano Banana), ChatGPT / DALL-E, Stable Diffusion, Adobe Firefly, Midjourney, and other AI models.
Strips SynthID, C2PA Content Credentials, EXIF/XMP "Made with AI" labels, and visible sparkle overlays — all in one command.
Try it online: raiw.cc No Python, no GPU, no setup. Visible-watermark and metadata removal are free. Invisible-watermark removal (SynthID / SDXL regeneration) normally needs a local GPU and ~2 GB of models. On raiw.cc it runs on cloud GPUs in one click for a small per-image fee.
Try it online: raiw.cc
No Python, no GPU, no setup. Visible-watermark and metadata removal are free. Invisible-watermark removal (SynthID / SDXL regeneration) normally needs a local GPU and ~2 GB of models. On raiw.cc it runs on cloud GPUs in one click for a small per-image fee.
If this tool saves you time, consider sponsoring its development.
Intended for lawful use only. Publishing and running this software is lawful; responsibility for any downstream use, and for compliance with local law, rests entirely with the user. Some jurisdictions restrict removing an AI label as such (see Legal). The authors do not condone use for deception, fraud, or any unlawful activity.
Intended for lawful use only. Publishing and running this software is lawful; responsibility for any downstream use, and for compliance with local law, rests entirely with the user. Some jurisdictions restrict removing an AI label as such (see Legal). The authors do not condone use for deception, fraud, or any unlawful activity.
Scope
This tool removes AI-provenance watermarks that a platform stamps onto content you generated yourself — SynthID, the Gemini / Nano Banana sparkle, the Doubao / Jimeng / Samsung visible AI labels, the Chinese TC260 "由…AI生成" label, and C2PA / IPTC / EXIF "Made with AI" metadata. The point is your autonomy over your own output.
It does not target watermarks that protect someone else's paid or copyrighted content — stock-agency overlays (Shutterstock, Getty, iStock, Adobe Stock), classifieds-site marks, or any tiled "preview" watermark whose job is to gate a purchase. Removing those is out of scope by design. erase is a generic, user-driven region tool for your own objects, not an automatic stock-watermark remover.
erase
Features
Visible watermark removal — a registry of known marks in their usual places: the Gemini / Nano Banana sparkle, the Doubao "豆包AI生成" text strip, the Jimeng "★ 即梦AI" wordmark, and the Samsung Galaxy AI "✦ Contenuti generati dall'AI" strip (bottom-left, locale-specific). Each is removed by reverse-alpha blending against a captured alpha map (original = (wm − α·logo)/(1−α)), recovering the true pixels rather than inpainting a guess. The Gemini sparkle recovers cleanly on its own on bright backgrounds; it adapts the alpha to each image's sparkle opacity, so a more-opaque-than-captured sparkle is still fully removed (and on a dark background, where the fixed alpha would over-subtract and leave a dark spot, it automatically inpaints the small sparkle footprint instead); the Doubao, Jimeng, and Samsung text marks re-rasterize slightly per image, so a thin residual inpaint over the glyph footprint clears the leftover edges (the alpha maps are reproducibly rebuilt from controlled captures by scripts/visible_alpha_solve.py). Fast, offline, no GPU. visible --mark auto finds and removes the strongest detected mark. (For arbitrary logos/objects, see erase.)
GitHub 上出现了开源项目 Remove–AI–Watermarks,提供命令行工具与库两种形式,可自动识别并移除图像中由 AI 生成的隐藏水印。该项目旨在提供便捷的技术方案,以应对 AI 内容标识带来的编辑与再利用限制,目前在开发者社区已获得较高关注。
原文 · 保持原样,未翻译
Remove-AI-Watermarks
Remove visible and invisible AI watermarks from images generated by Google Gemini (Nano Banana), ChatGPT / DALL-E, Stable Diffusion, Adobe Firefly, Midjourney, and other AI models.
Strips SynthID, C2PA Content Credentials, EXIF/XMP "Made with AI" labels, and visible sparkle overlays — all in one command.
Try it online: raiw.cc No Python, no GPU, no setup. Visible-watermark and metadata removal are free. Invisible-watermark removal (SynthID / SDXL regeneration) normally needs a local GPU and ~2 GB of models. On raiw.cc it runs on cloud GPUs in one click for a small per-image fee.
Try it online: raiw.cc
No Python, no GPU, no setup. Visible-watermark and metadata removal are free. Invisible-watermark removal (SynthID / SDXL regeneration) normally needs a local GPU and ~2 GB of models. On raiw.cc it runs on cloud GPUs in one click for a small per-image fee.
original = (wm − α·logo)/(1−α)
scripts/visible_alpha_solve.py
visible --mark auto
erase
Universal region eraser (erase) — remove any logo / watermark / object inside boxes you specify, regardless of position or colour. Default cv2 inpainting (CPU, instant); optional big-LaMa via onnxruntime (lama extra) for higher quality
erase
lama
Invisible watermark removal — SynthID, StableSignature, TreeRing via diffusion-based regeneration (needs a local GPU, or run it with no setup on raiw.cc)
AI metadata stripping — EXIF, PNG text chunks, C2PA provenance manifests (PNG / JPEG / AVIF / HEIF / JPEG-XL, MP4 / MOV / M4V / M4A at the container level, and WebM / MP3 / WAV / FLAC / OGG losslessly via ffmpeg), XMP DigitalSourceType
"Made with AI" label removal — removes the AI-disclosure metadata that platforms read to apply automatic labels (useful for clearing a false-positive label from a human-edited photograph)
Analog Humanizer — optional film grain and chromatic aberration post-processing
Text and face preservation (default) — the default pipeline is a canny ControlNet that keeps text and face structure sharp through the removal pass (without copying original pixels, so SynthID is still removed). Use --pipeline sdxl for plain SDXL img2img (lighter, no extra model download) on inputs without text or faces. Canny preserves face structure, not identity (the regenerated face drifts in likeness). The library does not ship a face-restore extra: every approach evaluated (GFPGAN-on-cleaned, PhotoMaker-V2, InstantID txt2img, InstantID img2img-on-cleaned) regenerated the face via SDXL and made the output look more AI-generated than the cleaned image. The cleaned controlnet output is the least-AI face state achievable without re-introducing SynthID.
--pipeline sdxl
Batch processing — process entire directories
Detection — three-stage NCC watermark detection with confidence scoring
Provenance detection (identify) — aggregate C2PA issuer, the C2PA soft-binding forensic-watermark vendor (Adobe TrustMark, Digimarc, Imatag, ...), IPTC "Made with AI" plus the IPTC 2025.1 AISystemUsed field, embedded SD/ComfyUI params, EXIF/XMP generator tags, the xAI/Grok EXIF signature, the China TC260 AIGC label (XMP, PNG chunk, EXIF, or JPEG segment), the HuggingFace hf-job-id job marker, the SynthID metadata proxy, the C2PA cloud-manifest reference (Adobe Durable Content Credentials, when the embedded manifest is stripped), the visible marks (Gemini sparkle plus the Doubao "豆包AI生成" / Jimeng "即梦AI" / Samsung Galaxy AI "Contenuti generati dall'AI" text marks), the open SD/SDXL/FLUX invisible watermark, and (with the trustmark extra) the open Adobe TrustMark watermark into one origin-platform + watermark-inventory verdict (--json for machine output)
Visible overlays are used by Google Gemini / Nano Banana (sparkle logo), by ByteDance's Doubao ("豆包AI生成" corner text) and Jimeng / Dreamina ("★ 即梦AI" wordmark), and by Samsung Galaxy AI ("✦ Contenuti generati dall'AI" strip, bottom-left, locale-specific). All are removed on CPU by reverse-alpha against a captured alpha map (Jimeng and Samsung add a thin residual inpaint over the glyph footprint, since their marks re-rasterize per image). Other services rely on invisible watermarks and/or metadata; our diffusion-based regeneration works against any invisible watermark in pixel or frequency domain. For a visible mark from any other source (any position, any colour), use the universal erase --region command.
Visible overlays are used by Google Gemini / Nano Banana (sparkle logo), by ByteDance's Doubao ("豆包AI生成" corner text) and Jimeng / Dreamina ("★ 即梦AI" wordmark), and by Samsung Galaxy AI ("✦ Contenuti generati dall'AI" strip, bottom-left, locale-specific). All are removed on CPU by reverse-alpha against a captured alpha map (Jimeng and Samsung add a thin residual inpaint over the glyph footprint, since their marks re-rasterize per image). Other services rely on invisible watermarks and/or metadata; our diffusion-based regeneration works against any invisible watermark in pixel or frequency domain. For a visible mark from any other source (any position, any colour), use the universal erase --region command.
erase --region
Detection: remove-ai-watermarks identify reports the origin platform and watermark inventory for all the signals above — C2PA issuer, the C2PA soft-binding forensic-watermark vendor (TrustMark / Digimarc / Imatag / ...), IPTC "Made with AI" plus the IPTC 2025.1 AISystemUsed field, the China TC260 AIGC label (XMP, PNG chunk, EXIF, or JPEG segment), the HuggingFace hf-job-id job marker, embedded generation params, EXIF/XMP generator tags, the xAI/Grok EXIF signature, the SynthID metadata proxy, the C2PA cloud-manifest reference (Adobe Durable Content Credentials, when the embedded manifest is stripped), the visible marks (Gemini sparkle plus the Doubao "豆包AI生成" / Jimeng "即梦AI" / Samsung Galaxy AI "Contenuti generati dall'AI" text marks), and (with the [detect] / [trustmark] extras) the open SD/SDXL/FLUX and Adobe TrustMark invisible watermarks. SynthID and the proprietary soft-binding watermarks (Digimarc etc.) have no local decoder, so they are reported by metadata proxy / vendor name only.
Detection: remove-ai-watermarks identify reports the origin platform and watermark inventory for all the signals above — C2PA issuer, the C2PA soft-binding forensic-watermark vendor (TrustMark / Digimarc / Imatag / ...), IPTC "Made with AI" plus the IPTC 2025.1 AISystemUsed field, the China TC260 AIGC label (XMP, PNG chunk, EXIF, or JPEG segment), the HuggingFace hf-job-id job marker, embedded generation params, EXIF/XMP generator tags, the xAI/Grok EXIF signature, the SynthID metadata proxy, the C2PA cloud-manifest reference (Adobe Durable Content Credentials, when the embedded manifest is stripped), the visible marks (Gemini sparkle plus the Doubao "豆包AI生成" / Jimeng "即梦AI" / Samsung Galaxy AI "Contenuti generati dall'AI" text marks), and (with the [detect] / [trustmark] extras) the open SD/SDXL/FLUX and Adobe TrustMark invisible watermarks. SynthID and the proprietary soft-binding watermarks (Digimarc etc.) have no local decoder, so they are reported by metadata proxy / vendor name only.
remove-ai-watermarks identify
AISystemUsed
hf-job-id
[detect]
[trustmark]
How it works
Removing the Gemini / Nano Banana sparkle watermark
Google Gemini (internally codenamed Nano Banana) adds a visible sparkle logo to generated images using alpha blending:
watermarked = α × logo + (1 − α) × original
watermarked = α × logo + (1 − α) × original
We reverse this with a known alpha map (extracted from Gemini / Nano Banana output on a pure-black background):
original = (watermarked − α × logo) / (1 − α)
original = (watermarked − α × logo) / (1 − α)
A three-stage NCC (Normalized Cross-Correlation) detector finds the watermark position and scale dynamically, so it works even if the image was resized or cropped. After removal, residual sparkle-edge artifacts are cleaned via gradient-masked inpainting.
Speed: ~0.05s per image. No GPU needed.
Removing the Doubao "豆包AI生成" text watermark
Doubao (ByteDance) stamps every output with a light, semi-transparent "豆包AI生成" text strip in the bottom-right corner — the visible AIGC label mandated by China's TC260 standard. It is a fixed semi-transparent white overlay, so it is removed by reverse-alpha blending: original = (watermarked - α·logo) / (1 - α), recovering the true pixels instead of hallucinating them. The α map is solved from controlled black/gray captures (rebuildable with scripts/visible_alpha_solve.py). Like the Jimeng mark, Doubao re-rasterizes its text slightly per image, so reverse-alpha is followed by a thin residual inpaint over the glyph footprint to clear the leftover edges, and the α template is NCC-aligned to the actual mark (handling per-image scale/position jitter). Detection matches the same glyph silhouette against the corner (normalized correlation), so it keys on the "豆包AI生成" shape, not on textured corners.
original = (watermarked - α·logo) / (1 - α)
scripts/visible_alpha_solve.py
Speed: ~0.05s, no GPU needed.
Removing the Jimeng "★ 即梦AI" wordmark
Jimeng / Dreamina (即梦AI, also ByteDance, distinct from Doubao) stamps a "★ 即梦AI" wordmark — a four-point sparkle followed by the 即梦AI characters — in the bottom-right corner. It is a fixed semi-transparent pure-white overlay, solved from controlled black / gray / white captures the same way as Doubao. visible --mark auto detects and removes it (or force it with --mark jimeng). One difference from Doubao: Jimeng re-rasterizes its mark slightly differently per image, so a single alpha map does not cancel it pixel-for-pixel — reverse-alpha knocks the mark down and a residual inpaint over the glyph footprint clears the remaining outline. The two ByteDance marks do not confuse auto: detection keys on each mark's own glyph shape (the Jimeng detector scores far below its threshold on a Doubao strip, and vice versa).
Removing the Samsung Galaxy AI "✦ Contenuti generati dall'AI" mark
Samsung's on-device Generative AI edits (Generative Edit, Sketch to Image, Portrait Studio) burn a visible sparkle + "generated with AI" string into the bottom-left corner — a faint, low-opacity semi-transparent white overlay. It is solved from controlled black / gray / white captures the same way as Jimeng and removed by reverse-alpha plus a thin residual inpaint over the glyph footprint (the mark re-rasterizes per image, and the flat captures are smaller than real photos, so the alpha template is NCC-aligned and width-scaled to the actual mark). visible --mark auto detects and removes it (or force it with --mark samsung); being bottom-left it never confuses the bottom-right Gemini/Doubao/Jimeng marks. The string is locale-specific — this build is calibrated for the Italian "Contenuti generati dall'AI" variant; other locales need their own captured template (open a sample on issue #37).
For any visible mark the dedicated engines do not cover — a logo anywhere, any colour — erase --region x,y,w,h inpaints the box you specify. The default cv2 backend is instant and dependency-free; the optional lama backend (big-LaMa via onnxruntime, lama extra, ~200 MB model downloaded on first use) gives much cleaner fills on textured regions at the cost of ~3-4 GB RAM per call.
erase --region x,y,w,h
cv2
lama
lama
Removing SynthID and other invisible watermarks
Google embeds SynthID into every image generated by Gemini / Nano Banana. Other AI services use StableSignature, TreeRing, and similar schemes. These imperceptible frequency-domain patterns survive cropping, resizing, and JPEG compression.
The removal pipeline (default profile, SDXL):
image → encode to latent space (VAE) at native resolution → add controlled noise (forward diffusion) → denoise (reverse diffusion, ~50 steps; strength is vendor-adaptive: 0.20 OpenAI / 0.30 Google / 0.30 unknown, same for both pipelines; override with --strength) → decode back to pixels (VAE)
image → encode to latent space (VAE) at native resolution → add controlled noise (forward diffusion) → denoise (reverse diffusion, ~50 steps; strength is vendor-adaptive: 0.20 OpenAI / 0.30 Google / 0.30 unknown, same for both pipelines; override with --strength) → decode back to pixels (VAE)
Large inputs run at native resolution (no down-then-up round-trip, which was the main quality loss in issue #10); use --max-resolution N only to cap GPU/MPS memory on very large inputs. Small inputs (long side under 1024 px) are auto-upscaled to a 1024 px floor before diffusion, because SDXL distorts on a tiny latent, and the result is restored to the original size (a transparent quality boost). Disable the floor with --min-resolution 0. The floor upscale uses Lanczos by default; --upscaler esrgan (the esrgan extra) runs Real-ESRGAN first for sharper detail and falls back to Lanczos if the extra is absent. ESRGAN is a generic photo/texture GAN with no face/glyph prior, so it is best for photo/texture content -- it can degrade faces (the diffusion pass regenerates them, so the final recovers) and thin text; keep Lanczos for text-heavy inputs.
--max-resolution N
--min-resolution 0
--upscaler esrgan
esrgan
Default strength is vendor-adaptive (no flag needed). The tool reads the C2PA issuer to detect which vendor's SynthID is present and picks the strength accordingly: OpenAI gpt-image → 0.20, Google Gemini → 0.30, unknown source → 0.30. The same ladder applies to both pipelines — these are the oracle-certified controlnet floors (June 2026 Modal cert, multi-seed). They also cover plain sdxl: the two pipelines have opposite hard cases (controlnet leaves SynthID on photoreal, sdxl on flat graphics), but on its own hard case sdxl is the weaker remover, so it needs at least controlnet's strength — using one certified ladder is the safe choice (margin-based for sdxl, not separately certified). The dominant factor is the vendor (Google's SynthID is ~3x more robust). There is no local SynthID detector, so if the oracle still reads SynthID, raise --strength; if you care more about preserving fine detail, lower it. (Caveat: Google's 0.30 was validated only at --max-resolution 1536; a very large native Gemini image may need ~0.35+.) The default pipeline is controlnet — it preserves text and face structure. It runs the same SDXL img2img scrub but adds a canny ControlNet that conditions the regeneration on the image's edge map, so text and structure stay sharp at the strengths that remove SynthID. The watermark removal still comes from the img2img regeneration (--strength); the ControlNet only preserves structure — no original pixels are copied or frozen. The default strength ladder (OpenAI 0.20 / Google 0.30) is the oracle-certified controlnet floor. --controlnet-scale tunes the preservation strength (higher = closer to the original structure). Runs fp32 on mps/cpu (fp16 only on cuda/xpu, where the fp16-fixed SDXL VAE is loaded automatically). Pass --pipeline sdxl for plain SDXL img2img (lighter, no extra model download) on inputs without text or faces. No face-restore extra in the library. Every ArcFace-based regeneration approach we evaluated (GFPGAN-on-cleaned, PhotoMaker-V2, InstantID txt2img, InstantID img2img-on-cleaned at three parameter sweeps, 2026-06-04 - 2026-06-08 Modal cert sweeps) regenerated the face via SDXL diffusion — the output face pixels were diffusion-fresh (SynthID not re-introduced), but the face inherently looked more AI-generated than the cleaned image (SDXL "clean skin" gloss, lost original identity precision). The cleaned image from the main controlnet 0.20 pass is the least-AI face state we can reach without re-introducing SynthID. Empirical conclusion in docs/synthid-robust-identity-research-2026-06-08.md.
Default strength is vendor-adaptive (no flag needed). The tool reads the C2PA issuer to detect which vendor's SynthID is present and picks the strength accordingly: OpenAI gpt-image → 0.20, Google Gemini → 0.30, unknown source → 0.30. The same ladder applies to both pipelines — these are the oracle-certified controlnet floors (June 2026 Modal cert, multi-seed). They also cover plain sdxl: the two pipelines have opposite hard cases (controlnet leaves SynthID on photoreal, sdxl on flat graphics), but on its own hard case sdxl is the weaker remover, so it needs at least controlnet's strength — using one certified ladder is the safe choice (margin-based for sdxl, not separately certified). The dominant factor is the vendor (Google's SynthID is ~3x more robust). There is no local SynthID detector, so if the oracle still reads SynthID, raise --strength; if you care more about preserving fine detail, lower it. (Caveat: Google's 0.30 was validated only at --max-resolution 1536; a very large native Gemini image may need ~0.35+.)
0.20
0.30
0.30
controlnet
sdxl
--strength
0.30
--max-resolution 1536
0.35
The default pipeline is controlnet — it preserves text and face structure. It runs the same SDXL img2img scrub but adds a canny ControlNet that conditions the regeneration on the image's edge map, so text and structure stay sharp at the strengths that remove SynthID. The watermark removal still comes from the img2img regeneration (--strength); the ControlNet only preserves structure — no original pixels are copied or frozen. The default strength ladder (OpenAI 0.20 / Google 0.30) is the oracle-certified controlnet floor. --controlnet-scale tunes the preservation strength (higher = closer to the original structure). Runs fp32 on mps/cpu (fp16 only on cuda/xpu, where the fp16-fixed SDXL VAE is loaded automatically). Pass --pipeline sdxl for plain SDXL img2img (lighter, no extra model download) on inputs without text or faces.
controlnet
--strength
0.20
0.30
--controlnet-scale
--pipeline sdxl
No face-restore extra in the library. Every ArcFace-based regeneration approach we evaluated (GFPGAN-on-cleaned, PhotoMaker-V2, InstantID txt2img, InstantID img2img-on-cleaned at three parameter sweeps, 2026-06-04 - 2026-06-08 Modal cert sweeps) regenerated the face via SDXL diffusion — the output face pixels were diffusion-fresh (SynthID not re-introduced), but the face inherently looked more AI-generated than the cleaned image (SDXL "clean skin" gloss, lost original identity precision). The cleaned image from the main controlnet 0.20 pass is the least-AI face state we can reach without re-introducing SynthID. Empirical conclusion in docs/synthid-robust-identity-research-2026-06-08.md.
SDXL is the default since May 2026: empirically defeats SynthID v2 on Gemini 3 Pro outputs, where the older SD-1.5 pipeline at 768 px did not. The SD-1.5 path was removed once it was verified not to handle v2. Note the scope: this defeats the SynthID verifier, which is not the same as being forensically indistinguishable from a real photo. Recent work (arXiv:2605.09203) shows watermark-removal pipelines leave detectable traces, so a separate "this image was processed" classifier can still flag the output.
Oracle vs identify can disagree, and that is expected. An online verifier reads the actual SynthID pixel watermark and detects only its own vendor's content — openai.com/research/verify states "OpenAI generation signals will only be detected if the image was generated with our tools". Our identify cannot decode the pixel watermark (no vendor ships a local decoder), so it infers SynthID from the C2PA metadata instead. So after the SDXL pass the oracle can read "no SynthID" (pixel watermark gone) while identify still reports SynthID from a surviving C2PA manifest. They measure different signals. Run metadata --remove (or all) to also strip the manifest; note that a quiet metadata proxy is not proof the pixel watermark itself is gone.
Oracle vs identify can disagree, and that is expected. An online verifier reads the actual SynthID pixel watermark and detects only its own vendor's content — openai.com/research/verify states "OpenAI generation signals will only be detected if the image was generated with our tools". Our identify cannot decode the pixel watermark (no vendor ships a local decoder), so it infers SynthID from the C2PA metadata instead. So after the SDXL pass the oracle can read "no SynthID" (pixel watermark gone) while identify still reports SynthID from a surviving C2PA manifest. They measure different signals. Run metadata --remove (or all) to also strip the manifest; note that a quiet metadata proxy is not proof the pixel watermark itself is gone.
identify
identify
identify
metadata --remove
all
Technical deep-dive: see docs/synthid.md for a primary-source-cited breakdown of how SynthID works mechanically (post-hoc encoder/decoder, 136-bit payload, pixel-space embedding), what it empirically survives (JPEG, crop, resize: ~99.98% TPR at 0.1% FPR from arXiv:2510.09263), what removes it, and the forensic-stealth tradeoff (all known removal attacks are detectable at >98% TPR@1%FPR per arXiv:2605.09203).
Technical deep-dive: see docs/synthid.md for a primary-source-cited breakdown of how SynthID works mechanically (post-hoc encoder/decoder, 136-bit payload, pixel-space embedding), what it empirically survives (JPEG, crop, resize: ~99.98% TPR at 0.1% FPR from arXiv:2510.09263), what removes it, and the forensic-stealth tradeoff (all known removal attacks are detectable at >98% TPR@1%FPR per arXiv:2605.09203).
docs/synthid.md
Text and face preservation (the default pipeline; --pipeline sdxl opts down to plain SDXL): a canny ControlNet keeps text and face structure sharp through the removal pass, without copying or freezing any original pixels (so SynthID is still removed). Tune the preservation strength with --controlnet-scale. Canny preserves structure but not face identity: the regenerated face drifts in likeness. The library does not ship a face-restore extra (see the callout above).
--pipeline sdxl
--controlnet-scale
Analog Humanizer: optional film grain and chromatic aberration injection that mimics a photo of a screen, raising the bar for AI-generated image classifiers. (It frustrates generic classifiers but does not guarantee forensic invisibility — see the arXiv:2605.09203 note above.)
Stripping C2PA, EXIF, and "Made with AI" metadata
AI tools embed generation metadata that social platforms use to show "Made with AI" labels:
XMP DigitalSourceType — trainedAlgorithmicMedia tag used by Instagram, Facebook, and X (Twitter) to show "Made with AI"
trainedAlgorithmicMedia
PNG text chunks — ComfyUI workflows, AUTOMATIC1111 parameters
C2PA Content Credentials — cryptographic provenance manifests from Google Imagen, OpenAI DALL-E, Adobe Firefly
The cleaner parses each layer, removes AI-related fields, and preserves standard metadata (Author, Copyright, Title).
Installation
Homebrew (macOS / Linux)
brew install wiltodelta/tap/remove-ai-watermarks
This installs the core command surface (identify, metadata, visible, erase) as a self-contained CLI. The diffusion-based invisible / all pipeline needs heavy ML dependencies (torch, diffusers, multi-GB) and is kept out of the Homebrew build; add it with the gpu extra via pip if you need it:
identify
metadata
visible
erase
invisible
all
gpu
pip install "remove-ai-watermarks[gpu]"
conda
A conda-forge recipe is under review (staged-recipes PR). Once it merges, the core package installs with:
conda install -c conda-forge remove-ai-watermarks
Like the Homebrew build, this is the core command surface; add the diffusion invisible / all pipeline with the pip gpu extra.
invisible
all
gpu
Recommended
Install as an isolated CLI tool — no need to manage virtual environments:
Using pipx (https://pipx.pypa.io) pipx install git+https://github.com/wiltodelta/remove-ai-watermarks.git # Or using uv (https://docs.astral.sh/uv) uv tool install git+https://github.com/wiltodelta/remove-ai-watermarks.git
To update to the latest version:
pipx upgrade remove-ai-watermarks # or uv tool upgrade remove-ai-watermarks
Install from repository
Prerequisites: Python 3.10+ and pip (or uv).
pip
uv
1. Clone the repository git clone https://github.com/wiltodelta/remove-ai-watermarks.git cd remove-ai-watermarks # 2. Install the package in editable mode pip install -e . # Or, if you use uv: uv pip install -e .
After installation the remove-ai-watermarks command is available system-wide.
remove-ai-watermarks
Note: The base install covers visible watermark removal and metadata stripping. For invisible watermark removal (SynthID etc.), install GPU dependencies: pip install -e ".[gpu]" # or: uv pip install -e ".[gpu]" Without the [gpu] extra, all still runs the visible and metadata steps, but it skips the invisible (SynthID) step, prints a clear warning, and exits with a non-zero status so a skipped step is not mistaken for a clean result. To let identify decode the open Stable Diffusion / SDXL / FLUX invisible watermarks, install the detect extra (adds the invisible-watermark decoder): pip install -e ".[detect]" # or: uv pip install -e ".[detect]" To also decode the open Adobe TrustMark watermark (behind Adobe Durable Content Credentials), install the trustmark extra (pulls torch and downloads model weights on first use): pip install -e ".[trustmark]" # or: uv pip install -e ".[trustmark]" For sharper upscaling of small inputs before diffusion (--upscaler esrgan, Real-ESRGAN), install the esrgan extra. It loads via spandrel (MIT, no basicsr); the Real-ESRGAN weights (BSD-3-Clause) download on first use: pip install -e ".[esrgan]" # or: uv pip install -e ".[esrgan]"
Note: The base install covers visible watermark removal and metadata stripping. For invisible watermark removal (SynthID etc.), install GPU dependencies:
Without the [gpu] extra, all still runs the visible and metadata steps, but it skips the invisible (SynthID) step, prints a clear warning, and exits with a non-zero status so a skipped step is not mistaken for a clean result.
[gpu]
all
To let identify decode the open Stable Diffusion / SDXL / FLUX invisible watermarks, install the detect extra (adds the invisible-watermark decoder):
To also decode the open Adobe TrustMark watermark (behind Adobe Durable Content Credentials), install the trustmark extra (pulls torch and downloads model weights on first use):
For sharper upscaling of small inputs before diffusion (--upscaler esrgan, Real-ESRGAN), install the esrgan extra. It loads via spandrel (MIT, no basicsr); the Real-ESRGAN weights (BSD-3-Clause) download on first use:
Invisible removal uses diffusion models and a GPU for reasonable speed.
On first run, the model (~2 GB) will be downloaded automatically. # Device is auto-detected: CUDA (Linux/Windows) > MPS (macOS) > CPU. # To force a device: --device cuda / --device mps / --device cpu # Optional: set a HuggingFace token for gated/private models cp .env.example .env # Edit .env and set HF_TOKEN=hf_your_token_here
Install with dev dependencies (pytest, ruff, pyright) pip install -e ".[dev]" # Or with uv: uv pip install -e ".[dev]" # Run tests pytest # Run linters ./maintain.sh
ComfyUI
If this tool saves you time, consider sponsoring its development.
Intended for lawful use only. Publishing and running this software is lawful; responsibility for any downstream use, and for compliance with local law, rests entirely with the user. Some jurisdictions restrict removing an AI label as such (see Legal). The authors do not condone use for deception, fraud, or any unlawful activity.
Intended for lawful use only. Publishing and running this software is lawful; responsibility for any downstream use, and for compliance with local law, rests entirely with the user. Some jurisdictions restrict removing an AI label as such (see Legal). The authors do not condone use for deception, fraud, or any unlawful activity.
Scope
This tool removes AI-provenance watermarks that a platform stamps onto content you generated yourself — SynthID, the Gemini / Nano Banana sparkle, the Doubao / Jimeng / Samsung visible AI labels, the Chinese TC260 "由…AI生成" label, and C2PA / IPTC / EXIF "Made with AI" metadata. The point is your autonomy over your own output.
It does not target watermarks that protect someone else's paid or copyrighted content — stock-agency overlays (Shutterstock, Getty, iStock, Adobe Stock), classifieds-site marks, or any tiled "preview" watermark whose job is to gate a purchase. Removing those is out of scope by design. erase is a generic, user-driven region tool for your own objects, not an automatic stock-watermark remover.
erase
Features
Visible watermark removal — a registry of known marks in their usual places: the Gemini / Nano Banana sparkle, the Doubao "豆包AI生成" text strip, the Jimeng "★ 即梦AI" wordmark, and the Samsung Galaxy AI "✦ Contenuti generati dall'AI" strip (bottom-left, locale-specific). Each is removed by reverse-alpha blending against a captured alpha map (original = (wm − α·logo)/(1−α)), recovering the true pixels rather than inpainting a guess. The Gemini sparkle recovers cleanly on its own on bright backgrounds; it adapts the alpha to each image's sparkle opacity, so a more-opaque-than-captured sparkle is still fully removed (and on a dark background, where the fixed alpha would over-subtract and leave a dark spot, it automatically inpaints the small sparkle footprint instead); the Doubao, Jimeng, and Samsung text marks re-rasterize slightly per image, so a thin residual inpaint over the glyph footprint clears the leftover edges (the alpha maps are reproducibly rebuilt from controlled captures by scripts/visible_alpha_solve.py). Fast, offline, no GPU. visible --mark auto finds and removes the strongest detected mark. (For arbitrary logos/objects, see erase.)
original = (wm − α·logo)/(1−α)
scripts/visible_alpha_solve.py
visible --mark auto
erase
Universal region eraser (erase) — remove any logo / watermark / object inside boxes you specify, regardless of position or colour. Default cv2 inpainting (CPU, instant); optional big-LaMa via onnxruntime (lama extra) for higher quality
erase
lama
Invisible watermark removal — SynthID, StableSignature, TreeRing via diffusion-based regeneration (needs a local GPU, or run it with no setup on raiw.cc)
AI metadata stripping — EXIF, PNG text chunks, C2PA provenance manifests (PNG / JPEG / AVIF / HEIF / JPEG-XL, MP4 / MOV / M4V / M4A at the container level, and WebM / MP3 / WAV / FLAC / OGG losslessly via ffmpeg), XMP DigitalSourceType
"Made with AI" label removal — removes the AI-disclosure metadata that platforms read to apply automatic labels (useful for clearing a false-positive label from a human-edited photograph)
Analog Humanizer — optional film grain and chromatic aberration post-processing
Text and face preservation (default) — the default pipeline is a canny ControlNet that keeps text and face structure sharp through the removal pass (without copying original pixels, so SynthID is still removed). Use --pipeline sdxl for plain SDXL img2img (lighter, no extra model download) on inputs without text or faces. Canny preserves face structure, not identity (the regenerated face drifts in likeness). The library does not ship a face-restore extra: every approach evaluated (GFPGAN-on-cleaned, PhotoMaker-V2, InstantID txt2img, InstantID img2img-on-cleaned) regenerated the face via SDXL and made the output look more AI-generated than the cleaned image. The cleaned controlnet output is the least-AI face state achievable without re-introducing SynthID.
--pipeline sdxl
Batch processing — process entire directories
Detection — three-stage NCC watermark detection with confidence scoring
Provenance detection (identify) — aggregate C2PA issuer, the C2PA soft-binding forensic-watermark vendor (Adobe TrustMark, Digimarc, Imatag, ...), IPTC "Made with AI" plus the IPTC 2025.1 AISystemUsed field, embedded SD/ComfyUI params, EXIF/XMP generator tags, the xAI/Grok EXIF signature, the China TC260 AIGC label (XMP, PNG chunk, EXIF, or JPEG segment), the HuggingFace hf-job-id job marker, the SynthID metadata proxy, the C2PA cloud-manifest reference (Adobe Durable Content Credentials, when the embedded manifest is stripped), the visible marks (Gemini sparkle plus the Doubao "豆包AI生成" / Jimeng "即梦AI" / Samsung Galaxy AI "Contenuti generati dall'AI" text marks), the open SD/SDXL/FLUX invisible watermark, and (with the trustmark extra) the open Adobe TrustMark watermark into one origin-platform + watermark-inventory verdict (--json for machine output)
Visible overlays are used by Google Gemini / Nano Banana (sparkle logo), by ByteDance's Doubao ("豆包AI生成" corner text) and Jimeng / Dreamina ("★ 即梦AI" wordmark), and by Samsung Galaxy AI ("✦ Contenuti generati dall'AI" strip, bottom-left, locale-specific). All are removed on CPU by reverse-alpha against a captured alpha map (Jimeng and Samsung add a thin residual inpaint over the glyph footprint, since their marks re-rasterize per image). Other services rely on invisible watermarks and/or metadata; our diffusion-based regeneration works against any invisible watermark in pixel or frequency domain. For a visible mark from any other source (any position, any colour), use the universal erase --region command.
Visible overlays are used by Google Gemini / Nano Banana (sparkle logo), by ByteDance's Doubao ("豆包AI生成" corner text) and Jimeng / Dreamina ("★ 即梦AI" wordmark), and by Samsung Galaxy AI ("✦ Contenuti generati dall'AI" strip, bottom-left, locale-specific). All are removed on CPU by reverse-alpha against a captured alpha map (Jimeng and Samsung add a thin residual inpaint over the glyph footprint, since their marks re-rasterize per image). Other services rely on invisible watermarks and/or metadata; our diffusion-based regeneration works against any invisible watermark in pixel or frequency domain. For a visible mark from any other source (any position, any colour), use the universal erase --region command.
erase --region
Detection: remove-ai-watermarks identify reports the origin platform and watermark inventory for all the signals above — C2PA issuer, the C2PA soft-binding forensic-watermark vendor (TrustMark / Digimarc / Imatag / ...), IPTC "Made with AI" plus the IPTC 2025.1 AISystemUsed field, the China TC260 AIGC label (XMP, PNG chunk, EXIF, or JPEG segment), the HuggingFace hf-job-id job marker, embedded generation params, EXIF/XMP generator tags, the xAI/Grok EXIF signature, the SynthID metadata proxy, the C2PA cloud-manifest reference (Adobe Durable Content Credentials, when the embedded manifest is stripped), the visible marks (Gemini sparkle plus the Doubao "豆包AI生成" / Jimeng "即梦AI" / Samsung Galaxy AI "Contenuti generati dall'AI" text marks), and (with the [detect] / [trustmark] extras) the open SD/SDXL/FLUX and Adobe TrustMark invisible watermarks. SynthID and the proprietary soft-binding watermarks (Digimarc etc.) have no local decoder, so they are reported by metadata proxy / vendor name only.
Detection: remove-ai-watermarks identify reports the origin platform and watermark inventory for all the signals above — C2PA issuer, the C2PA soft-binding forensic-watermark vendor (TrustMark / Digimarc / Imatag / ...), IPTC "Made with AI" plus the IPTC 2025.1 AISystemUsed field, the China TC260 AIGC label (XMP, PNG chunk, EXIF, or JPEG segment), the HuggingFace hf-job-id job marker, embedded generation params, EXIF/XMP generator tags, the xAI/Grok EXIF signature, the SynthID metadata proxy, the C2PA cloud-manifest reference (Adobe Durable Content Credentials, when the embedded manifest is stripped), the visible marks (Gemini sparkle plus the Doubao "豆包AI生成" / Jimeng "即梦AI" / Samsung Galaxy AI "Contenuti generati dall'AI" text marks), and (with the [detect] / [trustmark] extras) the open SD/SDXL/FLUX and Adobe TrustMark invisible watermarks. SynthID and the proprietary soft-binding watermarks (Digimarc etc.) have no local decoder, so they are reported by metadata proxy / vendor name only.
remove-ai-watermarks identify
AISystemUsed
hf-job-id
[detect]
[trustmark]
How it works
Removing the Gemini / Nano Banana sparkle watermark
Google Gemini (internally codenamed Nano Banana) adds a visible sparkle logo to generated images using alpha blending:
watermarked = α × logo + (1 − α) × original
watermarked = α × logo + (1 − α) × original
We reverse this with a known alpha map (extracted from Gemini / Nano Banana output on a pure-black background):
original = (watermarked − α × logo) / (1 − α)
original = (watermarked − α × logo) / (1 − α)
A three-stage NCC (Normalized Cross-Correlation) detector finds the watermark position and scale dynamically, so it works even if the image was resized or cropped. After removal, residual sparkle-edge artifacts are cleaned via gradient-masked inpainting.
Speed: ~0.05s per image. No GPU needed.
Removing the Doubao "豆包AI生成" text watermark
Doubao (ByteDance) stamps every output with a light, semi-transparent "豆包AI生成" text strip in the bottom-right corner — the visible AIGC label mandated by China's TC260 standard. It is a fixed semi-transparent white overlay, so it is removed by reverse-alpha blending: original = (watermarked - α·logo) / (1 - α), recovering the true pixels instead of hallucinating them. The α map is solved from controlled black/gray captures (rebuildable with scripts/visible_alpha_solve.py). Like the Jimeng mark, Doubao re-rasterizes its text slightly per image, so reverse-alpha is followed by a thin residual inpaint over the glyph footprint to clear the leftover edges, and the α template is NCC-aligned to the actual mark (handling per-image scale/position jitter). Detection matches the same glyph silhouette against the corner (normalized correlation), so it keys on the "豆包AI生成" shape, not on textured corners.
original = (watermarked - α·logo) / (1 - α)
scripts/visible_alpha_solve.py
Speed: ~0.05s, no GPU needed.
Removing the Jimeng "★ 即梦AI" wordmark
Jimeng / Dreamina (即梦AI, also ByteDance, distinct from Doubao) stamps a "★ 即梦AI" wordmark — a four-point sparkle followed by the 即梦AI characters — in the bottom-right corner. It is a fixed semi-transparent pure-white overlay, solved from controlled black / gray / white captures the same way as Doubao. visible --mark auto detects and removes it (or force it with --mark jimeng). One difference from Doubao: Jimeng re-rasterizes its mark slightly differently per image, so a single alpha map does not cancel it pixel-for-pixel — reverse-alpha knocks the mark down and a residual inpaint over the glyph footprint clears the remaining outline. The two ByteDance marks do not confuse auto: detection keys on each mark's own glyph shape (the Jimeng detector scores far below its threshold on a Doubao strip, and vice versa).
Removing the Samsung Galaxy AI "✦ Contenuti generati dall'AI" mark
Samsung's on-device Generative AI edits (Generative Edit, Sketch to Image, Portrait Studio) burn a visible sparkle + "generated with AI" string into the bottom-left corner — a faint, low-opacity semi-transparent white overlay. It is solved from controlled black / gray / white captures the same way as Jimeng and removed by reverse-alpha plus a thin residual inpaint over the glyph footprint (the mark re-rasterizes per image, and the flat captures are smaller than real photos, so the alpha template is NCC-aligned and width-scaled to the actual mark). visible --mark auto detects and removes it (or force it with --mark samsung); being bottom-left it never confuses the bottom-right Gemini/Doubao/Jimeng marks. The string is locale-specific — this build is calibrated for the Italian "Contenuti generati dall'AI" variant; other locales need their own captured template (open a sample on issue #37).
For any visible mark the dedicated engines do not cover — a logo anywhere, any colour — erase --region x,y,w,h inpaints the box you specify. The default cv2 backend is instant and dependency-free; the optional lama backend (big-LaMa via onnxruntime, lama extra, ~200 MB model downloaded on first use) gives much cleaner fills on textured regions at the cost of ~3-4 GB RAM per call.
erase --region x,y,w,h
cv2
lama
lama
Removing SynthID and other invisible watermarks
Google embeds SynthID into every image generated by Gemini / Nano Banana. Other AI services use StableSignature, TreeRing, and similar schemes. These imperceptible frequency-domain patterns survive cropping, resizing, and JPEG compression.
The removal pipeline (default profile, SDXL):
image → encode to latent space (VAE) at native resolution → add controlled noise (forward diffusion) → denoise (reverse diffusion, ~50 steps; strength is vendor-adaptive: 0.20 OpenAI / 0.30 Google / 0.30 unknown, same for both pipelines; override with --strength) → decode back to pixels (VAE)
image → encode to latent space (VAE) at native resolution → add controlled noise (forward diffusion) → denoise (reverse diffusion, ~50 steps; strength is vendor-adaptive: 0.20 OpenAI / 0.30 Google / 0.30 unknown, same for both pipelines; override with --strength) → decode back to pixels (VAE)
Large inputs run at native resolution (no down-then-up round-trip, which was the main quality loss in issue #10); use --max-resolution N only to cap GPU/MPS memory on very large inputs. Small inputs (long side under 1024 px) are auto-upscaled to a 1024 px floor before diffusion, because SDXL distorts on a tiny latent, and the result is restored to the original size (a transparent quality boost). Disable the floor with --min-resolution 0. The floor upscale uses Lanczos by default; --upscaler esrgan (the esrgan extra) runs Real-ESRGAN first for sharper detail and falls back to Lanczos if the extra is absent. ESRGAN is a generic photo/texture GAN with no face/glyph prior, so it is best for photo/texture content -- it can degrade faces (the diffusion pass regenerates them, so the final recovers) and thin text; keep Lanczos for text-heavy inputs.
--max-resolution N
--min-resolution 0
--upscaler esrgan
esrgan
Default strength is vendor-adaptive (no flag needed). The tool reads the C2PA issuer to detect which vendor's SynthID is present and picks the strength accordingly: OpenAI gpt-image → 0.20, Google Gemini → 0.30, unknown source → 0.30. The same ladder applies to both pipelines — these are the oracle-certified controlnet floors (June 2026 Modal cert, multi-seed). They also cover plain sdxl: the two pipelines have opposite hard cases (controlnet leaves SynthID on photoreal, sdxl on flat graphics), but on its own hard case sdxl is the weaker remover, so it needs at least controlnet's strength — using one certified ladder is the safe choice (margin-based for sdxl, not separately certified). The dominant factor is the vendor (Google's SynthID is ~3x more robust). There is no local SynthID detector, so if the oracle still reads SynthID, raise --strength; if you care more about preserving fine detail, lower it. (Caveat: Google's 0.30 was validated only at --max-resolution 1536; a very large native Gemini image may need ~0.35+.) The default pipeline is controlnet — it preserves text and face structure. It runs the same SDXL img2img scrub but adds a canny ControlNet that conditions the regeneration on the image's edge map, so text and structure stay sharp at the strengths that remove SynthID. The watermark removal still comes from the img2img regeneration (--strength); the ControlNet only preserves structure — no original pixels are copied or frozen. The default strength ladder (OpenAI 0.20 / Google 0.30) is the oracle-certified controlnet floor. --controlnet-scale tunes the preservation strength (higher = closer to the original structure). Runs fp32 on mps/cpu (fp16 only on cuda/xpu, where the fp16-fixed SDXL VAE is loaded automatically). Pass --pipeline sdxl for plain SDXL img2img (lighter, no extra model download) on inputs without text or faces. No face-restore extra in the library. Every ArcFace-based regeneration approach we evaluated (GFPGAN-on-cleaned, PhotoMaker-V2, InstantID txt2img, InstantID img2img-on-cleaned at three parameter sweeps, 2026-06-04 - 2026-06-08 Modal cert sweeps) regenerated the face via SDXL diffusion — the output face pixels were diffusion-fresh (SynthID not re-introduced), but the face inherently looked more AI-generated than the cleaned image (SDXL "clean skin" gloss, lost original identity precision). The cleaned image from the main controlnet 0.20 pass is the least-AI face state we can reach without re-introducing SynthID. Empirical conclusion in docs/synthid-robust-identity-research-2026-06-08.md.
Default strength is vendor-adaptive (no flag needed). The tool reads the C2PA issuer to detect which vendor's SynthID is present and picks the strength accordingly: OpenAI gpt-image → 0.20, Google Gemini → 0.30, unknown source → 0.30. The same ladder applies to both pipelines — these are the oracle-certified controlnet floors (June 2026 Modal cert, multi-seed). They also cover plain sdxl: the two pipelines have opposite hard cases (controlnet leaves SynthID on photoreal, sdxl on flat graphics), but on its own hard case sdxl is the weaker remover, so it needs at least controlnet's strength — using one certified ladder is the safe choice (margin-based for sdxl, not separately certified). The dominant factor is the vendor (Google's SynthID is ~3x more robust). There is no local SynthID detector, so if the oracle still reads SynthID, raise --strength; if you care more about preserving fine detail, lower it. (Caveat: Google's 0.30 was validated only at --max-resolution 1536; a very large native Gemini image may need ~0.35+.)
0.20
0.30
0.30
controlnet
sdxl
--strength
0.30
--max-resolution 1536
0.35
The default pipeline is controlnet — it preserves text and face structure. It runs the same SDXL img2img scrub but adds a canny ControlNet that conditions the regeneration on the image's edge map, so text and structure stay sharp at the strengths that remove SynthID. The watermark removal still comes from the img2img regeneration (--strength); the ControlNet only preserves structure — no original pixels are copied or frozen. The default strength ladder (OpenAI 0.20 / Google 0.30) is the oracle-certified controlnet floor. --controlnet-scale tunes the preservation strength (higher = closer to the original structure). Runs fp32 on mps/cpu (fp16 only on cuda/xpu, where the fp16-fixed SDXL VAE is loaded automatically). Pass --pipeline sdxl for plain SDXL img2img (lighter, no extra model download) on inputs without text or faces.
controlnet
--strength
0.20
0.30
--controlnet-scale
--pipeline sdxl
No face-restore extra in the library. Every ArcFace-based regeneration approach we evaluated (GFPGAN-on-cleaned, PhotoMaker-V2, InstantID txt2img, InstantID img2img-on-cleaned at three parameter sweeps, 2026-06-04 - 2026-06-08 Modal cert sweeps) regenerated the face via SDXL diffusion — the output face pixels were diffusion-fresh (SynthID not re-introduced), but the face inherently looked more AI-generated than the cleaned image (SDXL "clean skin" gloss, lost original identity precision). The cleaned image from the main controlnet 0.20 pass is the least-AI face state we can reach without re-introducing SynthID. Empirical conclusion in docs/synthid-robust-identity-research-2026-06-08.md.
SDXL is the default since May 2026: empirically defeats SynthID v2 on Gemini 3 Pro outputs, where the older SD-1.5 pipeline at 768 px did not. The SD-1.5 path was removed once it was verified not to handle v2. Note the scope: this defeats the SynthID verifier, which is not the same as being forensically indistinguishable from a real photo. Recent work (arXiv:2605.09203) shows watermark-removal pipelines leave detectable traces, so a separate "this image was processed" classifier can still flag the output.
Oracle vs identify can disagree, and that is expected. An online verifier reads the actual SynthID pixel watermark and detects only its own vendor's content — openai.com/research/verify states "OpenAI generation signals will only be detected if the image was generated with our tools". Our identify cannot decode the pixel watermark (no vendor ships a local decoder), so it infers SynthID from the C2PA metadata instead. So after the SDXL pass the oracle can read "no SynthID" (pixel watermark gone) while identify still reports SynthID from a surviving C2PA manifest. They measure different signals. Run metadata --remove (or all) to also strip the manifest; note that a quiet metadata proxy is not proof the pixel watermark itself is gone.
Oracle vs identify can disagree, and that is expected. An online verifier reads the actual SynthID pixel watermark and detects only its own vendor's content — openai.com/research/verify states "OpenAI generation signals will only be detected if the image was generated with our tools". Our identify cannot decode the pixel watermark (no vendor ships a local decoder), so it infers SynthID from the C2PA metadata instead. So after the SDXL pass the oracle can read "no SynthID" (pixel watermark gone) while identify still reports SynthID from a surviving C2PA manifest. They measure different signals. Run metadata --remove (or all) to also strip the manifest; note that a quiet metadata proxy is not proof the pixel watermark itself is gone.
identify
identify
identify
metadata --remove
all
Technical deep-dive: see docs/synthid.md for a primary-source-cited breakdown of how SynthID works mechanically (post-hoc encoder/decoder, 136-bit payload, pixel-space embedding), what it empirically survives (JPEG, crop, resize: ~99.98% TPR at 0.1% FPR from arXiv:2510.09263), what removes it, and the forensic-stealth tradeoff (all known removal attacks are detectable at >98% TPR@1%FPR per arXiv:2605.09203).
Technical deep-dive: see docs/synthid.md for a primary-source-cited breakdown of how SynthID works mechanically (post-hoc encoder/decoder, 136-bit payload, pixel-space embedding), what it empirically survives (JPEG, crop, resize: ~99.98% TPR at 0.1% FPR from arXiv:2510.09263), what removes it, and the forensic-stealth tradeoff (all known removal attacks are detectable at >98% TPR@1%FPR per arXiv:2605.09203).
docs/synthid.md
Text and face preservation (the default pipeline; --pipeline sdxl opts down to plain SDXL): a canny ControlNet keeps text and face structure sharp through the removal pass, without copying or freezing any original pixels (so SynthID is still removed). Tune the preservation strength with --controlnet-scale. Canny preserves structure but not face identity: the regenerated face drifts in likeness. The library does not ship a face-restore extra (see the callout above).
--pipeline sdxl
--controlnet-scale
Analog Humanizer: optional film grain and chromatic aberration injection that mimics a photo of a screen, raising the bar for AI-generated image classifiers. (It frustrates generic classifiers but does not guarantee forensic invisibility — see the arXiv:2605.09203 note above.)
Stripping C2PA, EXIF, and "Made with AI" metadata
AI tools embed generation metadata that social platforms use to show "Made with AI" labels:
XMP DigitalSourceType — trainedAlgorithmicMedia tag used by Instagram, Facebook, and X (Twitter) to show "Made with AI"
trainedAlgorithmicMedia
PNG text chunks — ComfyUI workflows, AUTOMATIC1111 parameters
C2PA Content Credentials — cryptographic provenance manifests from Google Imagen, OpenAI DALL-E, Adobe Firefly
The cleaner parses each layer, removes AI-related fields, and preserves standard metadata (Author, Copyright, Title).
Installation
Homebrew (macOS / Linux)
brew install wiltodelta/tap/remove-ai-watermarks
This installs the core command surface (identify, metadata, visible, erase) as a self-contained CLI. The diffusion-based invisible / all pipeline needs heavy ML dependencies (torch, diffusers, multi-GB) and is kept out of the Homebrew build; add it with the gpu extra via pip if you need it:
identify
metadata
visible
erase
invisible
all
gpu
pip install "remove-ai-watermarks[gpu]"
conda
A conda-forge recipe is under review (staged-recipes PR). Once it merges, the core package installs with:
conda install -c conda-forge remove-ai-watermarks
Like the Homebrew build, this is the core command surface; add the diffusion invisible / all pipeline with the pip gpu extra.
invisible
all
gpu
Recommended
Install as an isolated CLI tool — no need to manage virtual environments:
Using pipx (https://pipx.pypa.io) pipx install git+https://github.com/wiltodelta/remove-ai-watermarks.git # Or using uv (https://docs.astral.sh/uv) uv tool install git+https://github.com/wiltodelta/remove-ai-watermarks.git
To update to the latest version:
pipx upgrade remove-ai-watermarks # or uv tool upgrade remove-ai-watermarks
Install from repository
Prerequisites: Python 3.10+ and pip (or uv).
pip
uv
1. Clone the repository git clone https://github.com/wiltodelta/remove-ai-watermarks.git cd remove-ai-watermarks # 2. Install the package in editable mode pip install -e . # Or, if you use uv: uv pip install -e .
After installation the remove-ai-watermarks command is available system-wide.
remove-ai-watermarks
Note: The base install covers visible watermark removal and metadata stripping. For invisible watermark removal (SynthID etc.), install GPU dependencies: pip install -e ".[gpu]" # or: uv pip install -e ".[gpu]" Without the [gpu] extra, all still runs the visible and metadata steps, but it skips the invisible (SynthID) step, prints a clear warning, and exits with a non-zero status so a skipped step is not mistaken for a clean result. To let identify decode the open Stable Diffusion / SDXL / FLUX invisible watermarks, install the detect extra (adds the invisible-watermark decoder): pip install -e ".[detect]" # or: uv pip install -e ".[detect]" To also decode the open Adobe TrustMark watermark (behind Adobe Durable Content Credentials), install the trustmark extra (pulls torch and downloads model weights on first use): pip install -e ".[trustmark]" # or: uv pip install -e ".[trustmark]" For sharper upscaling of small inputs before diffusion (--upscaler esrgan, Real-ESRGAN), install the esrgan extra. It loads via spandrel (MIT, no basicsr); the Real-ESRGAN weights (BSD-3-Clause) download on first use: pip install -e ".[esrgan]" # or: uv pip install -e ".[esrgan]"
Note: The base install covers visible watermark removal and metadata stripping. For invisible watermark removal (SynthID etc.), install GPU dependencies:
Without the [gpu] extra, all still runs the visible and metadata steps, but it skips the invisible (SynthID) step, prints a clear warning, and exits with a non-zero status so a skipped step is not mistaken for a clean result.
[gpu]
all
To let identify decode the open Stable Diffusion / SDXL / FLUX invisible watermarks, install the detect extra (adds the invisible-watermark decoder):
To also decode the open Adobe TrustMark watermark (behind Adobe Durable Content Credentials), install the trustmark extra (pulls torch and downloads model weights on first use):
For sharper upscaling of small inputs before diffusion (--upscaler esrgan, Real-ESRGAN), install the esrgan extra. It loads via spandrel (MIT, no basicsr); the Real-ESRGAN weights (BSD-3-Clause) download on first use:
Invisible removal uses diffusion models and a GPU for reasonable speed.
On first run, the model (~2 GB) will be downloaded automatically. # Device is auto-detected: CUDA (Linux/Windows) > MPS (macOS) > CPU. # To force a device: --device cuda / --device mps / --device cpu # Optional: set a HuggingFace token for gated/private models cp .env.example .env # Edit .env and set HF_TOKEN=hf_your_token_here
Install with dev dependencies (pytest, ruff, pyright) pip install -e ".[dev]" # Or with uv: uv pip install -e ".[dev]" # Run tests pytest # Run linters ./maintain.sh