mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-08-19 12:07:13 +02:00
Support Python 3.11 through 3.14
This commit is contained in:
@@ -28,13 +28,26 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Cross-OS x Python-floor/recent. The default-install surface only
|
||||
# needs the core deps plus dev tooling (no GPU extra): the model-running
|
||||
# paths are availability-gated and skip when torch/diffusers are absent,
|
||||
# so this matrix exercises metadata, identify, visible, and the cv2
|
||||
# eraser on every OS without pulling the heavy ML stack.
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
python-version: ["3.10", "3.12"]
|
||||
# Exercise every supported minor. The floor and latest minor run on all
|
||||
# operating systems; intermediate minors run on Linux to bound the
|
||||
# matrix without leaving a declared Python version untested.
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.11"
|
||||
- os: macos-latest
|
||||
python-version: "3.11"
|
||||
- os: windows-latest
|
||||
python-version: "3.11"
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.12"
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.13"
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.14"
|
||||
- os: macos-latest
|
||||
python-version: "3.14"
|
||||
- os: windows-latest
|
||||
python-version: "3.14"
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
|
||||
@@ -54,12 +54,12 @@ removal.
|
||||
| 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 | `remove-ai-watermarks[all]` |
|
||||
| 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](docs/installation.md#feature-extras) documents their exact
|
||||
dependency composition and model requirements.
|
||||
dependency composition, Python compatibility, and model requirements.
|
||||
|
||||
## Quick start
|
||||
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@ defaults. This page focuses on choosing the right command.
|
||||
| `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]` |
|
||||
| Adobe TrustMark signals in `identify` on Python 3.11-3.12 | `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]` |
|
||||
@@ -26,7 +26,7 @@ defaults. This page focuses on choosing the right command.
|
||||
| `video visible`, `video all`, and visible/all batch modes | `remove-ai-watermarks[video]` plus ffmpeg on PATH |
|
||||
| `video invisible` and `video all --invisible` | `remove-ai-watermarks[video,diffusion]` plus ffmpeg on PATH |
|
||||
| HEIC/HEIF/AVIF pixel input | Add `remove-ai-watermarks[heif]` |
|
||||
| Every production command and backend | `remove-ai-watermarks[all]` |
|
||||
| Every production command and Python-compatible 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]`.
|
||||
@@ -426,7 +426,7 @@ It is a memory strategy, not a guarantee of better quality.
|
||||
|
||||
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
|
||||
makes every backend compatible with the active Python available; a smaller installation such as
|
||||
`remove-ai-watermarks[visible,qwen-zimage]` can also run the command with fewer
|
||||
optional backends.
|
||||
|
||||
|
||||
+6
-1
@@ -14,7 +14,12 @@ The optional TrustMark decoder downloads weights into its installed package dire
|
||||
|
||||
## CI
|
||||
|
||||
`.github/workflows/test.yml` runs Ruff, a cross-platform test matrix over the supported-Python floor and the latest supported minor with default plus development dependencies, and a separate job that installs ffmpeg on Ubuntu to run the full-clip video test. 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.
|
||||
`.github/workflows/test.yml` runs Ruff, a test matrix over every supported Python
|
||||
minor with default plus development dependencies, cross-platform coverage at the
|
||||
oldest and newest minors, and a separate job that installs ffmpeg on Ubuntu to
|
||||
run the full-clip video test. 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.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Installation
|
||||
|
||||
Python 3.10.1 or newer is required.
|
||||
Python 3.11 through 3.14 are supported.
|
||||
|
||||
## Default metadata mode
|
||||
|
||||
@@ -99,12 +99,12 @@ application actually uses:
|
||||
| `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 |
|
||||
| `trustmark` | Adobe TrustMark detection on Python 3.11-3.12 | 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 |
|
||||
| `all` | Every production feature available on the active Python | All compatible rows above | Yes |
|
||||
| `dev` | Tests, linting, typing, and upstream parity checks | `video`, `detect`, upstream invisible-watermark | Yes, for parity tests |
|
||||
|
||||
Dependency composition:
|
||||
@@ -123,8 +123,10 @@ flowchart LR
|
||||
```
|
||||
|
||||
`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`.
|
||||
another feature when required. TrustMark requires NumPy 1.x, which has no
|
||||
CPython 3.13 or 3.14 wheels, so that branch is available only on Python
|
||||
3.11-3.12. The `all` bundle contains every production branch compatible with
|
||||
the active Python and never includes `dev`.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -141,7 +143,7 @@ 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
|
||||
# Every production capability compatible with this Python
|
||||
uv tool install --force "remove-ai-watermarks[all]"
|
||||
|
||||
# An arbitrary minimal combination
|
||||
|
||||
@@ -347,8 +347,10 @@ The `trustmark` extra adds Adobe TrustMark decoding. The implementation retains
|
||||
an additional JPEG re-encode gate and requires the binary payload and schema to
|
||||
remain identical because isolated decoder hits can otherwise be content noise.
|
||||
It accepts Variant P schemas 0-2. Variant Q requires a different model, and
|
||||
schema 3 is rejected at the measured precision threshold. The calibration
|
||||
history is in [module internals](module-internals.md#metadata-and-provenance).
|
||||
schema 3 is rejected at the measured precision threshold. Its NumPy 1.x runtime
|
||||
limits the extra to Python 3.11-3.12; the rest of the package remains supported
|
||||
through Python 3.14. The calibration history is in
|
||||
[module internals](module-internals.md#metadata-and-provenance).
|
||||
|
||||
External AI versus real image classifiers are out of scope. The project
|
||||
identifies concrete local provenance signals instead of shipping a generic
|
||||
|
||||
+14
-20
@@ -7,7 +7,7 @@ name = "remove-ai-watermarks"
|
||||
version = "0.26.3"
|
||||
description = "AI watermark remover for visible, invisible, and provenance marks in images and video"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10.1"
|
||||
requires-python = ">=3.11,<3.15"
|
||||
license = {text = "Apache-2.0"}
|
||||
keywords = [
|
||||
"ai-watermark",
|
||||
@@ -37,7 +37,6 @@ classifiers = [
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
@@ -65,7 +64,10 @@ dependencies = [
|
||||
|
||||
[project.optional-dependencies]
|
||||
pixels = [
|
||||
"numpy>=1.24.0",
|
||||
"numpy>=1.24.0; python_version < '3.13'",
|
||||
# NumPy 1.x has no CPython 3.13+ wheels. Requiring 2.x prevents installers
|
||||
# from attempting an unsupported source build on the newer Python lines.
|
||||
"numpy>=2; python_version >= '3.13'",
|
||||
"opencv-python-headless>=4.8.0",
|
||||
]
|
||||
# Optional HEIC/AVIF pixel decode. Metadata scanning handles these containers
|
||||
@@ -75,12 +77,10 @@ heif = [
|
||||
]
|
||||
visible = ["remove-ai-watermarks[pixels]"]
|
||||
# Video visible removal uses the shared pixel runtime. PyAV packetizes processed
|
||||
# VFR frames with explicit PTS before system ffmpeg encodes them. PyAV 18 requires
|
||||
# Python 3.11; the 16.x wheel line still covers Python 3.10.
|
||||
# VFR frames with explicit PTS before system ffmpeg encodes them.
|
||||
video = [
|
||||
"remove-ai-watermarks[visible]",
|
||||
"av>=16,<17; python_version < '3.11'",
|
||||
"av>=18,<19; python_version >= '3.11'",
|
||||
"av>=18,<19",
|
||||
]
|
||||
# Open DWT-DCT watermarks used by Stable Diffusion / SDXL / FLUX. The in-tree
|
||||
# decoder avoids the upstream invisible-watermark package's mandatory torch and
|
||||
@@ -125,7 +125,9 @@ qwen-zimage = [
|
||||
# because it pulls torch and downloads model weights on first use. identify()
|
||||
# guards the import and skips the TrustMark signal when absent.
|
||||
trustmark = [
|
||||
"trustmark>=0.8.0",
|
||||
# TrustMark requires NumPy <2, whose last release has no CPython 3.13+
|
||||
# wheels. Keep this feature installable only on its real runtime range.
|
||||
"trustmark>=0.8.0; python_version < '3.13'",
|
||||
]
|
||||
# Universal region eraser backend -- big-LaMa via onnxruntime (Carve/LaMa-ONNX,
|
||||
# Apache-2.0). CPU, no torch. Model (~200 MB) is downloaded on first use and
|
||||
@@ -133,10 +135,7 @@ trustmark = [
|
||||
# eraser backend needs none of this.
|
||||
lama = [
|
||||
"remove-ai-watermarks[visible]",
|
||||
# ONNX Runtime 1.24 dropped CPython 3.10 wheels; keep the project's
|
||||
# supported 3.10 line on the last compatible release series.
|
||||
"onnxruntime>=1.16.0,<1.24; python_version < '3.11'",
|
||||
"onnxruntime>=1.16.0; python_version >= '3.11'",
|
||||
"onnxruntime>=1.24.0",
|
||||
"huggingface-hub>=0.20.0",
|
||||
]
|
||||
# Lightweight inpaint backend -- MI-GAN via onnxruntime (andraniksargsyan/migan,
|
||||
@@ -146,8 +145,7 @@ lama = [
|
||||
# LaMa, the quality-first `auto` choice, is too large. Same runtime as `lama`.
|
||||
migan = [
|
||||
"remove-ai-watermarks[visible]",
|
||||
"onnxruntime>=1.16.0,<1.24; python_version < '3.11'",
|
||||
"onnxruntime>=1.16.0; python_version >= '3.11'",
|
||||
"onnxruntime>=1.24.0",
|
||||
"huggingface-hub>=0.20.0",
|
||||
]
|
||||
dev = [
|
||||
@@ -160,10 +158,6 @@ dev = [
|
||||
"ruff>=0.4.0",
|
||||
"pyright>=1.1.0",
|
||||
"invisible-watermark>=0.2.0",
|
||||
# maintain.sh helpers; they only support newer Pythons, so gate them by
|
||||
# marker to keep the py3.10 resolution (and CI matrix) solvable.
|
||||
"uv-outdated>=0.1.0; python_version >= '3.12'",
|
||||
"uv-secure>=0.12.0; python_version >= '3.12'",
|
||||
]
|
||||
# ``qwen-zimage`` already pulls ``diffusion``; naming both would suggest diffusion is
|
||||
# independently sufficient for a removal, which it is not.
|
||||
@@ -204,7 +198,7 @@ pythonpath = ["src"]
|
||||
addopts = "-v --tb=short"
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py310"
|
||||
target-version = "py311"
|
||||
line-length = 120
|
||||
exclude = ["_refs"]
|
||||
extend-exclude = ["*.md"]
|
||||
@@ -227,7 +221,7 @@ quote-style = "double"
|
||||
indent-style = "space"
|
||||
|
||||
[tool.pyright]
|
||||
pythonVersion = "3.10"
|
||||
pythonVersion = "3.11"
|
||||
typeCheckingMode = "strict"
|
||||
exclude = ["_refs"]
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# /// script
|
||||
# requires-python = ">=3.10"
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "click",
|
||||
# "numpy",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# /// script
|
||||
# requires-python = ">=3.10"
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "diffusers>=0.35.0",
|
||||
# "transformers>=4.51.0",
|
||||
|
||||
@@ -22,7 +22,7 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import Counter
|
||||
from datetime import datetime, timezone
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
@@ -180,7 +180,7 @@ def ingest(
|
||||
"c2pa_issuer": issuer,
|
||||
"synthid_metadata": "yes" if synthid else "",
|
||||
"verified_via": verified_via,
|
||||
"added": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"added": datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"notes": notes,
|
||||
}
|
||||
)
|
||||
|
||||
+15
-2
@@ -5,19 +5,27 @@ from __future__ import annotations
|
||||
from importlib.metadata import metadata, requires
|
||||
|
||||
from packaging.requirements import Requirement
|
||||
from packaging.specifiers import SpecifierSet
|
||||
from packaging.utils import canonicalize_name
|
||||
|
||||
|
||||
def _requirement_names(extra: str | None = None) -> set[str]:
|
||||
def _requirement_names(extra: str | None = None, *, python_version: str | None = None) -> set[str]:
|
||||
selected_extra = extra or ""
|
||||
environment = {"extra": selected_extra}
|
||||
if python_version is not None:
|
||||
environment["python_version"] = python_version
|
||||
parsed = (Requirement(value) for value in requires("remove-ai-watermarks") or [])
|
||||
return {
|
||||
canonicalize_name(requirement.name)
|
||||
for requirement in parsed
|
||||
if requirement.marker is None or requirement.marker.evaluate({"extra": selected_extra})
|
||||
if requirement.marker is None or requirement.marker.evaluate(environment)
|
||||
}
|
||||
|
||||
|
||||
def test_supported_python_floor_is_published():
|
||||
assert SpecifierSet(metadata("remove-ai-watermarks")["Requires-Python"]) == SpecifierSet(">=3.11,<3.15")
|
||||
|
||||
|
||||
def test_default_install_is_metadata_focused():
|
||||
default = _requirement_names()
|
||||
|
||||
@@ -55,6 +63,11 @@ def test_file_format_and_detector_dependencies_are_independent():
|
||||
assert "pywavelets" in _requirement_names("detect")
|
||||
|
||||
|
||||
def test_trustmark_is_limited_to_its_numpy_compatible_python_range():
|
||||
assert "trustmark" in _requirement_names("trustmark", python_version="3.12")
|
||||
assert "trustmark" not in _requirement_names("trustmark", python_version="3.13")
|
||||
|
||||
|
||||
def test_extras_use_capability_names_without_legacy_aliases():
|
||||
extras = set(metadata("remove-ai-watermarks").get_all("Provides-Extra") or [])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user