Victor Kuznetsov 07b07634cf Merge the docs/arxiv-paper-review research line into main (#95)
* Add rigorous SynthID research and evaluation harness

* Record SynthID cross-epoch validation results

* Reset SynthID detector hypotheses to paired training

* Add paired wavelet and spectral SynthID probes

* Record closed-corpus SynthID detector ablations

* Add registered SynthID phase probing

* Add periodic SynthID tile probing

* Add calibrated SynthID pixel detector

* Harden scale-registered SynthID detection

* Record SynthID phase and channel rescue findings

* Add production SynthID routing and OpenAI verification

* Reframe periodic pixel route as pipeline lattice; confirm and harden detection

The frozen periodic experts read an origin-anchored generation-pipeline lattice destroyed by a crop off the tile grid, not the crop-robust SynthID mark. Route the pixel result as an experimental pipeline_lattice signal kept out of the watermark inventory, and carry the crop sensitivity in every verdict envelope.

Add split-patch phase/amplitude/codeword confirmation for registered-v3, affine-lattice and cyclostationary research probes, and timeout/retry/error-taxonomy hardening for the official OpenAI verification path.

* Record pipeline-lattice reframing and registered-v3 phase lock

Document that the pixel route detects an origin-anchored lattice rather than the watermark, and record the registered-v3 two-pixel phase-lock measurement: a 2px diagonal crop killed all 36 tested detections (28 foreign-generator, 8 Google) with recovery only at offsets that are multiples of four. Update README, CLI, Python API, supported-signals, known-limitations, module-internals, the SynthID reference, and the detector research plan.

* Split SynthID campaign docs by detector, classifiers, and removal

Keep Model 1 as a research AI-versus-camera result outside the library.
Retire the mixed oracle-ladders page to a routing hub and mark the plan
file as a chronological archive.

* Keep 2026-08-22 SynthID campaign results in research docs only

Drop Bayer, Model 1, and lattice re-check numbers from user guides and
module internals. The library surface is unchanged.

* Unplug lattice detector from package, expand research docs

* Remove lattice detector sources from the public package

* Document SynthID GitHub sweep findings

* Document general AI classifier sweep

* Document modern-negative taxonomy audit and classifier doc split

Split general AI-classifier research into ai-generated-image-classifiers.md
and record the modern-negative campaign: Unsplash/Openverse date-clean cells,
contamination controls, quarter-hard paired result, closed linear veto and
taxonomy-continuation arms.

pre-commit: 1) maintain.sh - exit 1, known uv-secure lightning advisory with no upstream fix (same triage as main); core checks separately green (ruff, format, pyright, 1564 tests); 2) /simplify - docs-only single pass, no findings; 3) docs sync - split references updated everywhere, none stale; 4) CLAUDE.md - routing line already updated, compliant

* Record wild-AI audit, Meta provider class, and stock-negative expansion

The wild vendor-flagged AI cell (300 stratified rows) puts Model 1 recall at
69.7% on unknown-renderer stock AI; the stock-negative harvest triples the
modern fashion/product cells and confirms the combined-pool veto control;
the Meta muse-image corpus doubles to 132 rows with its margin sweep; a
per-channel cv2 reference fixes the latent fold test under cv2 4.10.0.

pre-commit: 1) maintain.sh - exit 1, known uv-secure lightning advisory with no upstream fix; core checks separately green (ruff, format, pyright, 1665 tests); 2) /simplify - docs-only single pass, no findings; 3) docs sync - new run references point at the gitignored research store, none stale; 4) CLAUDE.md - compact, no changes needed

* Close representation-change campaign: both candidates falsified

Pre-registered gates (AI-test >=1772, fresh OI <=50, Kodak 0, FLUX >=288,
EvalGEN >=95) rejected DINOv2-L ridge head (1427/1847, 162/3000, 1/24, 29/300,
5/100) and the paired-reconstruction FFT tower (14/1847, 12/3000, 0/24, 0/300,
0/100; contract learned at 0.949 pairwise separation but no transfer to
production generators). Campaign verdict: general classifier beyond local
scale as pursued; product stays on documented remainder.

pre-commit: 1) maintain.sh - exit 1, known uv-secure lightning advisory with no upstream fix; core checks green minutes ago (ruff, format, pyright, 1665 tests); 2) /simplify - docs-only single pass, no findings; 3) docs sync - verdict mirrored in data/research/REPRESENTATION-CAMPAIGN.md, no stale refs; 4) CLAUDE.md - no changes

