Release 0.22.0 with composable feature extras

This commit is contained in:
Victor Kuznetsov
2026-07-31 10:39:13 -07:00
parent 9c9e81c756
commit 08dc078d91
32 changed files with 567 additions and 172 deletions
+33 -5
View File
@@ -9,15 +9,35 @@ remove-ai-watermarks [OPTIONS] COMMAND [ARGS]
Run `remove-ai-watermarks COMMAND --help` for the complete option list and
defaults. This page focuses on choosing the right command.
## Command dependency map
| Command or signal | Required installation |
| --- | --- |
| `metadata` and metadata-only `identify` | Default package |
| Visible signals in `identify` | `remove-ai-watermarks[visible]` (`pixels` is the minimal runtime) |
| Open DWT-DCT signals in `identify` | `remove-ai-watermarks[detect]` |
| Adobe TrustMark signals in `identify` | `remove-ai-watermarks[trustmark]` |
| `visible` and `erase` with OpenCV | `remove-ai-watermarks[visible]` (`pixels` is the minimal runtime) |
| `visible` or `erase` with MI-GAN | `remove-ai-watermarks[migan]` |
| `visible` or `erase` with big-LaMa | `remove-ai-watermarks[lama]` |
| `invisible` | `remove-ai-watermarks[diffusion]` |
| `invisible --pipeline qwen-zimage` | `remove-ai-watermarks[qwen-zimage]` |
| HEIC/HEIF/AVIF pixel input | Add `remove-ai-watermarks[heif]` |
| Every production command and backend | `remove-ai-watermarks[all]` |
`batch` requires the same extra as its selected mode. Extras can be combined in
one installation, for example `remove-ai-watermarks[visible,detect,heif]`.
## Inspect an image
```bash
remove-ai-watermarks identify image.png
```
`identify` combines supported metadata and pixel signals into one provenance
report. When no signal is found, it reports the origin as unknown. It does not
claim the image is clean.
`identify` always inspects supported metadata. When pixel extras are installed,
it also evaluates supported visible and invisible pixel signals. When no signal
is found, it reports the origin as unknown. It does not claim the image is
clean.
Machine readable output:
@@ -36,6 +56,8 @@ invisible pixel detectors. Metadata inspection still runs.
## Remove known visible marks
Install `remove-ai-watermarks[visible]` before using `visible` or `erase`.
```bash
remove-ai-watermarks visible image.png -o clean.png
```
@@ -130,7 +152,7 @@ non-ISOBMFF audio and video path.
Install the diffusion dependencies first:
```bash
uv tool install --force "remove-ai-watermarks[gpu]"
uv tool install --force "remove-ai-watermarks[diffusion]"
```
Then run:
@@ -194,6 +216,12 @@ It is a memory strategy, not a guarantee of better quality.
## Run the full pipeline
The `all` command and the `all` installation extra are separate concepts. The
command runs every applicable stage. Installing `remove-ai-watermarks[all]`
makes every production backend available; a smaller installation such as
`remove-ai-watermarks[visible,diffusion]` can also run the command with fewer
optional backends.
```bash
remove-ai-watermarks all image.png -o clean.png
```
@@ -206,7 +234,7 @@ The command runs:
The visible options and diffusion options are also available on `all`.
If diffusion is required but the `gpu` extra is unavailable, `all` still
If diffusion is required but the `diffusion` extra is unavailable, `all` still
writes the result of the visible and metadata stages, prints a prominent
warning, and exits with code 1. This prevents a partial result from being
reported as complete.
+3 -3
View File
@@ -6,15 +6,15 @@ Read this reference for environment setup, dependency recovery, CI behavior, and
- Use `uv sync --frozen --extra dev` and add only the feature extras needed for the task.
- Do not use `uv pip install` for development tools. It can re-resolve `uv.lock` outside the compatible ML dependency set.
- A core-only sync removes GPU packages by design. Package imports remain light through lazy exports; only removal paths should require the heavy stack.
- On an unreliable connection, sync the needed `dev` and `gpu` extras and run the lint, type, and test commands directly instead of downloading every optional learned backend.
- A default-only sync removes every pixel and model package by design. Package imports remain light through lazy exports.
- On an unreliable connection, sync `dev` plus only the required feature extras, such as `diffusion`, and run the checks directly instead of downloading every optional learned backend.
- Run `uv` from the repository root or it may create a bare environment without the project dependencies.
The optional TrustMark decoder downloads weights into its installed package directory. After pruning that extra, a leftover weights directory can make availability checks see an empty namespace package. If Pyright reports an unknown `TrustMark` import and `find_spec("trustmark")` returns a loader-less spec, remove that regenerable remnant from the active virtual environment and resync.
## CI
`.github/workflows/test.yml` runs Ruff and a cross-platform supported-Python test matrix with core plus development dependencies. GPU and model-running tests skip in that matrix; metadata, identification, visible removal, and the OpenCV eraser remain covered across operating systems.
`.github/workflows/test.yml` runs Ruff and a cross-platform supported-Python test matrix with default plus development dependencies. Diffusion and model-running tests skip in that matrix; metadata, identification, visible removal, the DWT-DCT decoder, and the OpenCV eraser remain covered across operating systems.
Keep `uv.lock` compatible with `uv sync --frozen`. Dependency pull-request checks use GitHub's merge result against current `main`; if `main` moves, merge it locally and rerun the full gate because a newer linter can expose stale directives in later code.
+83 -21
View File
@@ -2,15 +2,17 @@
Python 3.10.1 or newer is required.
## Core install
## Default metadata mode
The core package provides:
The default package provides:
- provenance inspection;
- visible watermark removal with OpenCV;
- manual region erasing with OpenCV;
- 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:
```bash
@@ -29,12 +31,27 @@ You can also install the Homebrew package on macOS or Linux:
brew install wiltodelta/tap/remove-ai-watermarks
```
## Invisible watermark removal
## Visible watermark removal
Diffusion based removal needs the `gpu` extra:
Visible mark detection, OpenCV inpainting, and manual region erasing need the
`visible` extra:
```bash
uv tool install --force "remove-ai-watermarks[gpu]"
uv tool install --force "remove-ai-watermarks[visible]"
```
Add `heif` only when the pixel path must decode HEIC, HEIF, or AVIF:
```bash
uv tool install --force "remove-ai-watermarks[visible,heif]"
```
## Invisible watermark removal
Diffusion based removal needs the `diffusion` extra:
```bash
uv tool install --force "remove-ai-watermarks[diffusion]"
```
The code supports CUDA, XPU, MPS, and CPU devices. A GPU is recommended because
@@ -46,28 +63,73 @@ For the CUDA only Qwen Image plus Z-Image profile:
uv tool install --force "remove-ai-watermarks[qwen-zimage]"
```
The `qwen-zimage` extra includes the normal `gpu` dependencies.
The `qwen-zimage` extra includes the normal `diffusion` dependencies.
## Optional features
## Feature extras
Install only what you need:
Extras are composable. Install only the capabilities and file formats the
application actually uses:
| Extra | Adds |
| --- | --- |
| `migan` | MI-GAN ONNX fill backend |
| `lama` | big-LaMa ONNX fill backend |
| `detect` | Open DWT-DCT watermark decoder used by `identify` |
| `trustmark` | Adobe TrustMark decoder |
| `esrgan` | Real-ESRGAN upscaling before diffusion |
| `qwen-zimage` | CUDA only Qwen Image plus Z-Image pipeline |
| 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 |
| `detect` | Open DWT-DCT detection for Stable Diffusion, SDXL, and FLUX | `pixels`, PyWavelets | No |
| `trustmark` | Adobe TrustMark detection | trustmark | Yes |
| `diffusion` | Diffusion-based invisible watermark removal | `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 |
| `esrgan` | Real-ESRGAN upscaling before diffusion | `pixels`, spandrel | Yes |
| `qwen-zimage` | CUDA-only Qwen Image plus Z-Image pipeline | `diffusion`, DiffSynth | Yes |
| `all` | Every production feature | All rows above | Yes |
| `dev` | Tests, linting, typing, and upstream parity checks | `visible`, `detect`, upstream invisible-watermark | Yes, for parity tests |
Example:
Dependency composition:
```mermaid
flowchart LR
visible --> pixels
detect --> pixels
diffusion --> pixels
migan --> visible
lama --> visible
esrgan --> pixels
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:
```bash
# 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]"
# 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]"
```
Some optional models download their weights on first use.
`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
@@ -81,7 +143,7 @@ Add the feature groups required for your work:
```bash
uv sync --frozen --extra dev
uv sync --frozen --extra dev --extra gpu
uv sync --frozen --extra dev --extra diffusion
```
Run commands from the repository root:
+7 -4
View File
@@ -11,7 +11,8 @@ superseded experiments live in the research archive listed in
Visible removal changes only the selected mask, but the hidden pixels still
have to be reconstructed.
- OpenCV is fast and dependency free. It works well on flat backgrounds but
- OpenCV is fast and requires no model download. It works well on flat
backgrounds but
can smear texture or repeated structure.
- MI-GAN is a lighter learned backend. It can improve natural texture but may
ghost or invent structure.
@@ -162,11 +163,13 @@ The metadata path recognizes JPEG XL containers, but the visible and diffusion
image paths do not list `.jxl` as a supported pixel format because the package
does not include a JPEG XL pixel decoder.
### HEIC, HEIF, and AVIF use a Pillow fallback
### HEIC, HEIF, and AVIF pixel decoding uses an optional Pillow fallback
OpenCV does not decode these formats in the project. `image_io.imread` falls
back to Pillow with `pillow-heif`. A corrupt or truncated file may still fail to
decode.
back to Pillow with `pillow-heif` when the `heif` extra is installed alongside
a pixel feature. The
default metadata path scans these containers without that plugin. A corrupt or
truncated file may still fail to decode.
### Some metadata removal requires ffmpeg
+7 -1
View File
@@ -144,6 +144,11 @@ metadata extraction from verdict logic:
- `identify` preserves the path-based API and adds the optional registered
visible-mark and open invisible-watermark decoders after extraction.
The `detect` extra composes the shared `pixels` runtime with PyWavelets. Its
in-tree [`dwt_dct.py`](../src/remove_ai_watermarks/dwt_dct.py) decoder preserves
the upstream matrix algorithm without installing Torch or non-headless OpenCV.
The upstream MIT notice ships inside the wheel under `licenses/`.
`is_ai_generated` is `True` or `None`; absence of evidence is not reported as a
human-made verdict. `ai_source_kind` distinguishes fully generated content from
AI-enhanced composites when the source metadata provides that distinction.
@@ -379,7 +384,8 @@ Contracts:
- `to_bgr` normalizes grayscale and alpha-bearing arrays.
- `read_bgr_and_alpha` and `write_bgr_with_alpha` preserve the alpha plane.
- `imwrite` returns a success flag; every caller must check it.
- HEIC, HEIF, and AVIF fall back to Pillow plus `pillow-heif`.
- HEIC, HEIF, and AVIF pixel reads fall back to Pillow plus `pillow-heif` from
the independent `heif` extra. Metadata scanning does not require that plugin.
- A visible no-op can preserve the original file bytes.
Regression coverage:
+15
View File
@@ -3,8 +3,17 @@
Use the high level API for normal application integration. Low level detector
and pipeline modules are intended for maintainers and specialized workflows.
Dependency groups are identical for the CLI and Python API. The default install
covers metadata extraction, normalization, verdict logic, and stripping.
Array/pixel APIs use `pixels`; visible removal uses `visible`; DWT-DCT detection
uses `detect`; and diffusion removal uses `diffusion`. Add `heif` independently
when path-based pixel APIs must decode HEIC, HEIF, or AVIF. See the complete
[feature-extra matrix](installation.md#feature-extras).
## Remove visible marks
Install `remove-ai-watermarks[visible]` before using the visible-removal API.
```python
import remove_ai_watermarks as raiw
@@ -68,6 +77,9 @@ result, removed = raiw.remove_visible(image, backend="cv2")
## Inspect provenance
The default installation evaluates file metadata. Add `visible`, `detect`, or
`trustmark` to enable the corresponding optional pixel signals.
Get the vendor keys used by visible removal:
```python
@@ -172,6 +184,9 @@ as proof that metadata was removed.
## Remove invisible watermarks
Install `remove-ai-watermarks[diffusion]` for the standard pipelines or
`remove-ai-watermarks[qwen-zimage]` for the CUDA-only high-fidelity profile.
```python
from pathlib import Path
+3 -2
View File
@@ -55,8 +55,9 @@ manual Homebrew formula update is the fallback when its automation is blocked.
The conda job uses the published artifact rather than a locally built archive
as the hash source and commits the resulting recipe change to `main`. Runtime
dependency mapping remains review-controlled: keep it aligned with the core
dependencies in `pyproject.toml`, and document any conda-forge package that is
dependency mapping remains review-controlled: keep it aligned with the default
metadata dependencies in `pyproject.toml`, do not copy optional pixel extras
into the default recipe, and document any conda-forge package that is
unavailable and must be omitted.
## Source distribution boundary
+4 -1
View File
@@ -33,7 +33,7 @@ when you can select the affected area yourself.
| Backend | Install | Behavior |
| --- | --- | --- |
| `cv2` | Core package | Classical OpenCV inpainting |
| `cv2` | `remove-ai-watermarks[visible]` | Classical OpenCV inpainting |
| `migan` | `remove-ai-watermarks[migan]` | MI-GAN through ONNX Runtime |
| `lama` | `remove-ai-watermarks[lama]` | big-LaMa through ONNX Runtime |
| `auto` | Depends on installed extras | Selects LaMa, then MI-GAN, then OpenCV |
@@ -69,6 +69,9 @@ Pixel based image commands discover these extensions:
- HEIC and HEIF;
- AVIF.
HEIC, HEIF, and AVIF pixel decoding requires the independent `heif` extra in
addition to the selected pixel feature. Metadata scanning does not.
Metadata inspection and removal additionally have container paths for:
- JPEG XL metadata;
+1 -1
View File
@@ -224,7 +224,7 @@ from the test set + this doc).
## 6. Integration cost (rough)
- New deps: `diffusers` already in the gpu extra; PhotoMaker ships as a `.bin`
- New deps: `diffusers` already in the diffusion extra; PhotoMaker ships as a `.bin`
loaded via `pipeline.load_photomaker_adapter(...)`. The OpenCLIP encoder is the
same one diffusers already pulls. No new heavy pip dep.
- Weight download: PhotoMaker-V1 weights are ~3 GB. Add to the Modal HF volume