diff --git a/.claude/rules/development.md b/.claude/rules/development.md index d75500f..e47532f 100644 --- a/.claude/rules/development.md +++ b/.claude/rules/development.md @@ -31,9 +31,14 @@ Every registered mark carries a committed example: `data/fixtures/visible// (image marks) and `example.mp4` (video marks), regenerated by `scripts/render_visible_examples.py`. `tests/test_visible_examples.py` is the canary: it fails when a mark is registered without an example and when an engine stops detecting its -own example. The examples are synthetic composites of the committed silhouettes -- user -uploads never enter the repository. When the generator fails after a geometry or gate -change, fix the generator (and the engine) together; do not hand-edit the binaries. +own example. The examples are deterministic synthetic composites of the committed +silhouettes. When the generator fails after a geometry or gate change, fix the generator +(and the engine) together; do not hand-edit the binaries. + +Visible-mark content must distinguish the registry key, the provider's canonical +product name, and the literal text rendered in the mark whenever those differ. Verify +external provider names against the provider's official source rather than inferring +them from an asset filename or OCR text. ## Model-adjacent tests diff --git a/README.md b/README.md index 447ad7f..49cdf1a 100644 --- a/README.md +++ b/README.md @@ -320,7 +320,8 @@ remove-ai-watermarks batch ./images --mode all Visible mark support includes: - Google Gemini and Nano Banana visible sparkle watermark; -- Doubao, Jimeng, Qwen, Kling AI, Yuanbao, Baidu, LiblibAI, and RunningHub labels; +- Doubao, the Jimeng wordmark and top-left `AI生成` pill, Qwen, Kling AI, + Yuanbao, Baidu, LiblibAI, and RunningHub labels; - one calibrated Microsoft top-right white AI-badge variant; - one calibrated Samsung Galaxy AI label variant. @@ -330,7 +331,9 @@ signals. Optional decoders add support for open DWT-DCT watermarks and Adobe TrustMark. The exact support matrix, including important locale and detector limits, lives -in [supported signals](docs/supported-signals.md). +in [supported signals](docs/supported-signals.md). The +[synthetic example gallery](data/fixtures/visible/README.md) carries one canonical +sample for every registered image and video mark. ## How it works diff --git a/data/fixtures/visible/README.md b/data/fixtures/visible/README.md index cb2978c..d0905e0 100644 --- a/data/fixtures/visible/README.md +++ b/data/fixtures/visible/README.md @@ -10,13 +10,13 @@ does an engine that stops detecting its own example. Every example is SYNTHETIC: `scripts/render_visible_examples.py` composites the mark's committed silhouette (the same font-rendered asset the detector matches) onto a deterministic generated base photo at the engine's measured geometry. -No user upload and no vendor asset enters the repository: corpus files under -`data/spaces/` are user content and stay out of git by policy, and the -silhouettes themselves are our own renders (`scripts/render_vendor_silhouettes.py`). +The silhouettes are repository-owned font renders generated by +`scripts/render_vendor_silhouettes.py`; the gallery does not copy vendor raster +assets. The examples demonstrate DETECTION geometry and house style, not vendor raster fidelity; real-world variants (fonts, opacities, sizes) are covered by the -engines' calibration cohorts, which are local-only. +engines' recorded calibration evidence. ## Regeneration diff --git a/docs/supported-signals.md b/docs/supported-signals.md index 557af50..6c9f5b5 100644 --- a/docs/supported-signals.md +++ b/docs/supported-signals.md @@ -20,7 +20,7 @@ The `visible` command registers these mark keys: | `runninghub` | `RunningHub AI生成` | Top left | Strict visual and position gates. | | `baidu` | `百度 AI生成` | Bottom right | Detector and extended removal footprint. | | `liblib` | `LiblibAI` | Bottom center | Includes a minimum image size gate. | -| `microsoft` | One Microsoft white AI-badge variant | Top right | Strict gate; other documented icon, text, and position variants are not covered. | +| `microsoft` | One Microsoft white AI-badge variant | Top right | Strict uses the visual gate; auto can use Microsoft provenance for the measured [relaxed gate](module-internals.md#visible-mark-removal). Other documented icon, text, and position variants are not covered. | | `jimeng_pill` | `AI生成` pill | Top left | Weak detector with additional product and background gates. | `--mark auto` evaluates all registered marks and removes every selected match. @@ -30,6 +30,10 @@ the masked area. Marks from other vendors are not detected automatically. Use `erase --region` when you can select the affected area yourself. +Synthetic examples for every registered image and video mark live in the +[visible-mark gallery](../data/fixtures/visible/README.md). They demonstrate the +detectors' canonical geometry and house style, not vendor raster fidelity. + ### Visible video marks | Key | Mark | Motion | Important limit | diff --git a/scripts/assets/visible-mark-candidates/README.md b/scripts/assets/visible-mark-candidates/README.md index 94a5fd7..b29318f 100644 --- a/scripts/assets/visible-mark-candidates/README.md +++ b/scripts/assets/visible-mark-candidates/README.md @@ -12,6 +12,11 @@ provenance cohorts, not automatically visible-mark positives; comparison control are independently selected no-signal images and are not adjudicated negatives. Each needs capture-solved alphas or vendor-accurate font work before it can ship; `scripts/vendor_mark_calibrate.py` is the candidate-detector harness. +Rows through `mindvideo_alpha.png` and the parked candidates were recorded on +2026-08-27; rows from `higgsfield_alpha.png` through `gendo_alpha.png` were +recorded on 2026-08-28. These are repository record dates, not inferred capture +dates. The Mark column quotes the candidate raster text; where that text differs +from the provider's canonical name, the provider is named separately. | Asset | Mark | Evidence | Result | | --- | --- | --- | --- | @@ -20,13 +25,13 @@ ship; `scripts/vendor_mark_calibrate.py` is the candidate-detector harness. | `notebooklm_alpha.png` | NotebookLM wordmark (bottom-right) | 12 corpus files | locate geometry not yet fitted; POS max 0.12. | | `dola_alpha.png` | DolaAI on images (the video mark is registered) | 12 corpus files | POS 0.11-0.21 vs NEG max 0.30. | | `mindvideo_alpha.png` | MindVideo.AI (top-right) | 11 corpus files | POS 0.29-0.32 vs NEG max 0.30 -- borderline overlap, not shippable. | -| `higgsfield_alpha.png` | HIGGSFIELD AI wordmark (bottom-right; the boxed `AI` variant shares the cohort) | 5 wordmark files (16 in the boxed-AI OCR cluster) | POS max 0.26 vs NEG max 0.22 -- no separation; the mark may be two-part (wordmark + boxed AI) and needs a composed template. | -| `jianying_alpha.png` | 剪映AI (CapCut's CN sibling, bottom-right) | 2 corpus files | POS 0.29 vs NEG max 0.35. | +| `higgsfield_alpha.png` | `HIGGSFIELD AI` wordmark (Higgsfield AI, bottom-right; the boxed `AI` variant shares the cohort) | 5 wordmark files (16 in the boxed-AI OCR cluster) | POS max 0.26 vs NEG max 0.22 -- no separation; the mark may be two-part (wordmark + boxed AI) and needs a composed template. | +| `jianying_alpha.png` | `剪映AI` wordmark (Jianying / 剪映, the China product in the CapCut family; bottom-right) | 2 corpus files | POS 0.29 vs NEG max 0.35. | | `capcut_alpha.png` | CapCut AI pill (top-left; likely pill class, not plain text) | 3 corpus files | POS max 0.15 vs NEG max 0.33 -- locate geometry not yet fitted for the pill form. | -| `zsky_alpha.png` | MADE WITH zsky.ai (bottom-right) | 2 corpus files | POS 0.10 vs NEG max 0.27. | -| `chromastudio_alpha.png` | ChromaStudio.ai (bottom-right) | 2 corpus files | POS 0.11 vs NEG max 0.29. | -| `digenai_alpha.png` | DIGENAI (bottom-right) | 3 corpus files (one 2026-07-24 batch) | POS 0.16 vs NEG max 0.31. | -| `gendo_alpha.png` | GendoAI (bottom-left) | 3 corpus files | POS 0.08 vs NEG max 0.32. | +| `zsky_alpha.png` | `MADE WITH zsky.ai` wordmark (ZSky AI, bottom-right) | 2 corpus files | POS 0.10 vs NEG max 0.27. | +| `chromastudio_alpha.png` | `ChromaStudio.ai` wordmark (ChromaStudio, bottom-right) | 2 corpus files | POS 0.11 vs NEG max 0.29. | +| `digenai_alpha.png` | `DIGENAI` wordmark (Digen AI, bottom-right) | 3 corpus files (one 2026-07-24 batch) | POS 0.16 vs NEG max 0.31. | +| `gendo_alpha.png` | `GendoAI` wordmark (Gendo, bottom-left) | 3 corpus files | POS 0.08 vs NEG max 0.32. | | `xinghui_alpha.png` | 星绘AI生成 (parked before this set) | -- | prior parking, unchanged. | | `qingyan_alpha.png` | 清言·AI生成 (parked before this set) | -- | prior parking, unchanged. | | `hailuo_alpha.png` | Hailuo AI image wordmark (parked before this set; the VIDEO label is registered) | -- | prior parking, unchanged. | diff --git a/scripts/record_parity_audit.py b/scripts/record_parity_audit.py index acb0226..c9c2a02 100644 --- a/scripts/record_parity_audit.py +++ b/scripts/record_parity_audit.py @@ -34,7 +34,7 @@ DATA SAFETY Read-only over a local dataset. Writes only the given output path, which belongs outside the repository. Resumable: rerunning skips files already recorded. - uv run python scripts/record_parity_audit.py data/spaces/originals .local-eval/parity.jsonl + uv run python scripts/record_parity_audit.py /path/to/dataset .local-eval/parity.jsonl uv run python scripts/record_parity_audit.py --baseline .local-eval/previous.jsonl """ diff --git a/scripts/render_pill_silhouette.py b/scripts/render_pill_silhouette.py index 0258967..cf7c8ec 100644 --- a/scripts/render_pill_silhouette.py +++ b/scripts/render_pill_silhouette.py @@ -6,7 +6,7 @@ the committed asset is a font-rendered binary SILHOUETTE (mark shape only, zero content), used ONLY to (a) detect the pill by edge-NCC in the top-left corner and (b) build the inpaint mask. It is NOT an alpha map -- removal quality comes from the inpaint backend (MI-GAN/cv2), so the silhouette need not be pixel-accurate, and the -synthetic render keeps corpus/user content out of the tracked repo (data-safety). +synthetic render keeps the asset deterministic and reproducible. The calibrated edge-NCC threshold is 0.22 in the top-left ROI. Re-run to regenerate the asset: uv run python scripts/render_pill_silhouette.py diff --git a/scripts/render_vendor_silhouettes.py b/scripts/render_vendor_silhouettes.py index c6bf68a..552430f 100644 --- a/scripts/render_vendor_silhouettes.py +++ b/scripts/render_vendor_silhouettes.py @@ -75,8 +75,8 @@ MARKS = { "chromastudio_alpha.png": "ChromaStudio.ai", "digenai_alpha.png": "DIGENAI", "gendo_alpha.png": "GendoAI", - # CapCut's Chinese sibling, JianYing, stamps 剪映AI bottom-right (the - # international CapCut pill sits top-left). + # Jianying / 剪映, the China product in the CapCut family, stamps 剪映AI + # bottom-right (the international CapCut pill sits top-left). "jianying_alpha.png": "剪映AI", } _REGISTERED = {f"{key}_alpha.png" for key in mark_keys()} & MARKS.keys() diff --git a/scripts/render_visible_examples.py b/scripts/render_visible_examples.py index 85fe547..55918c7 100644 --- a/scripts/render_visible_examples.py +++ b/scripts/render_visible_examples.py @@ -5,10 +5,8 @@ sample of everything it supports and a canary test can hold both sides to it (mark registered without example; engine regressed on its canonical example). Every example is SYNTHETIC: a deterministic generated base photo with the mark's -own committed silhouette composited at the engine's measured geometry. User -uploads never enter the repository (data/spaces stays out of git), and no vendor -asset is copied -- the silhouettes are the same font-rendered templates the -detectors match against. +own committed silhouette composited at the engine's measured geometry. The +silhouettes are repository-owned font renders, not copied vendor raster assets. Regenerate with: uv run python scripts/render_visible_examples.py diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 54830bd..eb391e6 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -359,8 +359,7 @@ class TestC2paMarkerIn: class TestSamsungGenai: """Samsung Galaxy AI editing marker (genAIType in PhotoEditor_Re_Edit_Data). - Synthetic byte blobs -- real Galaxy files are user content and not shipped - (public repo), same discipline as the Grok/Doubao fixtures. + Synthetic byte blobs keep this fixture self-contained and deterministic. """ @staticmethod diff --git a/tests/test_visible_examples.py b/tests/test_visible_examples.py index 115ea09..4e1e27f 100644 --- a/tests/test_visible_examples.py +++ b/tests/test_visible_examples.py @@ -6,7 +6,7 @@ Two failures this suite exists to catch: generated from the engines' measured geometry, so this is a regression tripwire). The examples are SYNTHETIC (``scripts/render_visible_examples.py`` composites the -committed silhouettes onto a generated base). User uploads never enter the repo. +committed silhouettes onto a generated base). """ from __future__ import annotations