* Record full failure map, H2 router falsification, and H3 process detector closure

20 measured negative domains with batch-2 (memes, whiteboards, tickets, book
pages). H2 domain router falsified: 97.9% of AI-test looks structured, every
family 94-100%. H3 process detector on 369 contrastive pairs from three
generators (OpenAI gpt-image-1-mini, Meta muse-image-1.0, Gemini flash-image)
achieves 0.909 pairwise AUC on training pairs but transfers at AUC 0.55-0.60
to production AI; human receipts score higher than any AI family. The
generator-specific spectral trace does not generalize across three generators,
closing the process-trace family in its current form.

pre-commit: 1) maintain.sh - exit 1, known lightning advisory; core green (ruff, format, pyright, 1665 tests); 2) /simplify - docs + research scripts, single pass, no findings; 3) docs sync - all artifacts in data/research/, no stale refs; 4) CLAUDE.md - no changes

* Record H4-H6 research hypotheses: MLP probe, spectral invariant, TTA voting

H4 (MLP on frozen CLIP): 98.6% AI-test recall but 15.8% photo FPR — same
trade-off curve as quarter-hard. H5 (spectral invariant from CVPR 2025):
2.4% recall, the 1/f^alpha profile does not discriminate current generators.
H6 (TTA voting): fixes museum FP but not documents/UI/fashion, loses 9% recall.
All three falsified against frozen gates; ten total closed hypotheses now
document the CLIP-L representation limit from every angle.

Also records the provider cascade across all 20 domains (69.8% stray on
documents mirrors the detector failure map) and the full Y-phase campaign
PSNR data awaiting oracle rate-limit reset.

pre-commit: 1) maintain.sh - exit 1, known lightning advisory; core green (ruff, format, pyright, 1665 tests); 2) /simplify - docs-only single pass, no findings; 3) docs sync - all artifacts in data/research/, no stale refs; 4) CLAUDE.md - no changes

* Fix Y-phase frequency convention, record H4-H6 and campaign results

The initial Y-phase campaign targeted FFT bins 16-32 instead of spatial
periods 16-32 px (1/32 to 1/16 cycles/px). After the fix, the full-octave
Y-phase scramble reproduces the known kill (not_detected at 20.5 dB on s1
at native 1122x1402). Partial sub-bands are visually invisible (42.5 dB)
but stay detected: the mark is distributed across the full octave, not
concentrated. Point intervention is not viable within this frequency band.

H4 MLP probe: 98.6% recall, 15.8% FPR (same trade-off wall).
H5 spectral invariant: 2.4% recall (1/f^alpha does not discriminate).
H6 TTA voting: fixes museum FP but not documents/UI/fashion.

pre-commit: 1) maintain.sh - exit 1, known lightning advisory; core green (1665 tests, verified minutes ago); 2) /simplify - docs-only; 3) docs sync - all artifacts synced; 4) CLAUDE.md - no changes

* Add TC260 fifth class and per-class margin calibration to provider cascade

Five-head cascade (openai/google/tc260/meta/no_ai): openai 68.8%, google
76.6%, tc260 63.8%, meta 71.8% mean recall at uniform margin 0.50.
Per-class margins calibrated against 1.67% photo budget: openai 0.31,
google 0.29, tc260 0.39, meta 0.55. TC260 test recall 71.4% at calibrated
margins on 196 held-out images. Chinese-AIGC is a viable pixel class.

pre-commit: 1) maintain.sh - exit 1, known lightning advisory; core green (verified within the hour); 2) /simplify - research script, single pass; 3) docs sync - artifacts in data/research/; 4) CLAUDE.md - no changes

* Record universal detector v3-v8: diverse training shifts the curve

Fresh MLP on frozen CLIP embeddings with 49 negative domains (12,258 images)
and 5,043 AI positives achieves 93.0% AI-test recall with 39/49 domains at
zero false positives. Digital art FP dropped from 99.3% (Model 1) to 24.8%,
fashion from 62-71% to 12%, UI from 99.3% to 12%. Photo FPR 0.7%. The
improvement comes from training data diversity on frozen embeddings, not
representation change (v5 fine-tune proved this by undoing the gains).
Also adds .local-eval to ruff exclude so the gate covers only tracked files.

