Every code-referencing claim in the docs, the README and the rules files was checked against src/, and each finding was re-derived independently before it was applied. 35 held, 5 were false positives. Two of them were code, not text. `InvisibleOptions` promises in its docstring to mirror `InvisibleEngine`, and two defaults had silently stopped: `max_resolution=None` reached `_target_size`'s `max_resolution > 0` and raised `TypeError` on every library call that left the options alone, and `cpu_offload=True` made a library run slower than the identical CLI run. Both are fixed, and `TestInvisibleOptionsMirrorTheEngine` compares the two signatures field by field rather than pinning the two values that happen to be known. A companion assertion in `TestTargetSize` reads the engine's own declared default, so a drift on the engine side -- which the mirror check alone would accept, because both sides would still agree -- fails too. The user-facing docs: README called `invisible` GPU-optional where it raises without CUDA, and gave the image `metadata` command `video metadata`'s output rule, promising the source survives a command that overwrites it. Yuanbao was missing from the supported-mark list. `veo` was listed among the video policies that require a run anchor, though its row sets no `anchor_iou`. `known-limitations` called ControlNet the default profile and contradicted itself ninety lines below. An unescaped pipe truncated the `hailuo` table row. The `dev` extra, the CI shape, ffmpeg's role, the sdist boundary and the strength-curve range were corrected, and `remove_all`/`remove_batch`, the pill gate, `erase --keep-metadata` and `all`'s CUDA failure mode were documented. Research notes that described removed modules, extras and flags in the present tense now say so once in the page banner instead of sentence by sentence, which covers the whole page rather than the lines that happened to be noticed, and one fixture is referred to by role rather than by name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7.0 KiB
Installation
Python 3.10.1 or newer is required.
Default metadata mode
The default package provides:
- provenance inspection;
- AI metadata inspection and removal.
It installs Pillow, piexif, and c2pa-python for reading metadata directly from files. It does not install NumPy, OpenCV, pillow-heif, Torch, diffusion models, or invisible-watermark decoders.
Install it as an isolated command with uv:
uv tool install remove-ai-watermarks
Or with pipx:
pipx install remove-ai-watermarks
You can also install the Homebrew package on macOS or Linux:
brew install wiltodelta/tap/remove-ai-watermarks
Visible watermark removal
Visible mark detection, OpenCV inpainting, and manual region erasing need the
visible extra:
uv tool install --force "remove-ai-watermarks[visible]"
Add heif only when the pixel path must decode HEIC, HEIF, or AVIF:
uv tool install --force "remove-ai-watermarks[visible,heif]"
Video processing
Video metadata inspection works with the default package, and MP4 and MOV
stripping uses the in-tree ISOBMFF box walker. Stripping the non-ISOBMFF
containers (MKV, WebM, AVI, FLV, and the audio formats) and writing any cleaned
video need ffmpeg on PATH, for example brew install ffmpeg. Stable
visible-mark identification and removal, full video cleaning, and visible/all
batch modes need the video extra:
uv tool install --force "remove-ai-watermarks[video]"
The extra includes the visible pixel runtime and PyAV for preserving variable frame timestamps. Video SynthID regeneration also needs the diffusion stack:
uv tool install --force "remove-ai-watermarks[video,diffusion]"
Invisible watermark removal
Install the qwen-zimage extra:
uv tool install --force "remove-ai-watermarks[qwen-zimage]"
Both remaining profiles run a Z-Image face stage on the DiffSynth runtime, so
both need this extra. It includes the diffusion dependencies; diffusion on its
own covers the torch and diffusers imports but not the face stage, so it is not
enough to run a removal.
An NVIDIA GPU is required. qwen-zimage and sdxl-zimage are CUDA-only, and
construction refuses any other device rather than falling back to a slow or broken
one. There is no CPU, MPS or XPU path for invisible-watermark removal. Visible-mark
removal, metadata stripping and every identify command still run anywhere.
Video SynthID regeneration is a separate VAE path and does still run on CPU or MPS;
it needs the diffusion extra, not this one.
Feature extras
Extras are composable. Install only the capabilities and file formats the application actually uses:
| Extra | Capability | Automatically includes | Torch or model download |
|---|---|---|---|
pixels |
Shared BGR array and image-processing runtime | NumPy, headless OpenCV | No |
heif |
HEIC, HEIF, and AVIF pixel decoding | pillow-heif | No |
visible |
Visible mark detection, OpenCV inpainting, and manual erasing | pixels |
No |
video |
Visible video identification/removal and timestamp preservation | visible, PyAV |
No |
detect |
Open DWT-DCT detection for Stable Diffusion, SDXL, and FLUX | pixels, PyWavelets |
No |
trustmark |
Adobe TrustMark detection | trustmark | Yes |
diffusion |
Torch and Diffusers runtime; video SynthID regeneration | pixels, Torch, Diffusers |
Yes |
migan |
MI-GAN ONNX fill backend | visible, ONNX Runtime |
Model download, no Torch |
lama |
big-LaMa ONNX fill backend | visible, ONNX Runtime |
Model download, no Torch |
qwen-zimage |
Invisible image-watermark removal, both CUDA-only profiles | diffusion, DiffSynth |
Yes |
all |
Every production feature | All rows above | Yes |
dev |
Tests, linting, typing, and upstream parity checks | video, detect, upstream invisible-watermark |
Yes, for parity tests |
Dependency composition:
flowchart LR
visible --> pixels
video --> visible
detect --> pixels
diffusion --> pixels
migan --> visible
lama --> visible
qwen["qwen-zimage"] --> diffusion
heif
trustmark
heif and trustmark are independent branches. Combine them explicitly with
another feature when required. The all bundle contains every production
branch but never includes dev.
Examples:
# Metadata plus torch-free DWT-DCT detection
uv tool install --force "remove-ai-watermarks[detect]"
# Visible removal with HEIC/AVIF support and MI-GAN
uv tool install --force "remove-ai-watermarks[migan,heif]"
# Visible video removal with preserved timestamps
uv tool install --force "remove-ai-watermarks[video]"
# DWT-DCT and TrustMark detection without diffusion removal
uv tool install --force "remove-ai-watermarks[detect,trustmark]"
# Every production capability
uv tool install --force "remove-ai-watermarks[all]"
# An arbitrary minimal combination
uv tool install --force "remove-ai-watermarks[migan,detect]"
heif stays independent so applications that only process PNG, JPEG, or WebP
do not install libheif. detect uses the in-tree torch-free decoder and does
not install the upstream invisible-watermark package. Optional models download
their weights on first use.
The old gpu and remove aliases are intentionally not provided. Use
diffusion and visible respectively.
Install from the repository
git clone https://github.com/wiltodelta/remove-ai-watermarks.git
cd remove-ai-watermarks
uv sync --frozen
Add the feature groups required for your work:
uv sync --frozen --extra dev
uv sync --frozen --extra dev --extra diffusion
Run commands from the repository root:
uv run remove-ai-watermarks --help
Development setup
Install development dependencies:
uv sync --frozen --extra dev
Run the complete project gate:
bash maintain.sh
The script syncs every optional backend on top of the dev environment above,
then runs dependency checks, linting, formatting, type checking, and the test
suite. It applies Ruff fixes and formatting in place rather than only reporting
them.
Hugging Face authentication
Pass a Hugging Face token directly when the selected model or account requires one:
remove-ai-watermarks invisible image.png --hf-token "$HF_TOKEN"
The CLI also loads HF_TOKEN from the environment and from a local .env
file. The same name is documented in .env.example.
Troubleshooting
The first model run is slow
Diffusion and learned fill backends may download model weights on first use. Later runs reuse their caches.
The command skips invisible removal
The normal behavior is to skip diffusion when no supported local signal is
found. A missing signal does not prove that the image is clean. If you know the
image came from a relevant generator, use --force.
If the CLI reports that the removal dependencies are unavailable, install the
qwen-zimage extra. diffusion alone covers Torch and Diffusers but not the
DiffSynth face stage that both profiles run. Video SynthID removal is a separate
path and needs video and diffusion.