mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-08-09 23:50:40 +02:00
Delete every knob the fixed profiles cannot honor
The CLI still advertised --model, --steps, --guidance-scale, --device and a deprecated --auto. Each pinned a value the two surviving profiles fix -- the model stack, the per-stage distilled schedule, CFG 1.0, CUDA -- so the only outcome any of them had was an error raised several frames below the caller, under a message naming an internal profile. A flag whose sole result is a refusal is worse than no flag: it advertises a capability that does not exist, and it lets a wrapper thread a value that will silently do nothing. They are gone from the parser, from InvisibleEngine, and from WatermarkRemover, so the failure is now a TypeError or a Click "No such option" at the point the caller can act on. The install hint was wrong in the same way. is_available() checked torch and diffusers, then told the user to install [diffusion] -- which contains neither DiffSynth nor the Z-Image face stage both profiles run. Following the advice produced a second, different failure. The module list and the extra name now live once in watermark_profiles (REMOVAL_MODULES, INVISIBLE_EXTRA) and are read by both the CLI gate and the remover's precondition, which cannot drift apart because they are the same tuple. The adaptive-polish default moved out of the argument parser. It was resolved by reading Click's parameter source, which put per-profile data in the CLI layer, left the engine declaring the opposite default (False vs True) so a library caller and a CLI caller on one profile got different output, and lost the polish entirely for anything that supplies the flag non-interactively. The flag is now tri-state (default=None) and resolve_adaptive_polish owns the per-profile answer. The seed follows the same rule: the CLI stopped pre-resolving it. Dead code removed with it: six scan_*_video wrappers and the _scan_video helper none of them had a caller for, PNG_METADATA_KEYS, feather_region_composite and the remover region path that was only reachable from a no-caller convenience wrapper, remove_watermark_batch on both layers, try_empty_device_cache, the _generate/_run_qwen_zimage pass-through pair, self.model_id, and the _internal PEP 562 shim that no caller ever went through. get_device now answers cuda or cpu only: mps and xpu travelled one frame to the same CUDA-only refusal while costing a device probe each, and that refusal now names the resolved device, so device=None on a CUDA-less host says 'cpu' rather than 'None'. The XPU wheel index went with them. Docs: README, cli, installation, python-api, supported-signals, known-limitations and module-internals all still described the removed profiles, the CPU/MPS/XPU ladder, a `default`->`sdxl` alias, and the wrong extra. known-limitations still listed the retired SDXL strength ladder as current. scripts/smoke_matrix.py and real_examples_e2e.py drove --device mps. Next release is 0.25.0, not a patch: this removes public parameters and narrows a published extra on top of the released 0.24.0. pre-commit: 1) maintain.sh - exit 0 (1091 tests, Pyright 0 errors, no vulnerabilities); 2) /simplify - 4 agents, 11 findings applied, 2 skipped (dropping the `device` parameter entirely, which raiw-app pins; folding diffsynth into the `diffusion` extra, which video-only callers do not need); 3) docs sync - grepped every removed identifier across README, docs/, scripts/, .claude/; updated 9 docs; 4) CLAUDE.md - added the no-error-only-knobs rule to .claude/rules/development.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
bf4bfc1ab7
commit
52b2c115e8
+10
-7
@@ -20,8 +20,7 @@ defaults. This page focuses on choosing the right command.
|
||||
| `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]` |
|
||||
| `invisible` and `all` (needs CUDA) | `remove-ai-watermarks[qwen-zimage]` |
|
||||
| `video metadata` and `video identify --no-visible` | Default package |
|
||||
| `video identify`, `video visible`, and visible/all batch modes | `remove-ai-watermarks[video]` |
|
||||
| `video invisible` and `video all --invisible` | `remove-ai-watermarks[video,diffusion]` |
|
||||
@@ -340,10 +339,11 @@ failed encode does not overwrite an existing result.
|
||||
|
||||
## Remove invisible watermarks
|
||||
|
||||
Install the diffusion dependencies first:
|
||||
Install the removal dependencies first. Both profiles are CUDA-only and both
|
||||
run the DiffSynth Z-Image face stage, so this is the extra either one needs:
|
||||
|
||||
```bash
|
||||
uv tool install --force "remove-ai-watermarks[diffusion]"
|
||||
uv tool install --force "remove-ai-watermarks[qwen-zimage]"
|
||||
```
|
||||
|
||||
Then run:
|
||||
@@ -380,8 +380,11 @@ remove-ai-watermarks invisible image.png -o clean.png \
|
||||
--pipeline qwen-zimage --force
|
||||
```
|
||||
|
||||
The legacy `default` value is an alias for `sdxl`. The `--auto` option is
|
||||
deprecated, emits a warning, and changes nothing.
|
||||
There is no `--model`, `--steps`, `--guidance-scale` or `--device` option, and the
|
||||
deprecated `--auto` is gone. Each profile pins its model stack, its per-stage
|
||||
schedule, CFG 1.0 and CUDA, so every one of those flags existed only to be refused
|
||||
several layers down. They are not parsed at all now, which fails at the point the
|
||||
user can act on rather than after a model load.
|
||||
|
||||
### Work with limited memory
|
||||
|
||||
@@ -415,7 +418,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
|
||||
`remove-ai-watermarks[visible,diffusion]` can also run the command with fewer
|
||||
`remove-ai-watermarks[visible,qwen-zimage]` can also run the command with fewer
|
||||
optional backends.
|
||||
|
||||
```bash
|
||||
|
||||
+19
-15
@@ -65,22 +65,24 @@ uv tool install --force "remove-ai-watermarks[video,diffusion]"
|
||||
|
||||
## 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
|
||||
CPU inference is slow.
|
||||
|
||||
For the CUDA only Qwen Image plus Z-Image profile:
|
||||
Install the `qwen-zimage` extra:
|
||||
|
||||
```bash
|
||||
uv tool install --force "remove-ai-watermarks[qwen-zimage]"
|
||||
```
|
||||
|
||||
The `qwen-zimage` extra includes the normal `diffusion` dependencies.
|
||||
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
|
||||
|
||||
@@ -95,10 +97,10 @@ application actually uses:
|
||||
| `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` | Diffusion-based invisible watermark removal | `pixels`, Torch, Diffusers | 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` | CUDA-only Qwen Image plus Z-Image pipeline | `diffusion`, DiffSynth | Yes |
|
||||
| `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 | `visible`, `detect`, upstream invisible-watermark | Yes, for parity tests |
|
||||
|
||||
@@ -214,5 +216,7 @@ 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 diffusion dependencies are unavailable, install the
|
||||
`diffusion` extra. Video SynthID removal needs both `video` and `diffusion`.
|
||||
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`.
|
||||
|
||||
+26
-17
@@ -120,33 +120,42 @@ prefix so it can reuse identical latents across candidate strengths.
|
||||
|
||||
### Strength is content and seed dependent
|
||||
|
||||
For SDXL and ControlNet, the CLI resolves an unset strength from the detected
|
||||
vendor:
|
||||
The two profiles resolve an unset strength differently, because different things
|
||||
were measured for each.
|
||||
|
||||
- OpenAI: `0.10`;
|
||||
- Google: `0.15`;
|
||||
- unknown: `0.15`.
|
||||
`qwen-zimage` reads it from image area, through the resolution-adaptive denoise
|
||||
curve. The vendor is deliberately ignored: the curve, not the issuer, is what was
|
||||
calibrated.
|
||||
|
||||
An explicit `--strength` overrides these defaults. The defaults are operating
|
||||
points, not universal guarantees. Near a removal threshold, different content
|
||||
or a different random seed may change the verifier result.
|
||||
`sdxl-zimage` reads it from the C2PA issuer, on a flat ladder:
|
||||
|
||||
The base Qwen and `qwen-zimage` profiles have profile specific strength
|
||||
behavior. Consult `remove-ai-watermarks invisible --help` and the source of
|
||||
[`watermark_profiles.py`](../src/remove_ai_watermarks/_internal/watermark_profiles.py)
|
||||
for the current resolver.
|
||||
- OpenAI: `0.15`;
|
||||
- Google: `0.25`;
|
||||
- unknown: `0.25`, following the stricter of the two.
|
||||
|
||||
An SDXL global pass needs more denoise than Qwen at the same fidelity, and the
|
||||
values are flat rather than a curve because flat values are what was measured: each
|
||||
verdict came from a fixed strength at one size, and no size dependence has been
|
||||
established for that stage.
|
||||
|
||||
An explicit `--strength` overrides both. The defaults are operating points, not
|
||||
universal guarantees. Near a removal threshold, different content or a different
|
||||
random seed may change the verifier result, which is why both profiles are
|
||||
certified at a fixed seed. The live resolver is
|
||||
[`watermark_profiles.py`](../src/remove_ai_watermarks/_internal/watermark_profiles.py).
|
||||
|
||||
### Pipelines have different quality tradeoffs
|
||||
|
||||
| Pipeline | Main limit |
|
||||
| --- | --- |
|
||||
| `controlnet` | Edge conditioning can preserve a watermark carrying region too closely, and faces may drift. |
|
||||
| `sdxl` | Flat graphics and precise structure may receive too little or unhelpful change. |
|
||||
| `qwen` | Large CUDA oriented model; face smoothing can still be significant. |
|
||||
| `qwen-zimage` | CUDA only, large model stack, and limited broad certification across seeds and content. |
|
||||
| `sdxl-zimage` | CUDA only. Its strength ladder is flat per vendor, not a resolution curve, because flat values are what was measured. |
|
||||
|
||||
The legacy `default` profile name maps to `sdxl`. The `--auto` flag is
|
||||
deprecated, emits a warning, and changes nothing.
|
||||
The `controlnet`, `sdxl`, `qwen` and `default` profiles were removed, not aliased
|
||||
onward: a retired name is rejected at parse time rather than routed into a profile
|
||||
the caller never chose. There is no `--model`, `--steps`, `--guidance-scale`,
|
||||
`--device` or `--auto` option either; each profile pins its model stack, its
|
||||
per-stage schedule, CFG 1.0 and CUDA.
|
||||
|
||||
## Resolution and memory
|
||||
|
||||
|
||||
+45
-17
@@ -53,9 +53,19 @@ The decorators for diffusion options are shared by `invisible`, `all`, and
|
||||
`batch`. The runtime help generated by Click is the source of truth for option
|
||||
names and defaults.
|
||||
|
||||
The deprecated `--auto` option does not select a pipeline or change adaptive
|
||||
polishing. [`_resolve_auto_polish`](../src/remove_ai_watermarks/cli.py) emits a
|
||||
warning and returns the explicit polish value unchanged.
|
||||
`--adaptive-polish` is tri-state: it declares `default=None`, so "the user did not
|
||||
choose" is a value the CLI passes through rather than a default it has to invent.
|
||||
`resolve_adaptive_polish` in `watermark_profiles.py` turns that `None` into the
|
||||
profile's answer (off for `qwen-zimage`, whose output already matches the input's
|
||||
detail level; on for `sdxl-zimage`). The same call runs inside
|
||||
`InvisibleEngine.remove_watermark`, so a library caller and a CLI caller on one
|
||||
profile get the same output.
|
||||
|
||||
It used to read Click's parameter source in the CLI instead. That put per-profile
|
||||
data in the argument-parsing layer, left the engine declaring the opposite default,
|
||||
and silently lost the polish for anything supplying the flag non-interactively (an
|
||||
envvar default or a wrapper calling `main()` with a defaulted list is classified
|
||||
`DEFAULT`). The seed follows the same rule: the CLI does not pre-resolve it either.
|
||||
|
||||
Regression coverage:
|
||||
|
||||
@@ -477,25 +487,42 @@ Regression coverage:
|
||||
[`_internal/watermark_profiles.py`](../src/remove_ai_watermarks/_internal/watermark_profiles.py)
|
||||
is the source of truth for:
|
||||
|
||||
- profile aliases;
|
||||
- default model identifiers;
|
||||
- default steps and seeds;
|
||||
- vendor-adaptive strength resolution;
|
||||
- the minimum viable step calculation.
|
||||
- profile names and their underscore spellings;
|
||||
- the fixed seed;
|
||||
- the SDXL global-stage checkpoint id (`SDXL_MODEL_ID`) and the Canny ControlNet id;
|
||||
- strength resolution for both profiles.
|
||||
|
||||
The current profiles are `qwen-zimage` (the default) and `sdxl-zimage`, and both
|
||||
are CUDA-only. `controlnet`, `sdxl`, `qwen` and `default` were removed rather than
|
||||
kept as a CPU path, and are rejected rather than aliased onward. There is no
|
||||
content-dependent automatic router.
|
||||
|
||||
The current profiles are `controlnet`, `sdxl`, `qwen`, and `qwen-zimage`.
|
||||
For serverless cold starts, `InvisibleEngine.preload(global_only=True)` loads the
|
||||
mandatory Qwen stage and YuNet while leaving the optional Z-Image and SAM face
|
||||
mandatory global stage and YuNet while leaving the optional Z-Image and SAM face
|
||||
stack lazy until a face is detected. The default `preload()` still loads every
|
||||
stage.
|
||||
`default` is a legacy alias for `sdxl`. There is no content-dependent automatic
|
||||
router.
|
||||
|
||||
**What is deliberately not a parameter.** Model id, step count, CFG and any
|
||||
non-CUDA device are fixed by the profile, so none of them appears in
|
||||
`WatermarkRemover.__init__`, `remove_watermark`, `InvisibleEngine`, or the CLI.
|
||||
They used to be accepted and then rejected several frames down; a signature that
|
||||
refuses the argument outright fails where the caller can act on it, and stops a
|
||||
wrapper from threading a value that would silently do nothing. The step count and
|
||||
CFG live with the stage that runs them (`GLOBAL_STEPS`, `FACE_STEPS`, `GLOBAL_CFG`,
|
||||
`FACE_CFG` in `qwen_zimage_pipeline.py`). The dtype is likewise profile-owned: see
|
||||
"Face-stage dtype" for what an override cost the last time one existed.
|
||||
|
||||
[`invisible_engine.py`](../src/remove_ai_watermarks/invisible_engine.py) handles
|
||||
image sizing, postprocessing, and the public engine
|
||||
interface. It delegates model execution to
|
||||
[`_internal/watermark_remover.py`](../src/remove_ai_watermarks/_internal/watermark_remover.py).
|
||||
|
||||
`get_device` in that module answers only `cuda` or `cpu`. An `mps` or `xpu` answer
|
||||
would travel one frame to the same CUDA-only refusal while costing a device probe,
|
||||
and reporting it implied an Apple-silicon or Intel-GPU path that does not exist.
|
||||
The refusal names the *resolved* device, so `device=None` on a CUDA-less host says
|
||||
`'cpu'` rather than `'None'`.
|
||||
|
||||
The Python engine and CLI do not have identical defaults for every optional
|
||||
postprocessing argument. Integrations that require reproducibility should pass
|
||||
the relevant values explicitly.
|
||||
@@ -509,9 +536,8 @@ unit-test pass. Exact prompt and edge-map regression guards live in
|
||||
|
||||
Regression coverage:
|
||||
|
||||
- [`test_watermark_profiles.py`](../tests/test_watermark_profiles.py)
|
||||
- [`test_invisible_engine.py`](../tests/test_invisible_engine.py)
|
||||
- [`test_img2img_runner.py`](../tests/test_img2img_runner.py)
|
||||
- [`test_qwen_zimage_pipeline.py`](../tests/test_qwen_zimage_pipeline.py)
|
||||
- [`test_platform.py`](../tests/test_platform.py)
|
||||
|
||||
### CPU offload
|
||||
@@ -712,14 +738,16 @@ Regression coverage:
|
||||
### Tiling
|
||||
|
||||
[`_internal/tiling.py`](../src/remove_ai_watermarks/_internal/tiling.py) contains pure
|
||||
tile planning, feather weights, tile orchestration, and region compositing.
|
||||
tile planning, feather weights, and tile orchestration.
|
||||
|
||||
Tiling engages only when requested and the long side exceeds the tile size.
|
||||
It avoids an explicit full-image downscale but does not make diffusion
|
||||
pixel-preserving. Each tile is still regenerated.
|
||||
|
||||
`feather_region_composite` changes only the requested box and leaves pixels
|
||||
outside it unchanged.
|
||||
It also held a `feather_region_composite` for AI-*enhanced* composites, where only
|
||||
the edited region should change. Nothing ever reached it: the `erase` command
|
||||
inpaints through `region_eraser`, and the remover's `region` argument was only
|
||||
reachable from a module-level convenience wrapper with no callers. Both went.
|
||||
|
||||
Regression coverage:
|
||||
|
||||
|
||||
+18
-12
@@ -6,8 +6,9 @@ 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`; diffusion removal uses `diffusion`; and visible video processing
|
||||
uses `video`. Video SynthID removal combines `video` and `diffusion`. Add `heif`
|
||||
uses `detect`; invisible image removal uses `qwen-zimage` and an NVIDIA GPU; and
|
||||
visible video processing uses `video`. Video SynthID removal is a separate VAE
|
||||
path that still runs on CPU and combines `video` and `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).
|
||||
|
||||
@@ -380,8 +381,8 @@ to 8-bit SDR.
|
||||
|
||||
## 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.
|
||||
Install `remove-ai-watermarks[qwen-zimage]`. Both profiles need it, and both
|
||||
need an NVIDIA GPU.
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
@@ -400,8 +401,9 @@ engine.remove_watermark(
|
||||
)
|
||||
```
|
||||
|
||||
`device=None` selects the device automatically. Supported explicit values are
|
||||
defined by the CLI and runtime device resolver.
|
||||
`device=None` detects CUDA. The only other accepted value is `"cuda"`; anything
|
||||
else raises at construction rather than deferring a guaranteed failure to model
|
||||
load time.
|
||||
|
||||
For limited CUDA memory:
|
||||
|
||||
@@ -412,18 +414,22 @@ engine = InvisibleEngine(
|
||||
)
|
||||
```
|
||||
|
||||
Both profiles are CUDA-only, so `device=None` resolving to CPU or MPS cannot run
|
||||
invisible-watermark removal at all. For the SDXL global stage instead of Qwen:
|
||||
Both profiles are CUDA-only, so on a machine without an NVIDIA GPU `device=None`
|
||||
resolves to `cpu` and construction raises. For the SDXL global stage instead of
|
||||
Qwen:
|
||||
|
||||
```python
|
||||
engine = InvisibleEngine(pipeline="sdxl-zimage")
|
||||
```
|
||||
|
||||
The `qwen-zimage` extra must be installed for that profile.
|
||||
The `qwen-zimage` extra is required for both profiles: each runs the same
|
||||
DiffSynth Z-Image face stage.
|
||||
|
||||
The full `remove_watermark` signature includes strength, steps, guidance,
|
||||
seeding, tiling, resolution, and postprocessing controls. Read the
|
||||
method signature in
|
||||
`remove_watermark` takes strength, seed, tiling, resolution, and postprocessing
|
||||
controls. It takes no model id, step count or guidance scale, and neither does the
|
||||
constructor: each profile pins its model stack, its per-stage schedule and CFG
|
||||
1.0, so passing one raises `TypeError` at the call rather than being accepted and
|
||||
refused several layers down. Read the method signature in
|
||||
[`invisible_engine.py`](../src/remove_ai_watermarks/invisible_engine.py) or use
|
||||
the CLI guide for the concepts.
|
||||
Defaults can differ between the Python method and CLI profile resolution, so
|
||||
|
||||
@@ -114,13 +114,13 @@ The `invisible` command uses diffusion regeneration. It targets watermark
|
||||
patterns by changing the image rather than decoding and deleting a known
|
||||
payload.
|
||||
|
||||
Current pipeline values:
|
||||
Current pipeline values, both CUDA-only:
|
||||
|
||||
- `controlnet`;
|
||||
- `sdxl`;
|
||||
- `qwen`;
|
||||
- `qwen-zimage`;
|
||||
- legacy alias `default`, which resolves to `sdxl`.
|
||||
- `qwen-zimage`, the default;
|
||||
- `sdxl-zimage`, the same recipe and the same face stage on an SDXL global pass.
|
||||
|
||||
The `controlnet`, `sdxl`, `qwen` and `default` values were removed. A retired name
|
||||
is rejected at parse time rather than remapped onto a surviving profile.
|
||||
|
||||
SynthID does not have a public local pixel decoder in this project. The tool can
|
||||
infer likely presence from supported provenance metadata, but after that
|
||||
|
||||
+5
-4
@@ -714,10 +714,11 @@ end has simply never been through the Gemini oracle on any pipeline. Do not reas
|
||||
a resolution trend here; measure it.
|
||||
|
||||
**Current implication:** the old floor table remains evidence about the dated
|
||||
test set, not the current resolver. The shipped SDXL and ControlNet defaults are
|
||||
defined in `watermark_profiles.py`, and face restoration is available only
|
||||
through the separate `qwen-zimage` profile. Removal near a threshold remains
|
||||
seed dependent, so reproducible verification requires a fixed seed.
|
||||
test set, not the current resolver. The SDXL and ControlNet profiles it measured
|
||||
no longer exist; the shipped defaults are defined in `watermark_profiles.py`, and
|
||||
both surviving profiles run face repair as a built-in second stage rather than as
|
||||
an optional restore. Removal near a threshold remains seed dependent, so
|
||||
reproducible verification requires a fixed seed.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -91,20 +91,26 @@ and the ffmpeg audio/video strip. The gap to find is not only
|
||||
"logic untested" but
|
||||
"never executed on real data", which is precisely what this campaign is for.
|
||||
|
||||
#### Bug found by the extension: `--steps` below ~7 crashes inside torch
|
||||
#### Bug found by the extension: `--steps` below ~7 crashed inside torch
|
||||
|
||||
Effective timesteps are `int(steps * strength)`. At the vendor-adaptive default strength
|
||||
(0.15, or 0.10 for OpenAI) any `--steps` under 7 rounds to **zero**, and the pipeline dies
|
||||
with a raw traceback:
|
||||
**Fixed by deletion.** `--steps` no longer exists, on the CLI or in the Python API,
|
||||
so this class of failure is unreachable. Kept as a record of why.
|
||||
|
||||
Effective timesteps were `int(steps * strength)`. At the vendor-adaptive default
|
||||
strength (0.15, or 0.10 for OpenAI) any `--steps` under 7 rounded to **zero**, and the
|
||||
pipeline died with a raw traceback:
|
||||
|
||||
```
|
||||
$ remove-ai-watermarks invisible img.png --steps 5
|
||||
RuntimeError: cannot reshape tensor of 0 elements into shape [0, -1, 1, 512]
|
||||
```
|
||||
|
||||
Fully valid CLI arguments, no special flags, no `--force`. The value is accepted, the
|
||||
crash is a torch internal, and nothing tells the user that steps and strength interact.
|
||||
Fix is either a clamp to >=1 effective step or an up-front validation naming both values.
|
||||
Fully valid CLI arguments, no special flags, no `--force`. The value was accepted, the
|
||||
crash was a torch internal, and nothing told the user that steps and strength interact.
|
||||
The considered fixes were a clamp to >=1 effective step or an up-front validation
|
||||
naming both values; what shipped instead is that each stage owns its own distilled
|
||||
schedule and no caller can set it. The general lesson stands: a knob whose valid range
|
||||
depends on another knob's value needs the interaction validated where both are known,
|
||||
or it needs to not be a knob.
|
||||
|
||||
Method note: the first run of the knob rows failed 12 times with this identical error,
|
||||
which read like twelve broken features. It was one bad harness parameter (`--steps 4`)
|
||||
|
||||
Reference in New Issue
Block a user