pre-commit: 1) maintain.sh - exit 1, known lightning advisory; core green (ruff, format, pyright, 1731 tests); 2) /simplify - docs + research; 3) docs sync - all artifacts in data/research/; 4) CLAUDE.md - no changes

* Add three-tier ensemble detector: DEFINITELY/POSSIBLY/LIKELY_HUMAN

Combines Model 1 and v8 into calibrated confidence tiers. DEFINITELY_AI
(both models agree) achieves 79.6% recall at 0.50% photo FPR with 46/49
domains at zero FP. The union reaches 93.3% recall at 1.80% photo FPR
with Kodak 0/24. First detector working across ALL content types with
per-tier calibrated error rates.

pre-commit: 1) maintain.sh - exit 1, known lightning advisory; core green (ruff, format, pyright, 1731 tests); 2) /simplify - docs + ensemble; 3) docs sync - artifacts in data/research/; 4) CLAUDE.md - no changes

* Add provider v2: diversity training eliminates document leakage

Same diversity-training insight applied to the provider cascade. Adding
3,769 structured negative features to training drops document/receipt
leakage from 69.8% to 5.1%, UI from 10% to 4.3%, fashion from 5-15% to
0-3%. AI recall improves: OpenAI 84% (was 75%), Google 88.8% (was 79%).
Photo leakage 1.4%. Worst regression: memes 47.9% (was 27%).

pre-commit: 1) maintain.sh - exit 1, known lightning advisory; core green (ruff, format, pyright, 1731 tests); 2) /simplify - provider v2; 3) docs sync - artifacts in data/research/; 4) CLAUDE.md - no changes

* Add universal v11: full taxonomy, 136 domains, 124 at zero FP

Master data plan mapped 148 visual domains. Massive collection (3,343
images, 73 domains from Openverse) plus gap-filler (724 images from
Safebooru anime/manga) expanded training to 19,768 images across 136
negative domains. Universal v11 (MLP-512 + Model 1 ensemble):
DEFINITELY 90.3% recall at 0.50% photo FPR, ANY 95.6% recall,
124/134 domains at exactly 0% FP, worst FP 13.8% (digital art).

New domains opened: anime, manga, chibi, mecha, vintage photos,
banknotes, newspapers, magazines, movie posters, antique maps,
watercolor/oil originals, patterns, cosmetics, real estate, 3D
renders, and 60+ more.

pre-commit: 1) maintain.sh - exit 1, known lightning advisory; core green (ruff, format, pyright, 1731 tests); 2) /simplify - docs + research; 3) docs sync - artifacts in data/research/; 4) CLAUDE.md - no changes

* Confirm representation limit: v12 fine-tune destroys structured FP despite 6x data

Fine-tuning CLIP tower on 19,518 images achieves 95.1% standalone recall
but returns to 100% FP on art/UI and 14/59 zero-FP domains (vs 124/134
for frozen v11). This is a representation property, not a data issue.
Frozen CLIP + MLP (v11) is the only working architecture. Also records
provider v3 trained on all negative domains.

pre-commit: 1) maintain.sh - exit 1, known lightning advisory; core green (ruff, format, pyright, 1731 tests); 2) /simplify - docs + research; 3) docs sync - artifacts in data/research/; 4) CLAUDE.md - no changes

* Document v13 plateau: 128/136 zero FP, frozen-CLIP ceiling confirmed

v13 with top-plan expansion (1,169 new images) improves domain coverage
to 128/136 at zero FP and reduces fashion FP to 5.3%, but DEFINITELY
recall plateaus at 89.6% and art FP at 15.9%. The frozen CLIP + MLP
architecture has reached its ceiling.

pre-commit: 1) maintain.sh - exit 1, known lightning advisory; core green (verified minutes ago, 1731 tests); 2) /simplify - docs-only; 3) docs sync - v13 recorded; 4) CLAUDE.md - no changes

* Document the 2026-08-31 photo-classifier freeze.

Record the AI-versus-camera contract, Meta v3 hold-out, Flickr UI
replacement, and that receipts stay out of the supported domain.

* Scrub Spaces catalog details from classifier research docs

* Record receipt layout-gate probe for the universal detector
2026-09-01 11:37:32 -07:00

Remove AI Watermarks

