From 29de21256bba708568e6630649f9cb317eb89fa9 Mon Sep 17 00:00:00 2001 From: Victor Kuznetsov Date: Tue, 1 Sep 2026 15:33:01 -0700 Subject: [PATCH] Point the classify weights at wiltodelta/raiw-photo-classify The Hub model and the private training repository were both called raiw-models, so the training repo's README had to spend a paragraph telling the two apart. Renamed on both sides: the Hub model to raiw-photo-classify and the GitHub repository to raiw-classifier-training. Done now because it is free now. classify.py landed after v0.36.0 and is in no release, and the Hub model had zero all-time downloads, so no installed package and no user carries the old id. Hugging Face redirects the old URL and preserves counts, and the pinned freeze revision c0ac82b6 resolves on the new name -- verified by downloading through the library's own constants. Co-Authored-By: Claude Opus 5 --- .../workflows/publish-photo-classify-hf.yml | 2 +- docs/cli.md | 42 ++++++++++++++++++- docs/module-internals.md | 5 ++- docs/photo-classify.md | 2 +- docs/release-and-distribution.md | 13 ++++-- scripts/README.md | 2 +- scripts/publish_photo_classify_hf.py | 4 +- src/remove_ai_watermarks/classify.py | 2 +- tests/test_classify.py | 2 +- tests/test_publish_photo_classify_hf.py | 4 +- 10 files changed, 61 insertions(+), 17 deletions(-) diff --git a/.github/workflows/publish-photo-classify-hf.yml b/.github/workflows/publish-photo-classify-hf.yml index 89ec6f4..b1440a3 100644 --- a/.github/workflows/publish-photo-classify-hf.yml +++ b/.github/workflows/publish-photo-classify-hf.yml @@ -3,7 +3,7 @@ name: Publish photo classifier to Hugging Face # Manual only. Not part of a library release. The Space demo # (wiltodelta/remove-ai-watermarks) stays on distribute.yml / raiw-hf-space. # -# Hub model: wiltodelta/raiw-models +# Hub model: wiltodelta/raiw-photo-classify # Secret: HF_TOKEN, write-role Hugging Face token (same secret Space sync uses). # # Weights are not in git. Full mode downloads them from a GitHub Release diff --git a/docs/cli.md b/docs/cli.md index 460beda..3a33b8c 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -129,7 +129,7 @@ The contract is AI versus camera. Receipts, UI, and digital art are out of scope. Microsoft is not a pixel class: a DALL-E Bing image scores as `openai`, an Imagen Designer image as `google`. `tc260` is the China AIGC label standard, not one producer. Weights download on first use from -[`wiltodelta/raiw-models`](https://huggingface.co/wiltodelta/raiw-models), +[`wiltodelta/raiw-photo-classify`](https://huggingface.co/wiltodelta/raiw-photo-classify), or from `RAIW_CLASSIFY_WEIGHTS` if that directory already holds the freeze files. Guide: [photo pixel classification](photo-classify.md). Hub card: @@ -175,6 +175,18 @@ Use the strict visual gate without metadata or sibling corroboration: remove-ai-watermarks visible image.png --sensitivity strict -o clean.png ``` +Act on a mark you can see but the detector will not confirm, by naming it and +skipping the gate: + +```bash +remove-ai-watermarks visible image.png --mark gemini --no-detect -o clean.png +``` + +`--detect` is the default. `--no-detect` removes the named mark's footprint +unconditionally, so it only makes sense together with an explicit `--mark`; on +an image that never carried that mark it inpaints a region for nothing. Prefer +`erase --region` when you know the geometry. + When no known mark is detected, the command does not write a new output. Use `erase` if you can identify the affected region yourself. @@ -575,7 +587,33 @@ remove-ai-watermarks invisible image.png -o clean.png \ ``` Tiling avoids the explicit downscale but each tile is regenerated separately. -It is a memory strategy, not a guarantee of better quality. +It is a memory strategy, not a guarantee of better quality. `--tile-size` and +`--tile-overlap` set the tile geometry (defaults 1024 and 128 px); a larger +overlap costs time and hides seams. + +### Every tuning option + +These apply to `invisible`, `all`, and `batch`. Each has a measured default, so +reach for one only when you know why the default is wrong for your file. + +| Option | Effect | +| --- | --- | +| `--strength` | Denoising strength, 0.0-1.0. Default is profile- and vendor-adaptive; see [choose a strength cohort](#choose-a-strength-cohort). Raising it removes more and drifts further from the original. | +| `--vendor` | Strength cohort, and an assertion that the watermark is present. | +| `--pipeline` | Profile: `qwen-zimage` (default), `sdxl-zimage`, `chroma-zimage`, `auto`. | +| `--seed` | Fixed at 0 by default: every profile is certified at that seed, and removal near the floor is seed-dependent. | +| `--controlnet-scale` | Canny ControlNet conditioning on the global stage. Higher keeps the original structure and text closer. | +| `--humanize` | Analog film grain, 0 = off, typical 2.0-6.0. | +| `--unsharp` | Unsharp-mask sharpening, 0 = off. | +| `--adaptive-polish` / `--no-adaptive-polish` | Overrides the profile's own choice. Unset lets the profile decide: off for `qwen-zimage`, whose output already matches the input's detail level, on for `sdxl-zimage`. | +| `--max-resolution` | Cap the long side before diffusion; 0 = native. | +| `--tile`, `--tile-size`, `--tile-overlap` | Tiled regeneration for large inputs, as above. | +| `--cpu-offload` | Stream model components between CPU and GPU to lower CUDA memory pressure, at the cost of speed. | +| `--hf-token` | Hugging Face token for the model download; also read from the environment and a local `.env`. | + +There is no `--model`, `--steps`, `--guidance-scale` or `--device` option; the +profile pins all four. See [exit behavior](#exit-behavior) for what each command +returns. ## Run the full pipeline diff --git a/docs/module-internals.md b/docs/module-internals.md index 53a9f72..df1da98 100644 --- a/docs/module-internals.md +++ b/docs/module-internals.md @@ -968,7 +968,7 @@ file keys the Muse Image head `meta_muse_image`. Tests in `tests/test_classify.py` pin the gate without downloads and pin that `identify` does not import this module. -Weights stay out of git. The Hub snapshot is `wiltodelta/raiw-models`. +Weights stay out of git. The Hub snapshot is `wiltodelta/raiw-photo-classify`. `RAIW_CLASSIFY_WEIGHTS` overrides it. The extra is `classify`. User guide: [photo-classify.md](photo-classify.md). Hub card: [photo-classify-hf/README.md](photo-classify-hf/README.md). @@ -1835,7 +1835,8 @@ grain, unsharp masking, and adaptive polish helpers. `upscaler.py` held an optional Real-ESRGAN path, reachable only when enlarging a small image to the minimum-resolution floor. That floor existed to lift small -inputs toward SDXL's ~1024 training size; when the SDXL profiles were removed it +inputs toward SDXL's ~1024 training size; when the standalone SDXL and ControlNet +profiles were removed (`sdxl-zimage` is a later, surviving profile) it was forced to 0 on every path, so the module, the `--min-resolution` and `--upscaler` options and the `esrgan` extra were all unreachable and went with it. Only the `max_resolution` cap can move geometry now, and it only scales down. diff --git a/docs/photo-classify.md b/docs/photo-classify.md index 6a847b1..a3fa1e0 100644 --- a/docs/photo-classify.md +++ b/docs/photo-classify.md @@ -38,7 +38,7 @@ Missing extra raises `RuntimeError` with parameter (`None` / `"auto"` / `"cpu"` / `"cuda"`), not a CLI option. Weights are not in git. First call downloads -[`wiltodelta/raiw-models`](https://huggingface.co/wiltodelta/raiw-models) +[`wiltodelta/raiw-photo-classify`](https://huggingface.co/wiltodelta/raiw-photo-classify) at the freeze revision pinned in `classify.py`, or reads `RAIW_CLASSIFY_WEIGHTS` if that directory already has `clip-l-ft.pt`, `probe-weights-clip-l-ft.npz`, `detector.pt`, and `provider.pt`. The training diff --git a/docs/release-and-distribution.md b/docs/release-and-distribution.md index e69eeb0..d78c1e3 100644 --- a/docs/release-and-distribution.md +++ b/docs/release-and-distribution.md @@ -73,7 +73,7 @@ Upgrading the Hugging Face Space is a separate, manual step: bump pin. Its callback smoke tests run in that repository's CI on every push; they are the scenario check for the demo surface. -The Hugging Face **model** `wiltodelta/raiw-models` is not part of that +The Hugging Face **model** `wiltodelta/raiw-photo-classify` is not part of that release fan-out. It holds the photo-classify freeze weights. The library extra `classify` downloads that snapshot on first use, or reads `RAIW_CLASSIFY_WEIGHTS`. Update the Hub repo only with @@ -135,9 +135,14 @@ requests an older supported one. After publication, the `verify-release.yml` workflow runs automatically once `distribute.yml` completes and checks every surface below. It can also be -dispatched manually with a specific version. The ComfyUI check verifies the -sync workflow's latest run succeeded rather than querying the registry directly -(the registry has no public JSON API). The full checklist for completeness: +dispatched manually with a specific version. Every check fails the job; none of +them warns and continues, because a surface nobody can confirm is not a verified +surface. The ComfyUI check reads the published node from the registry's JSON API +(`https://api.comfy.org/nodes/remove-ai-watermarks`) and asserts the active +version declares this library release. It deliberately does NOT proxy through +the sync workflow's run status: the node repository also runs that workflow on a +daily schedule, so a green nightly run says nothing about whether this release +was synced. The full checklist for completeness: - both wheel and source distribution exist on PyPI; - the package version matches the tag; diff --git a/scripts/README.md b/scripts/README.md index 447e298..1342e8e 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -44,7 +44,7 @@ explicitly names a tracked canonical result. | `visible_recall_sample.py` | Build an unbiased blinded sample for recall measurement. | | `visible_sheets.py` | Build blinded contact sheets for relaxation candidates. | | `retrain_photo_classify.py` | CPU-retrain the 2026-08-31 photo heads from a sha256-keyed cache pack, no images. | -| `publish_photo_classify_hf.py` | Upload the photo-classify card and freeze weights to Hugging Face `wiltodelta/raiw-models`. Manual; the Action `publish-photo-classify-hf.yml` is the write-token path. | +| `publish_photo_classify_hf.py` | Upload the photo-classify card and freeze weights to Hugging Face `wiltodelta/raiw-photo-classify`. Manual; the Action `publish-photo-classify-hf.yml` is the write-token path. | | `verify_engine_selection_fixtures.py` | Verify hashes, dimensions, and prompt pairing in the tracked auto-engine content matrix. | ## Research and diagnostic prototypes diff --git a/scripts/publish_photo_classify_hf.py b/scripts/publish_photo_classify_hf.py index ad9db51..e1e8ae7 100644 --- a/scripts/publish_photo_classify_hf.py +++ b/scripts/publish_photo_classify_hf.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Publish the photo-classify freeze to Hugging Face wiltodelta/raiw-models. +"""Publish the photo-classify freeze to Hugging Face wiltodelta/raiw-photo-classify. Manual. Not a library release step. Weights are not in git: pass --src, a directory that holds clip-l-ft.pt and the probe, with detector.pt and @@ -23,7 +23,7 @@ from pathlib import Path log = logging.getLogger(__name__) -HUB_REPO = "wiltodelta/raiw-models" +HUB_REPO = "wiltodelta/raiw-photo-classify" CLIP_FILE = "clip-l-ft.pt" PROBE_FILE = "probe-weights-clip-l-ft.npz" DETECTOR_FILE = "detector.pt" diff --git a/src/remove_ai_watermarks/classify.py b/src/remove_ai_watermarks/classify.py index 4ad9295..469b93e 100644 --- a/src/remove_ai_watermarks/classify.py +++ b/src/remove_ai_watermarks/classify.py @@ -20,7 +20,7 @@ log = logging.getLogger(__name__) CLASSIFY_EXTRA = "'remove-ai-watermarks[classify]'" WEIGHTS_ENV = "RAIW_CLASSIFY_WEIGHTS" -WEIGHTS_REPO = "wiltodelta/raiw-models" +WEIGHTS_REPO = "wiltodelta/raiw-photo-classify" WEIGHTS_REVISION = "c0ac82b6f1ae9fc0b92c467562282e9422da6da6" CLIP_FILE = "clip-l-ft.pt" PROBE_FILE = "probe-weights-clip-l-ft.npz" diff --git a/tests/test_classify.py b/tests/test_classify.py index 9c642da..de3390e 100644 --- a/tests/test_classify.py +++ b/tests/test_classify.py @@ -45,7 +45,7 @@ def _scores( def test_hub_snapshot_is_the_freeze_revision() -> None: - assert WEIGHTS_REPO == "wiltodelta/raiw-models" + assert WEIGHTS_REPO == "wiltodelta/raiw-photo-classify" assert WEIGHTS_REVISION == "c0ac82b6f1ae9fc0b92c467562282e9422da6da6" diff --git a/tests/test_publish_photo_classify_hf.py b/tests/test_publish_photo_classify_hf.py index 6f51ee6..ad1eaad 100644 --- a/tests/test_publish_photo_classify_hf.py +++ b/tests/test_publish_photo_classify_hf.py @@ -19,8 +19,8 @@ def _publish() -> Any: return module -def test_hub_id_is_raiw_models() -> None: - assert _publish().HUB_REPO == "wiltodelta/raiw-models" +def test_hub_id_is_the_photo_classify_repo() -> None: + assert _publish().HUB_REPO == "wiltodelta/raiw-photo-classify" def test_weight_paths_accepts_run1_layout(tmp_path: Path) -> None: