Files
remove-ai-watermarks/pyproject.toml
T
Victor KuznetsovandClaude Opus 5.5 cc63bfd7bf Release 0.45.0
Bump the package to 0.45.0 and the agent skill to 1.0.11. The skill now names
the dola and workbuddy image mark keys, which 0.44.0 rejects, so the probe's
MIN_CLI_VERSION rises to 0.45.0 and both keys carry a "since CLI" note that
the floor test reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 17:45:18 -07:00

299 lines
11 KiB
TOML

[project]
# This name is the published PyPI distribution, not a local label: changing it
# strands every `pip install remove-ai-watermarks` and every pinned dependency
# on it, including the ComfyUI node package. The console script below carries
# the same weight, since users have it on PATH.
name = "remove-ai-watermarks"
version = "0.45.0"
description = "AI watermark remover for visible, invisible, and provenance marks in images and video"
readme = "README.md"
requires-python = ">=3.11,<3.15"
license = {text = "Apache-2.0"}
keywords = [
"ai-watermark",
"ai-watermark-remover",
"watermark-remover",
"watermark-removal",
"remove-watermark",
"synthid",
"c2pa",
"content-credentials",
"nano-banana",
"gemini",
"gemini-watermark",
"ai-metadata",
"metadata-removal",
"provenance",
"exif",
"stable-diffusion",
"comfyui",
"ai-detection",
]
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: Developers",
"Intended Audience :: End Users/Desktop",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Multimedia :: Graphics",
"Topic :: Multimedia :: Graphics :: Graphics Conversion",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Scientific/Engineering :: Image Processing",
"Topic :: Security",
"Topic :: Utilities",
]
dependencies = [
"pillow>=10.0.0",
"piexif>=1.1.3",
"click>=8.0.0",
"python-dotenv>=1.0.0",
# Official C2PA reader (Content Authenticity Initiative, MIT/Apache-2.0). The
# primary, spec-tracking manifest parser for the identify/metadata path; the
# hand-rolled caBX/CBOR scanner in _internal/c2pa.py is kept only as a fallback for
# synthetic/partial blobs the validator rejects. Binary wheel (Rust), but the
# import is light (no torch/numpy) so it fits the dependency-light identify
# host. Prebuilt wheels cover the full CI matrix (linux/macos/windows).
"c2pa-python>=0.38.0",
]
[project.optional-dependencies]
pixels = [
"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
# without this plugin; combine `heif` with any pixel feature only when needed.
heif = [
"pillow-heif>=0.13.0",
]
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.
video = [
"remove-ai-watermarks[visible]",
"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
# non-headless OpenCV dependencies.
detect = [
"remove-ai-watermarks[pixels]",
"PyWavelets>=1.1.1",
]
diffusion = [
"remove-ai-watermarks[pixels]",
# A CUDA-enabled torch build is required: invisible-watermark removal has no
# CPU, MPS or XPU path. The default PyPI wheel carries CUDA on Linux/Windows;
# on macOS there is no CUDA build and this extra installs only for the
# non-diffusion imports it shares.
"torch>=2.0.0",
"diffusers>=0.38.0",
# diffusers 0.38's auto-pipeline registry imports ``Qwen3VLForConditional
# Generation`` (its ``nucleusmoe_image`` pipeline), which only exists in
# transformers 5.x -- so ``from diffusers import AutoPipelineForImage2Image``
# fails on transformers 4.x. The real SDXL-loading break was NOT transformers
# 5.x but the tokenizers *release candidate* (0.23.0rc0) that the global
# ``prerelease = "allow"`` drags in: its CLIP tokenizer raises
# ``RobertaProcessing.__new__() got an unexpected keyword argument 'cls'``.
# Cap tokenizers to the verified stable 0.22 line so the rc is excluded
# while SDXL still loads.
# Transformers 5.16+ requires tokenizers 0.23.1+, outside the verified
# stable tokenizer line below. Keep the published extras jointly resolvable.
"transformers>=5,<5.16",
"tokenizers>=0.22,<0.23",
"accelerate>=0.25.0",
"safetensors",
]
# Full two-stage high-fidelity profile: Qwen-Image-2512 Lightning + DiffSynth
# Canny ControlNet for the frame, then SAM-masked Z-Image Turbo face repair.
# CUDA-only and intentionally separate from the normal diffusion extra because the
# additional model stack and DiffSynth runtime are large.
qwen-zimage = [
"remove-ai-watermarks[diffusion]",
"diffsynth>=2.0.17,<3",
# Chroma1-HD ships a binary spiece.model. Without the matching backend,
# Transformers falls through to tiktoken, which cannot parse that file.
"sentencepiece>=0.2.2,<0.3",
"torchvision>=0.20.0",
]
# Opt-in verified-text reconstruction over qwen-zimage. LaMa removes the changed
# candidate glyphs before exact Qwen-VAE cores are composited back.
text-restoration = [
"remove-ai-watermarks[qwen-zimage,lama]",
]
# Draft-only OCR proposals for verified-text manifests. CPU, no torch:
# PaddleOCR detection plus three script-chosen recognition engines. The models
# download on first use and are cached by huggingface_hub; never bundled.
# Keep every Hub extra below 2 so it remains composable with Diffusers (<2).
text-draft = [
"paddleocr>=3.3.3",
"paddlepaddle>=3.0.0",
"huggingface-hub>=0.20.0,<2",
]
# Adobe TrustMark decoder -- the open, keyless watermark behind Adobe Durable
# Content Credentials (soft-binding alg ``com.adobe.trustmark.P``). Optional
# because it pulls torch and downloads model weights on first use. identify()
# guards the import and skips the TrustMark signal when absent.
trustmark = [
# 0.9.2 (2026-09-08) dropped the NumPy <2 pin that kept earlier releases off
# CPython 3.13+; its 13 detector tests pass on 3.13 with NumPy 2.5.3.
"trustmark>=0.9.2",
]
# Metadata-free photo classifier (Model 1 then gated Model 2). CPU torch is
# enough. identify never imports this extra. Weights download on first use.
classify = [
"remove-ai-watermarks[pixels]",
"torch>=2.0.0",
"transformers>=5,<5.16",
"tokenizers>=0.22,<0.23",
"huggingface-hub>=0.20.0,<2",
]
# Optional vision-only CPU runtime for the photo classifier. Keep it separate
# because PyTorch remains the default and ONNX Runtime is needed only on opt-in.
classify-onnx = [
"remove-ai-watermarks[classify]",
"onnxruntime>=1.24.0",
]
# Compact OpenAI/Google/unknown source-export classifier. This is intentionally
# separate from the heavier photo AI-versus-camera classifier and is not a
# SynthID detector. Weights download on first use.
source-classify = [
"remove-ai-watermarks[pixels]",
"huggingface-hub>=0.20.0,<2",
]
# 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
# cached by huggingface_hub; it is never bundled in this repo. The default cv2
# eraser backend needs none of this.
lama = [
"remove-ai-watermarks[visible]",
"onnxruntime>=1.24.0",
"huggingface-hub>=0.20.0,<2",
]
# Lightweight inpaint backend -- MI-GAN via onnxruntime (andraniksargsyan/migan,
# MIT). CPU, no torch. Model (~28 MB) downloaded on first use and cached by
# huggingface_hub; never bundled. ~700-950 MB peak RAM / ~0.19 s/call -- the
# memory-tight learned tier (vs big-LaMa's ~4.7 GB). Select it explicitly when
# LaMa, the quality-first `auto` choice, is too large. Same runtime as `lama`.
migan = [
"remove-ai-watermarks[visible]",
"onnxruntime>=1.24.0",
"huggingface-hub>=0.20.0,<2",
]
dev = [
"remove-ai-watermarks[video]",
"remove-ai-watermarks[detect]",
# Development-only OpenAI Content Provenance oracle. It is not exposed by
# the installed CLI or the public Python API.
"openai>=2.52.0",
# Development-only local AudioSeal audio watermark oracle for the
# scripts/audioseal_experiment.py provenance study. Not part of the
# installed CLI, the public Python API, or any runtime pipeline.
"audioseal>=0.2.0",
"huggingface-hub>=0.20.0,<2",
# Direct Microsoft Content Provenance API transport. The adapter uses an
# Azure CLI Entra token and ignores process proxy settings.
"httpx>=0.28,<1",
# Development-only isolated browser automation for public provider oracles.
"playwright>=1.62,<2",
"pytest>=8.0.0",
"pytest-cov>=4.1.0",
"pytest-xdist>=3.5.0",
"packaging>=24.0",
"ruff>=0.4.0",
"pyright>=1.1.0",
"invisible-watermark>=0.2.0",
]
# ``qwen-zimage`` already pulls ``diffusion``; naming both would suggest diffusion is
# independently sufficient for a removal, which it is not.
all = ["remove-ai-watermarks[video,heif,detect,trustmark,text-restoration,migan,classify,source-classify]"]
[project.scripts]
remove-ai-watermarks = "remove_ai_watermarks.cli:main"
[project.urls]
Repository = "https://github.com/wiltodelta/remove-ai-watermarks"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.uv]
# Lock-resolution only; not published metadata. PaddleX 3.7 (the `ocr` extra's
# PaddleOCR) requires NumPy <2.4, and without this cap `uv lock --upgrade`
# prefers the newest NumPy and silently downgrades PaddleOCR to 3.4.
constraint-dependencies = ["numpy<2.4"]
[tool.hatch.build.targets.wheel]
packages = ["src/remove_ai_watermarks"]
[tool.hatch.build.targets.sdist]
# Keep the source distribution small and public-safe: ship tracked source and
# metadata, not corpora or local research/session artifacts. The wheel ships
# only src/.
include = [
"/src",
"/LICENSE",
"/README.md",
"/pyproject.toml",
]
exclude = [
"/data",
"/tmp",
"/.sc",
]
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src", "scripts"]
addopts = "-v --tb=short"
[tool.ruff]
target-version = "py311"
line-length = 120
exclude = ["_refs", ".local-eval"]
extend-exclude = ["*.md"]
[tool.ruff.lint]
select = ["E", "F", "B", "I", "S", "UP", "SIM", "RET", "COM", "C4", "G", "PT", "PIE", "T20", "DTZ", "ICN", "TCH", "RUF", "ANN"]
ignore = [
"COM812", # missing trailing comma (conflicts with ruff formatter)
"ANN401", # typing.Any — sometimes unavoidable with third-party libs
]
[tool.ruff.lint.per-file-ignores]
"scripts/**/*.py" = ["G004", "S108", "S310", "T20"]
"tests/*.py" = ["ANN", "S101", "S105", "S106", "S108"] # fixture and mock signatures need not annotate every parameter
"src/remove_ai_watermarks/_internal/watermark_remover.py" = ["S603", "S606", "S607"] # nvidia-smi capability probe
"src/remove_ai_watermarks/_internal/c2pa.py" = ["S110"] # try-except-pass for corrupt file handling
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
[tool.pyright]
pythonVersion = "3.11"
typeCheckingMode = "strict"
exclude = ["_refs", ".local-eval"]
[[tool.pyright.executionEnvironments]]
root = "tests"
extraPaths = ["."]
reportAttributeAccessIssue = false
reportOptionalSubscript = false
reportOptionalMemberAccess = false
reportArgumentType = false
reportUnknownMemberType = false
reportUnknownArgumentType = false
reportUnknownVariableType = false
reportMissingTypeArgument = false