Remove AI provenance marks from images and video you generated yourself:

  • known visible labels such as the Google Gemini sparkle watermark and vendor text marks;
  • invisible pixel watermarks through diffusion regeneration;
  • C2PA, EXIF, XMP, IPTC, and related AI metadata.

Video support covers provenance identification, complete visible-plus-metadata cleaning, directory batches, visible Sora, Veo, Seedance, Dola, Hailuo AI, and Kling AI mark removal, and oracle-certified VAE regeneration for video SynthID removal.

raiw.cc runs this library as a hosted service, with the GPU included and nothing to install. Visible mark and metadata removal at Standard output up to 12 MP are free there; original resolution above 12 MP and invisible watermark removal are paid.

PyPI Python Downloads License Tests Sponsor

This project is for lawful use on content you own. It does not target stock agency previews or other watermarks that protect third party paid content. See scope, safety, and legal notes.

Choose what you want to do

Goal Command GPU
Find provenance signals and watermarks identify No
Classify a photograph from pixels (opt-in, not provenance) classify No
Verify supported OpenAI SynthID from pixels with the official remote API verify-openai-synthid No
Remove known visible AI marks visible No
Erase a region you select erase No
Strip AI metadata metadata No
Identify supported video provenance video identify No
Remove visible marks and AI metadata from video video all No
Strip AI metadata from video video metadata No
Remove a registered visible AI mark from video video visible No
Process a directory of videos video batch Depends on mode
Remove video SynthID with the certified VAE profile video invisible Recommended
Regenerate an image to disrupt invisible watermarks invisible Required (CUDA)
Run visible, invisible, and metadata removal all Recommended
Process a directory batch Depends on mode

Microsoft Paint and Photos InvisMark declarations are routed automatically to pixel regeneration. The all command removes both the hidden pixel watermark and its linked C2PA manifest; metadata stripping alone removes only the manifest.

Installation modes

Need Install
Metadata inspection and stripping remove-ai-watermarks
Photograph AI-versus-camera classification remove-ai-watermarks[classify]
Official remote OpenAI SynthID verification remove-ai-watermarks[verify]
Visible detection and removal remove-ai-watermarks[visible]
Visible video processing remove-ai-watermarks[video]
Video SynthID removal remove-ai-watermarks[video,diffusion]
Torch-free DWT-DCT detection remove-ai-watermarks[detect]
Invisible image removal (needs CUDA) remove-ai-watermarks[qwen-zimage]
Every production feature available on the active Python remove-ai-watermarks[all]

Lower-level and specialized extras include pixels, heif, trustmark, migan, lama, and diffusion. The installation guide documents their exact dependency composition, Python compatibility, and model requirements.

Quick start

Install the metadata-focused default CLI:

uv tool install remove-ai-watermarks

Inspect an image:

remove-ai-watermarks identify image.png

To classify a photograph from pixels (AI versus camera, optional provider), install the extra and call classify. identify never starts it:

uv tool install --force "remove-ai-watermarks[classify]"
remove-ai-watermarks classify image.png

Guide: photo pixel classification.

Signed provenance is the supported route for SynthID and identify reads it. There is no local SynthID pixel detector in the package. Research on a periodic lattice expert is in scripts/synthid_runtime/ and synthid-detector-research.md.

For supported OpenAI images, the optional official verifier provides a pixel watermark verdict:

uv tool install --force "remove-ai-watermarks[verify]"
remove-ai-watermarks verify-openai-synthid image.png --acknowledge-upload

The command removes AI provenance metadata from a temporary copy, verifies that the decoded pixels are unchanged, uploads only that copy to OpenAI, and consumes only the independent SynthID response. It never runs implicitly from identify. An API key, endpoint access, and explicit upload acknowledgement are required.

For visible watermark removal, install the pixel dependencies:

uv tool install --force "remove-ai-watermarks[visible]"

Then remove a known visible mark and AI metadata:

remove-ai-watermarks visible image.png -o clean.png

Strip metadata without running visible inpainting or diffusion:

remove-ai-watermarks metadata image.png --remove -o clean.png

Without -o this command overwrites the source in place.

Inspect or remove AI metadata from an MP4, MOV, M4V, WebM, MKV, AVI, or FLV file:

remove-ai-watermarks video metadata input.mp4 --check
remove-ai-watermarks video metadata input.mp4 --remove -o clean.mp4

The video metadata command does not transcode video or audio streams. Unlike the image command above, when -o is omitted it writes <source>_clean and preserves the original. MP4 and MOV inspection includes the native TC260 AIGC tag in moov.udta.meta.keys/ilst, including a moov placed after the media payload, plus the QuickTime-form meta variants Doubao's iOS export writes (a bare meta box as a direct moov child, and a keyless hdlr=mdir metadata list). MKV and WebM inspection reads the normative Segment.Tags.Tag.SimpleTag placement. AVI uses LIST/INFO/AIGC, while FLV uses script.onMetaData.AIGC. The non-ISOBMFF formats are remuxed with stream copy for removal.

Use the product-oriented video path to identify or clean a file:

uv tool install --force "remove-ai-watermarks[video]"
remove-ai-watermarks video identify input.mp4
remove-ai-watermarks video all input.mp4 -o clean.mp4

video all removes a stable registered visible mark when present and always strips verified AI metadata. If neither signal is found, it still writes a same-container passthrough, so application callers get one predictable output contract. Proprietary invisible-video removal is excluded by default. --invisible opts into the lossy, oracle-certified video SynthID profile.

Process a directory with the same contract:

remove-ai-watermarks video batch ./videos --mode all

Remove a supported visible video mark:

remove-ai-watermarks video visible input.mp4 -o clean.mp4
remove-ai-watermarks video visible veo.mp4 --mark veo -o veo_clean.mp4
remove-ai-watermarks video visible seedance.mp4 --mark seedance -o seedance_clean.mp4
remove-ai-watermarks video visible dola.mp4 --mark dola -o dola_clean.mp4
remove-ai-watermarks video visible hailuo.mp4 --mark hailuo -o hailuo_clean.mp4
remove-ai-watermarks video visible kling.mp4 --mark kling -o kling_clean.mp4

This path scans the complete sequence before changing pixels. It accepts only a mark that repeats at a stable position across adjacent frames, then reuses the same OpenCV, MI-GAN, or LaMa fill backends as image removal. Audio is copied without re-encoding and is allowed to reach its natural end; the video stream is transcoded because its pixels change. By default, a guarded optical-flow pass motion-aligns the preceding accepted fill and blends it only when the nearby source context agrees; use --no-temporal-consistency to disable it. The encoder preserves supported 8-bit source chroma sampling, color tags, and MP4/MOV track timescale instead of relying on ffmpeg's implicit raw-BGR defaults. Variable frame intervals are preserved through a timestamped in-memory NUT bridge instead of being flattened to the average frame rate. Non-zero source start timestamps are retained together with the copied audio offset. The default --mark auto scans all providers in one decode pass and selects the first stable match in the specificity order shown below. Pass an explicit mark to restrict detection to one provider. Sora covers the moving Sora 2 mascot and wordmark. Veo covers both the current four-point diamond and the legacy Veo text. Seedance covers the fixed boxed AI label, Dola covers the fixed Dola AI text, Hailuo AI covers the composite MINIMAX | hailuo AI label, and Kling AI covers the bottom-right KLING AI label with its version suffix. A completed encode is published atomically. No output is written when no stable mark is found. HDR, PQ/HLG, and greater-than-8-bit inputs are rejected before encoding rather than silently reduced through OpenCV's 8-bit BGR boundary.

Remove video SynthID:

uv tool install --force "remove-ai-watermarks[video,diffusion]"
remove-ai-watermarks video invisible input.mp4 -o clean.mp4

This path regenerates the complete sequence with one latent-noise field shared across time, copies complete audio, strips source metadata, and publishes the completed encode atomically. The default noise_std=0.15 profile passed both the two-carrier calibration and a complete public eight-second Veo oracle check. Google does not publish a local decoder, so a fresh provider check remains useful for unusually important files or after provider changes, but it is not a product result state.

For invisible watermark removal, install the qwen-zimage extra. An NVIDIA GPU is required: all profiles are CUDA-only, and there is no CPU or MPS fallback.

uv tool install --force "remove-ai-watermarks[qwen-zimage]"
remove-ai-watermarks invisible image.png -o clean.png

If the local detectors cannot confirm an invisible watermark but you know the image came from an AI generator, add --force:

remove-ai-watermarks invisible image.png -o clean.png --force

Typography-heavy images can opt into the experimental verified-text post-pass. It accepts manually reviewed strings and line boxes or an operator-verified geometry-only manifest; it never treats raw OCR output as ground truth or runs automatically:

uv tool install --force "remove-ai-watermarks[text-restoration]"
remove-ai-watermarks invisible image.png -o clean.png \
  --text-manifest verified-lines.json --force

See the CLI guide for the manifest schema, compatibility restrictions, and oracle caveats.

See the installation guide for Homebrew, uv, optional features, and development setup.

Examples

Visible Gemini mark

Before After
Image with a visible Gemini watermark Image after visible watermark removal

The after raster is generated from the tracked before raster by the public path:

uv run remove-ai-watermarks visible demo_banana_before.png \
  --backend cv2 -o demo_banana_after.png

High quality invisible removal

qwen-zimage is the default profile: a Qwen-Image-2512 Lightning pass under Canny ControlNet, followed by SAM-masked Z-Image repair of any detected face. The alternative, sdxl-zimage, swaps the global stage for SDXL and keeps the same face stage. A third profile, chroma-zimage, uses the Apache-2.0 Chroma1 global pass with its own flat vendor floors; see docs/chroma1-engine-research.md for the calibration. --pipeline auto picks chroma-zimage for OpenAI and Microsoft provenance and qwen-zimage otherwise. All are CUDA only.

uv tool install --force "remove-ai-watermarks[qwen-zimage]"
remove-ai-watermarks invisible image.png -o clean.png --force
OpenAI example before OpenAI example after
OpenAI portrait grid before qwen-zimage OpenAI portrait grid after qwen-zimage
Gemini example before Gemini example after
Gemini sign before qwen-zimage Gemini sign after qwen-zimage

These exact output files were checked with the matching provider verifiers. That result applies to these files, not to every seed, image, or future watermark version.

Common recipes

Remove every detected visible mark

remove-ai-watermarks visible image.png -o clean.png

The default --mark auto checks all registered visible marks and removes every match. If the mark is visible to you but the detector misses it, select its region explicitly:

remove-ai-watermarks erase image.png \
  --region 1640,1930,400,100 \
  -o clean.png

--region uses x,y,width,height and may be repeated.

Use a learned fill backend

The visible extra uses OpenCV inpainting when no learned backend is installed. For more difficult backgrounds, the learned-backend extras include the same pixel dependencies automatically:

uv tool install --force "remove-ai-watermarks[migan]"
remove-ai-watermarks visible image.png -o clean.png --backend migan
uv tool install --force "remove-ai-watermarks[lama]"
remove-ai-watermarks visible image.png -o clean.png --backend lama

Reduce CUDA memory use

remove-ai-watermarks invisible image.png -o clean.png \
  --cpu-offload --force

CPU offload lowers CUDA memory pressure by moving model components between CPU and GPU, at the cost of speed.

Process a directory

remove-ai-watermarks batch ./images --mode visible
remove-ai-watermarks batch ./images --mode all

What the tool can recognize

Visible mark support includes:

  • Google Gemini and Nano Banana visible sparkle watermark;
  • Doubao, the Jimeng wordmark and top-left AI生成 pill, Qwen, Kling AI, Yuanbao, Baidu, LiblibAI, and RunningHub labels;
  • one calibrated Microsoft top-right white AI-badge variant;
  • one calibrated Samsung Galaxy AI label variant.

Metadata and provenance inspection covers C2PA, EXIF, XMP, IPTC, common generator parameters, China TC260 AIGC labels, and several vendor specific signals. Optional decoders add support for open DWT-DCT watermarks and Adobe TrustMark.

The exact support matrix, including important locale and detector limits, lives in supported signals. The synthetic example gallery carries one canonical sample for every registered image and video mark.

How it works

Visible removal follows three steps:

  1. Detect a registered mark in its expected area.
  2. Build a mask around the mark.
  3. Fill only the masked region with OpenCV, MI-GAN, or LaMa.

Metadata removal uses format aware stripping. JPEG metadata removal preserves the encoded image scan instead of recompressing it. Native MP4/MOV TC260 values are blanked without changing box sizes or media offsets. Other supported containers use their corresponding metadata path.

Invisible removal is different. It regenerates the image through a diffusion pipeline to disrupt pixel and frequency domain watermarks. This changes the image and cannot guarantee that a proprietary verifier will reject every output.

See supported signals and known limitations for the full technical boundary.

Python API

The visible-removal API requires remove-ai-watermarks[visible].

import remove_ai_watermarks as raiw

result, removed = raiw.remove_visible("watermarked.png", "clean.png")
print(removed)

openai_synthid = raiw.verify_openai_synthid("image.png", acknowledge_upload=True)
print(openai_synthid.status)

provenance = raiw.identify_video("input.mp4")
report = raiw.inspect_video_metadata("input.mp4")
complete = raiw.remove_video_all("input.mp4", "clean.mp4")
batch = raiw.remove_video_batch("videos", "videos_clean")
cleaned = raiw.remove_video_metadata("input.mp4")
synthid_cleaned = raiw.remove_video_invisible("input.mp4", "synthid_clean.mp4")
visible = raiw.remove_video_visible("input.mp4", "clean.mp4")
print(visible.mark)
veo = raiw.remove_video_visible("veo.mp4", "veo_clean.mp4", mark="veo")
seedance = raiw.remove_video_visible(
    "seedance.mp4",
    "seedance_clean.mp4",
    mark="seedance",
)
dola = raiw.remove_video_visible("dola.mp4", "dola_clean.mp4", mark="dola")

The high level API accepts a file path or a BGR NumPy array. For path inputs it also reads provenance metadata, preserves alpha, and can strip AI metadata from the written result.

See the Python API guide for visible removal, the full remove_all and remove_batch pipeline, provenance inspection, metadata stripping, and diffusion usage.

ComfyUI

The separate ComfyUI Remove AI Watermarks package provides nodes for visible removal, detection, region erasing, and invisible removal.

Important limitations

  • A missing local signal means unknown, not clean. Proprietary pixel watermarks may remain after metadata has been stripped.
  • Visible removal reconstructs a small region. Results depend on the background and selected fill backend.
  • Invisible removal changes the whole image and may alter faces, text, or fine detail.
  • Visible video removal recognizes the moving Sora 2 wordmark, the current Veo diamond plus legacy Veo text, the Seedance boxed AI label, and the fixed Dola, Hailuo AI, and Kling AI labels. It does not recognize the older Sora Turbo corner swirl or unregistered layouts from those providers. The classical OpenCV backend can smear structured backgrounds; use MI-GAN or LaMa when recovery quality matters.
  • Video SynthID regeneration changes resolution, frame rate, and image detail. The shipped profile is oracle-certified, but no public local decoder can certify an arbitrary output at runtime. Recheck unusually important outputs after provider changes.
  • Invisible-watermark removal requires CUDA. All profiles refuse any other device at construction rather than falling back to one that cannot run them. Visible removal, metadata stripping and identify still run anywhere.
  • Provider watermark systems can change. Validate important outputs with the provider's own verifier when one is available.

The shipped video invisible command uses the certified noise_std=0.15 profile. The companion scripts/video_synthid_sweep.py research harness builds a matched re-encode control plus VAE-regenerated candidates and leaves the verifier verdict blank:

uv run --extra video --extra diffusion python scripts/video_synthid_sweep.py input.mp4 -o sweep/

To score what an output actually cost, use scripts/video_fidelity_probe.py: the engine's own PSNR is measured before the resize and the encode, so only the probe sees the delivered picture.

uv run --extra video python scripts/video_fidelity_probe.py input.mp4 input_clean.mp4

The control must still be SynthID-positive before a negative candidate can count as removal evidence. In the 2026-07-29 two-clip calibration, both matched controls were positive in Gemini's built-in SynthID verifier; the stronger candidate was negative on both carriers, while a weaker candidate was negative on one. A later adversarial follow-up that asked ordinary Gemini to reinterpret the pixel result returned UNAVAILABLE; that follow-up was not a verifier rerun and does not invalidate the built-in verdicts. A 2026-07-31 full-clip check on a public eight-second Veo sample found 0.10 still detected and 0.15 not detected, so 0.15 is now the certified default. The reproducible hashes and verdicts live in data/evaluations/video-synthid-oracle.csv.

Documentation

Start with the documentation index.

Research notes and historical experiments are listed separately in the documentation index. They explain past decisions but do not define the current public API.

Contributing

Install the development environment and run the project gate:

uv sync --frozen --extra dev
bash maintain.sh

See module internals before changing a subsystem with documented invariants.

License

Apache 2.0. Copyright 2025-2026 wiltodelta.

Languages
Python 100%