Add real watermark fixtures and expand provider coverage

This commit is contained in:
Victor Kuznetsov committed 2026-09-05 13:12:58 -07:00
1 parent 9f7b707016
commit efdbfa0598
51 files changed
+1223 -106

No files matched your search

+3 -3
View File
@@ -203,9 +203,9 @@ to one provider.
Sora covers the moving Sora 2 mascot and wordmark. Veo covers both the current
four-point diamond and the legacy `Veo` text. Seedance covers the fixed boxed
`AI` label, Dola covers the fixed `Dola AI` text, Hailuo AI covers the composite
`MINIMAX | hailuo AI` label, and Kling AI covers the bottom-right `KLING AI`
label with its version suffix. A completed encode is published atomically. No
output is written when no stable mark is found.
`MINIMAX | hailuo AI` label, and Kling AI covers the bottom-right `KLING AI` or
`KlingAI` label with its version suffix. A completed encode is published
atomically. No output is written when no stable mark is found.
HDR, PQ/HLG, and greater-than-8-bit inputs are rejected before encoding rather
than silently reduced through OpenCV's 8-bit BGR boundary.
+8 -2
View File
@@ -7,8 +7,9 @@ data/
fixtures/
provenance/ Real format and provenance fixtures used by tests
(source records live in fixtures/README.md)
visible/ Synthetic per-mark example gallery (one committed example per
registered visible mark; see fixtures/visible/README.md)
visible/ Per-mark example gallery: one synthetic canonical example per
registered mark plus documented provider originals where available
(see fixtures/visible/README.md)
calibration/
<vendor>/ Minimal controlled inputs needed to rebuild detector assets
synthid/
@@ -24,6 +25,11 @@ data/
Reproducible full-clip Gemini SynthID verdicts
```
[`image-watermark-examples.csv`](image-watermark-examples.csv) and
[`video-watermark-examples.csv`](video-watermark-examples.csv) are the coverage
indexes for real carriers. They point to each canonical binary instead of
duplicating it and record unsupported gaps and source-specific licensing.
## Storage rules
Large local-only research data (the 211 GB Spaces corpus and the frozen
+145
View File
@@ -3,6 +3,151 @@
These files exercise real container, metadata, and pixel-provenance formats.
They are test inputs, not evaluation corpora.
## OpenRouter metadata survey
The following files are original byte streams returned by OpenRouter's Image
API on 2026-09-02. Each request used a repository-maintainer account, the same
neutral still-life prompt, one 1:1 output, and no image reference. The prompt
was `A documentary photograph of three smooth river stones on pale linen beside
a small blue glass vase, soft natural daylight, plain uncluttered background,
no text, no logos`.
| File | Model | Cost | SHA-256 | Expected metadata result |
| --- | --- | --- | --- | --- |
| `recraft-v3.webp` | `recraft/recraft-v3` | USD 0.04 | `e94c99ab50410092dbb601c8269ce812879e2271e18c7880021d2118666542ae` | Valid C2PA; `recraft.ai`; `trainedAlgorithmicMedia` |
| `krea-2-medium-turbo.png` | `krea/krea-2-medium-turbo` | USD 0.015 | `ee1302c01e8da8e316e6563b0f9d36d511b9048fb2f70b8851eeeda98c5fb6a0` | No locally readable provenance |
Both were paid API generations. Recraft grants ownership for paid-plan output,
and Krea states that users own compliant outputs. They are distributed under
the repository license.
- Recraft terms: <https://www.recraft.ai/docs/plans-and-billing/commercial-rights-and-ownership>
- Krea terms: <https://www.krea.ai/krea-2-licensing>
The same survey generated `sourceful/riverflow-v2-fast` output and found no
locally readable metadata. It is not committed because Riverflow's terms
restrict redistribution and using results to test or improve machine-learning
systems. Its measured SHA-256 was
`d4d7e80a3ed450129a0e77bdf330a83eb7de1e287d5b2b737cd26a011194a270`;
OpenRouter recorded a USD 0.02 request cost.
## WaveSpeedAI metadata survey
The following measurements came from original byte streams returned by the
WaveSpeedAI API on 2026-09-03. Each request used a repository-maintainer
account and the same neutral still-life prompt recorded above. The account
balance moved from USD 12.00 to USD 11.82 for the four successful requests.
| File | WaveSpeedAI model | SHA-256 | Expected metadata result |
| --- | --- | --- | --- |
| `qwen-image.png` | `wavespeed-ai/qwen-image/text-to-image` | `5b6d832834397498d0587b9b2536f88c75e923195c43c2b71d9f7f3c5422b4f1` | No provenance signal; one non-provenance PNG `job-id` text field |
| `hunyuan-image-3.png` | `wavespeed-ai/hunyuan-image-3` | `6ae2ac073ac79b02b0934c905364f200420c2ff60fea232bd9e0a5fca785cc09` | Valid C2PA from `fal.ai` naming `fal-ai/hunyuan-image`, plus Adobe TrustMark Variant P, schema 0 |
| `kling-image-v3.png` | `kwaivgi/kling-image-v3/text-to-image` | `501ab865b47ba59b64bc0f68118300744b0d5c7be2fa4e2ecbb3a78de5d8a82b` | TC260 PNG `AIGC` field naming producer `001191110108335469089C10100`; no visible Kling mark |
| `seedream-v4.jpg` | `bytedance/seedream-v4` | `9ec6af1fc0509d74e264b9631a6cddd2ce7ce20146347fe418e9306032beb759` | No locally readable provenance |
All four original byte streams are committed. Section 11.1 of WaveSpeedAI's
terms assigns its rights in Customer Data, explicitly including Outputs, to the
customer. The live catalog records used for these requests expose no additional
license, terms URL, attribution requirement, territory restriction, or
commercial-use restriction. The maintainer therefore distributes these paid,
neutral-prompt outputs under the repository license. This conclusion applies
to these WaveSpeedAI API transactions; it does not import unrelated terms from
the vendors' direct consumer services.
- WaveSpeedAI terms: <https://wavespeed.ai/static/terms>
- WaveSpeedAI commercial-use policy: <https://wavespeed.ai/docs/commercial-use-policy>
- Qwen-Image license: <https://github.com/QwenLM/Qwen-Image/blob/main/LICENSE>
## Qwen Create consumer output
`qwen-create-qwen-image-2.png` is the original 2048 x 2048 PNG downloaded from
an authenticated Qwen Create generation on 2026-09-03. It was generated by
`Qwen-Image 2.0` at 1:1 using the neutral still-life prompt recorded above.
The current global consumer surface differs from the supported Chinese Qwen
text mark: it places a small Qwen symbol at the bottom right, not
`千问AI生成`. The existing Qwen text detector therefore correctly does not
claim this new shape (measured strict confidence 0.178).
- SHA-256: `60c39ed8aedb081193c62289be549785e74d31fc8d071961003fabd74712429d`
- Expected metadata signal: TC260 AIGC label
- Expected `ContentProducer`: `001191440101MA9Y9T4H7A00000`
- Expected visible result: no registered visible mark; the Qwen symbol is a
newly observed, currently unsupported variant
- License: repository license; generated and explicitly cleared by the
maintainer
- Qwen Create terms: <https://terms.alicdn.com/legal-agreement/terms/c_end_product_protocol/20260519200335649/20260519200335649.html>
## Open DWT-DCT
`sdxl-dwt-dct.png` is a real AI-generated carrier with the public 48-bit SDXL
watermark embedded by ShieldMnt's `invisible-watermark` encoder using its
`dwtDct` method. The carrier is the original `kling-image-v3.png` WaveSpeedAI
output documented above, not a procedurally generated test texture. Encoding
was lossless to PNG and removed the source container metadata; the resulting
fixture contains only the expected open DWT-DCT signal.
- Source carrier SHA-256: `501ab865b47ba59b64bc0f68118300744b0d5c7be2fa4e2ecbb3a78de5d8a82b`
- Fixture SHA-256: `9ec3dcbfea19e4651b898f98ada28a1c81f054d63621a0bba2d484aa77da251a`
- Payload: `0b101100111110110010010000011110111011000110011110`
- Expected signal: open DWT-DCT, `Stable Diffusion XL`
- Verification: 48/48 payload bits recovered by both the upstream decoder and
the in-tree decoder; `identify` reports the expected high-confidence signal
- Encoder: <https://github.com/ShieldMnt/invisible-watermark>
- License: repository license; the encoder is MIT-licensed
## Microsoft InvisMark
### Microsoft Paint
`microsoft-paint-invismark.png` is the original byte stream supplied directly
by Xusheng Li on 2026-09-02 after his reverse-engineering research documented
Paint's pixel watermark and its matching C2PA soft binding. He authorized public
redistribution of the fixture under the MIT license. The neutral image was
generated by Microsoft Paint from the prompt `a cobalt blue circle above a tiny
orange square`.
- Source research: <https://xusheng.dev/posts/reversing/mspaint_invisible_watermark/main/>
- SHA-256: `74eda22d210d534fcb5b75bedf8acd3bd110b48a95f4b9e546cd34f546effc05`
- Dimensions: 1254 x 1254 RGB PNG
- Expected signal: C2PA soft binding `com.microsoft.invismark.1`
- Expected watermark ID: `a4ed421e-7c37-4409-998e-f8788c564572`
- Expected platform: `Microsoft (Copilot / Designer)`
- Expected C2PA result: valid asset binding and signature
- License: MIT, reproduced in `../licenses/xusheng-li-paint-invismark-MIT.txt`
### Microsoft MAI-Image through OpenRouter
`microsoft-invismark.png` is the original byte stream returned by OpenRouter's
Image API for `microsoft/mai-image-2.5-pro` on 2026-09-02. The request used a
repository-maintainer account and asked for one 1:1 image. OpenRouter routed
the request to Microsoft Azure. Microsoft states that it does not claim
ownership of AI-service output; the maintainer distributes this output under
the repository license.
- SHA-256: `be27ac7c351e7f381dc69e140ca3611ecd9db81e40926c68050ceffdd0ce6c04`
- Expected signal: C2PA soft binding `com.microsoft.invismark.1`
- Expected watermark ID: `19b070a1-23f0-40eb-826c-cade306337eb`
- Expected platform: `Microsoft (Copilot / Designer)`
- Request cost recorded by OpenRouter: USD 0.110757
- License: repository license
- Terms: <https://www.microsoft.com/en/servicesagreement/>
### Microsoft MAI-Image through Bing Image Creator
`microsoft-bing-image-creator.jpg` is the original download produced without
sign-in by Microsoft's public Bing Image Creator on 2026-09-03. The page named
the model `MAI-Image-2.5-Flash`; it used the neutral still-life prompt recorded
above and the 3:2 aspect ratio. The downloaded image has no visible AI badge,
so it does not close the separate visible Microsoft-badge fixture request.
- SHA-256: `55cad5355b6eeeb25f84ea074f85c0ea55b82d63c27f43e3429bb75f28734ac7`
- Dimensions: 1248 x 832 RGB JPEG
- Expected signal: valid C2PA plus soft binding `com.microsoft.invismark.1`
- Expected watermark ID: `aec9a34e-665f-4549-9c02-9d29337a5de6`
- Expected platform: `Microsoft (Copilot / Designer)`
- License: repository license
- Terms: <https://www.microsoft.com/en/servicesagreement/>
## Adobe TrustMark
`adobe-trustmark-p.png` is Adobe's official `images/ghost_P.png` Variant P
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 892 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 955 KiB

+152 -2
View File
@@ -17,6 +17,154 @@ The examples demonstrate DETECTION geometry and house style, not vendor raster
fidelity; real-world variants (fonts, opacities, sizes) are covered by the
engines' recorded calibration evidence.
The Qwen, Kling, Yuanbao, Baidu, Jimeng, LiblibAI, Microsoft, Sora, Hailuo, and Veo
directories additionally contain real provider-marked sources. These files
retain the source bytes and names used here are deliberately distinct from the
generated canonical examples. The current LiblibAI output is evidence of a
provider format change, not a positive example for the registered historical
wordmark detector:
| File | Source | SHA-256 | Expected detector |
| --- | --- | --- | --- |
| `qwen/provider-original.png` | OpenNoMark `examples/qwen/image_324855086256596.png`, commit `9455d2d` | `dce9e17c9b9b0483c672b71183b6eb5562f8205bb39a6da14451e07db13b1a50` | Qwen, confidence 0.73 |
| `kling/provider-original-direct.png` | Authenticated Kling AI IMAGE 3.0 generation, downloaded 2026-09-04 | `aefbf69a99cf7eb4211cc0513c6979b81731107a1bc605c972e01900b7c56f51` | Kling, confidence 0.43 |
| `yuanbao/provider-original.png` | Authenticated Tencent Yuanbao AI Image generation, downloaded 2026-09-05 | `e4fd856565d0434ef06deb78ff2088e597c4fec98640ad737213c7c0a917c82d` | Yuanbao, confidence 0.49 |
| `baidu/provider-original.jpeg` | OpenNoMark `examples/baidu/baidu_01.jpeg`, commit `9455d2d` | `52852c528f1a7b8918b665b2fcf883737ecc5016481d9c7683ebe07e152cc9d2` | Baidu, confidence 0.66 |
| `jimeng_pill/provider-published-example.jpg` | JPEG embedded on page 29 of Zhonglun W&D's 2026 *Bank Finance Legal Newsletter* | `1182fba74d3753785345f8a35d09a8c9b52ea8383ebe9ee999d0d027437f03fd` | Jimeng AI pill, confidence 0.28; Jimeng wordmark, confidence 0.61 |
| `liblib/current-provider-output.png` | Authenticated LiblibAI Smart Image V2 generation, downloaded 2026-09-05 | `60398d4f06fd50e047d3321c62405dae0a1c4a7070e77ac5e0548c516257d2e3` | No `liblib` detection: current top-left `AI生成` pill differs from the registered bottom-center `LiblibAI` wordmark |
| `microsoft/provider-original.png` | Authenticated Microsoft Copilot generation, downloaded 2026-09-03 | `0e76fb8aa0540b18fee4d63ac4b5fd63c7eee310474e896d21d27e668403afe7` | Microsoft, confidence 0.54 |
| `sora/provider-frame.jpg` | Sora 2 video frame from LLinked's Apache-2.0 `sora-watermark-dataset` | `bbcf70b737c30568668572a5bb23e70c52c9687808b0714ca98f9fac073f12c4` | Sora frame localization, confidence 0.69 |
| `sora/provider-original.mp4` | LLinked's Apache-2.0 `SoraWatermarkCleaner` fixture `resources/puppies.mp4`, commit `ad35ce9` | `7dd8a9d43d43cd5b901946c7ba14e8018e18d47e75f0f774157327ba7e9181c5` | Sora, 300/300 stable frames; Sora C2PA claim |
| `hailuo/provider-original.mp4` | Authenticated Hailuo AI MiniMax H3 generation, downloaded with `With Watermark` selected on 2026-09-03 | `6152609e0339c443af79e244440e1b1050f3c615a50f76bfaf6146584e68142f` | Hailuo, 124/124 stable frames |
| `kling/provider-original.mp4` | Authenticated Kling AI VIDEO 3.0 generation, downloaded 2026-09-04 | `f3e8d6a765fca699036d68fde28eb16f7aed97b6702f06f318e61ae8d4e53a69` | Kling, 121/121 stable frames |
| `veo/provider-original.mp4` | Authenticated Google Flow Veo 3.1 Fast generation with `Visible watermarking` enabled, downloaded 2026-09-03 | `e778194b545e34dafd2d5be58691031a070862890c41e6c527a652a114a7c7a4` | Veo, 192/192 stable frames |
OpenNoMark describes its corpus as real-image examples and distributes the
repository under CC BY-NC-SA 4.0. Its license text is reproduced in
`../../licenses/open-no-mark-CC-BY-NC-SA-4.0.txt`. The upstream repository does
not record per-image authorship beyond that repository-level license and claim.
The Qwen and Baidu files remain under that license and are not offered under
this repository's Apache-2.0 license.
The Microsoft file is the original 1024 x 1024 PNG downloaded from an
authenticated Copilot image-generation conversation. It uses the neutral
still-life prompt recorded in `../README.md`, carries the visible top-right
`Made with AI` pill, valid Microsoft C2PA, and InvisMark soft-binding ID
`975290d4-9c2f-4359-ad5a-92c5b5b850a4`. Microsoft does not claim ownership of
AI-service output; the maintainer distributes this generated output under the
repository license.
The Sora frame is the original JPEG frame
`images/train/images/failed_image_000016_frame_000150.jpg` from LLinked's
`sora-watermark-dataset`. The dataset card describes its images as frames
extracted from Sora-generated videos, provides manual watermark boxes, and
licenses the dataset under Apache-2.0. This neutral CGI frame carries the
current moving mascot-and-wordmark raster and passes the in-tree frame
localizer.
The Sora MP4 is the byte-for-byte `resources/puppies.mp4` fixture tracked by
LLinked's Apache-2.0 `SoraWatermarkCleaner` at commit `ad35ce9`. It preserves
the moving mascot-and-wordmark raster, timing, audio, and the source C2PA
manifest. The in-tree video path detects Sora on all 300 frames. The C2PA claim
names Sora as the generator and OpenAI/Truepic as the issuer; its content
integrity and signature verify, while the reader reports the signer certificate
as expired and untrusted.
- Sora watermark dataset:
<https://huggingface.co/datasets/LLinked/sora-watermark-dataset>
- SoraWatermarkCleaner source fixture:
<https://github.com/linkedlist771/SoraWatermarkCleaner/blob/ad35ce99a34fc9f9f6d448537e0c348311e74ccd/resources/puppies.mp4>
The Hailuo file is the original 2944 x 1248, 5-second MP4 downloaded from an
authenticated Hailuo AI generation. It was generated with MiniMax H3 for 60
top-up credits from the neutral prompt `A blue ceramic mug slowly rotates on a
plain light gray studio table, soft even lighting, fixed camera, clean
background.` The download menu explicitly offered clean and `With Watermark`
variants; this fixture is the latter. It carries the visible lower-right
`MINIMAX | hailuo AI` composite and a TC260 AIGC label naming MiniMax as the
producer. The maintainer generated and explicitly cleared this output for use
under the repository license.
- Hailuo AI paid-service and credit terms:
<https://hailuoai.video/doc/payment-policy.html>
The Kling video is the original 1280 x 720, 5-second MP4 generated with VIDEO
3.0 for 30 credits at 720p without native audio. Its neutral prompt was `A
locked-off wide shot of a small white paper boat floating across a calm
dark-blue pond at dusk, gentle ripples, realistic cinematic lighting, no text,
no logos.` It carries the fixed lower-right `KlingAI 3.0` logo and no locally
readable provenance metadata. The maintainer generated and explicitly cleared
the output for this noncommercial deterministic computer-vision regression
fixture. Kling AI's terms say the user owns applicable rights in the output,
require a regular account to retain Kling branding when distributing it, and
prohibit commercial use without written permission. The fixture retains the
brand and remains subject to those terms; it is not offered under the
repository license.
- Kling AI terms of service: <https://kling.ai/docs/user-policy>
- Kling AI paid-service terms: <https://kling.ai/docs/payment-policy>
The direct Kling image is the original 1024 x 1024 RGB PNG generated with
IMAGE 3.0 in 1K SD mode for one credit. Its neutral prompt was `A documentary
photograph of three smooth river stones on pale linen beside a small blue glass
vase, soft natural daylight, plain uncluttered background, no text, no logos.`
It carries the current lower-right `KlingAI 3.0` mark and a TC260 AIGC field
whose producer code `001191110108335469089C10100` normalizes to Kuaishou's
registered USCC. The maintainer generated and explicitly cleared the output for
this noncommercial deterministic computer-vision regression fixture. The same
Kling terms and retained-brand restriction listed for the video apply; the file
is not offered under the repository license.
The Yuanbao image is the original 1776 x 1328 RGB PNG returned by an
authenticated AI Image conversation. It was generated at no charge from the
neutral prompt `A documentary photograph of a blue ceramic mug on a plain
light-gray studio table, soft even lighting, no text, no logos, no people.` It
carries the visible lower-right two-line `元宝` / `AI生成` mark and a TC260 AIGC
field naming Tencent's registered producer. The maintainer generated and
explicitly cleared this output for use under the repository license.
The Jimeng image is the byte-for-byte embedded JPEG extracted with
`pdfimages` from page 29 of Zhonglun W&D's publicly distributed 2026 *Bank
Finance Legal Newsletter*. It is a real Jimeng-generated debt-collection
poster carrying both the top-left `AI生成` pill and the lower-right `即梦AI`
wordmark. The repository retains one 705 x 705 image rather than the complete
33-page publication, solely to test identification and removal of the two
marks. Copyright remains with the source rights holders; this research fixture
is not licensed under Apache-2.0. See
`../../licenses/zhonglunwende-jimeng-fair-use.txt` for provenance and use
limitations.
- Zhonglun W&D source publication:
<https://www.zhonglunwende.com/uploads/20260202/97f9a8f64892eac6b7e04e5e7ea45d0e.pdf>
The LiblibAI image is the original 2048 x 2048 RGBA PNG generated with Smart
Image V2 for 18 introductory points from the same neutral prompt. It carries a
top-left `AI生成` pill and a TC260 AIGC field whose producer code contains
LiblibAI's registered USCC, but it does not carry the historical bottom-center
`LiblibAI` wordmark that the registered `liblib` engine detects. This file
therefore records the current provider behavior without falsely closing the
historical positive-example gap. The maintainer generated and explicitly
cleared the output for use under the repository license.
- LiblibAI user agreement (generated-output ownership and watermark choices):
<https://www.liblib.art/activities/468ad794ccc7408d81757fd91be003ec?hideHeader=1>
The Veo file is the original 1280 x 720, 8-second MP4 downloaded from an
authenticated Google Flow project. It was generated with Veo 3.1 Fast for 20
Flow credits from the neutral prompt `A blue mug rotates on gray. Static camera.
No people, text, logos, or audio.` after the account's `Visible watermarking`
setting was enabled. It carries the fixed, low-contrast `Veo` text at the bottom
right, valid Google C2PA, and an invisible SynthID marker. Google documents that
Flow clips can be downloaded and shared, states that Google does not claim
ownership of original generated content, and explicitly identifies SynthID in
all Flow video output. The maintainer distributes this generated output under
the repository license.
- Google Flow output and watermark documentation:
<https://support.google.com/flow/answer/16353333>
- Google Flow download and sharing documentation:
<https://support.google.com/flow/answer/16935308>
## Regeneration
uv run python scripts/render_visible_examples.py
@@ -27,12 +175,14 @@ detect on its own example, so regeneration is the fix point for drift.
## Layout
<mark-key>/example.png 1536x2048..2048x2048 PNG, one per image mark
<mark-key>/provider-original.*
Documented real provider-marked source where available
<mark-key>/example.mp4 960x540 90-frame clip, one per video mark
(kling carries both: it is registered in both registries)
Special cases: `gemini` composites the sparkle alpha map at the provider's
configured position; `jimeng_pill` is the capture-less pill at the measured
3:4 portrait geometry; `microsoft` is the opaque white pill with dark text
configured position; `jimeng_pill` uses the measured 3:4 portrait geometry;
`microsoft` is the opaque white pill with dark text
holes (the discriminator its detector keys on). Video examples composite the
detector's own synthetic template on every frame; where two marks share a
shape family the example carries the discriminative variant (`veo` the legacy
Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

+19
View File
@@ -0,0 +1,19 @@
key,provider,watermark_type,path,status,verification,license
gemini,Google Gemini,visible sparkle,data/calibration/gemini/gemini_black_2048.png,available,original provider capture and detector calibration,repository
doubao,ByteDance Doubao,visible text,data/calibration/doubao/doubao_gray_1x1_1.png,available,original provider capture and detector calibration,repository
jimeng,ByteDance Jimeng,visible wordmark,data/calibration/jimeng/jimeng_cap_C.png,available,original provider capture and detector calibration,repository
qwen,Alibaba Qwen consumer app,visible text,data/fixtures/visible/qwen/provider-original.png,available,detected by Qwen engine; distributed under the OpenNoMark repository's CC BY-NC-SA 4.0 license; current global Qwen Create uses a different symbol captured in provenance fixtures,CC-BY-NC-SA-4.0; excluded from Apache-2.0
kling,Kuaishou Kling AI,visible text,data/fixtures/visible/kling/provider-original-direct.png,available,authenticated Kling IMAGE 3.0 original; Latin KlingAI 3.0 mark detected at confidence 0.43; TC260 producer maps to Kuaishou,subject to Kling AI terms; noncommercial fixture
yuanbao,Tencent Yuanbao,visible two-line text,data/fixtures/visible/yuanbao/provider-original.png,available,authenticated Yuanbao original; detected by Yuanbao engine at confidence 0.49; TC260 producer metadata maps to Tencent,repository
samsung,Samsung Galaxy AI,visible Italian text,data/calibration/samsung/samsung_gray_1.png,available,original provider capture and detector calibration,repository
runninghub,RunningHub,visible text,,missing,no redistributable provider original found,
baidu,Baidu,visible text and tag,data/fixtures/visible/baidu/provider-original.jpeg,available,detected by Baidu engine; distributed under the OpenNoMark repository's CC BY-NC-SA 4.0 license,CC-BY-NC-SA-4.0; excluded from Apache-2.0
liblib,LiblibAI,visible wordmark,data/fixtures/visible/liblib/current-provider-output.png,partial,"authenticated current LiblibAI original carries a top-left AI生成 pill rather than the registered historical bottom-center LiblibAI wordmark; TC260 producer metadata maps to LiblibAI; historical positive remains missing",repository
microsoft,Microsoft image output,visible top-right AI badge,data/fixtures/visible/microsoft/provider-original.png,available,authenticated Copilot original detected by Microsoft engine at confidence 0.54; also has valid C2PA and InvisMark,repository
jimeng_pill,ByteDance Jimeng,visible AI pill,data/fixtures/visible/jimeng_pill/provider-published-example.jpg,available,"real Jimeng output embedded in page 29 of Zhonglun W&D's 2026 Bank Finance Legal Newsletter; direct jimeng_pill detection confidence 0.28; the same image also carries the Jimeng wordmark",fair-use research fixture; excluded from Apache-2.0
google_synthid,Google,image SynthID,data/synthid/originals/Gemini_Generated_Image_633uuy633uuy633u.png,available,provider oracle recorded in data/synthid/manifest.csv,repository
openai_synthid,OpenAI,image SynthID,"data/synthid/originals/ChatGPT Image May 30, 2026, 10_31_08 AM.png",available,provider oracle recorded in data/synthid/manifest.csv,repository
meta_content_seal,Meta Muse Image,image Content Seal,data/contentseal/originals/gen_fox_forest.webp,available,provider oracle recorded in data/contentseal/manifest.csv,repository
microsoft_invismark,Microsoft Paint,image InvisMark,data/fixtures/provenance/microsoft-paint-invismark.png,available,"original Paint output supplied by Xusheng Li; valid C2PA names com.microsoft.invismark.1 and watermark ID a4ed421e-7c37-4409-998e-f8788c564572",MIT
open_dwt_dct,Stable Diffusion / SDXL / FLUX workflows,open DWT-DCT,data/fixtures/provenance/sdxl-dwt-dct.png,available,real AI-generated carrier with the official SDXL payload recovered 48/48 by upstream and in-tree decoders,repository
adobe_trustmark_p,Adobe,image TrustMark Variant P,data/fixtures/provenance/adobe-trustmark-p.png,available,official Adobe example and expected schema recorded in data/fixtures/README.md,MIT
1 key provider watermark_type path status verification license
2 gemini Google Gemini visible sparkle data/calibration/gemini/gemini_black_2048.png available original provider capture and detector calibration repository
3 doubao ByteDance Doubao visible text data/calibration/doubao/doubao_gray_1x1_1.png available original provider capture and detector calibration repository
4 jimeng ByteDance Jimeng visible wordmark data/calibration/jimeng/jimeng_cap_C.png available original provider capture and detector calibration repository
5 qwen Alibaba Qwen consumer app visible text data/fixtures/visible/qwen/provider-original.png available detected by Qwen engine; distributed under the OpenNoMark repository's CC BY-NC-SA 4.0 license; current global Qwen Create uses a different symbol captured in provenance fixtures CC-BY-NC-SA-4.0; excluded from Apache-2.0
6 kling Kuaishou Kling AI visible text data/fixtures/visible/kling/provider-original-direct.png available authenticated Kling IMAGE 3.0 original; Latin KlingAI 3.0 mark detected at confidence 0.43; TC260 producer maps to Kuaishou subject to Kling AI terms; noncommercial fixture
7 yuanbao Tencent Yuanbao visible two-line text data/fixtures/visible/yuanbao/provider-original.png available authenticated Yuanbao original; detected by Yuanbao engine at confidence 0.49; TC260 producer metadata maps to Tencent repository
8 samsung Samsung Galaxy AI visible Italian text data/calibration/samsung/samsung_gray_1.png available original provider capture and detector calibration repository
9 runninghub RunningHub visible text missing no redistributable provider original found
10 baidu Baidu visible text and tag data/fixtures/visible/baidu/provider-original.jpeg available detected by Baidu engine; distributed under the OpenNoMark repository's CC BY-NC-SA 4.0 license CC-BY-NC-SA-4.0; excluded from Apache-2.0
11 liblib LiblibAI visible wordmark data/fixtures/visible/liblib/current-provider-output.png partial authenticated current LiblibAI original carries a top-left AI生成 pill rather than the registered historical bottom-center LiblibAI wordmark; TC260 producer metadata maps to LiblibAI; historical positive remains missing repository
12 microsoft Microsoft image output visible top-right AI badge data/fixtures/visible/microsoft/provider-original.png available authenticated Copilot original detected by Microsoft engine at confidence 0.54; also has valid C2PA and InvisMark repository
13 jimeng_pill ByteDance Jimeng visible AI pill data/fixtures/visible/jimeng_pill/provider-published-example.jpg available real Jimeng output embedded in page 29 of Zhonglun W&D's 2026 Bank Finance Legal Newsletter; direct jimeng_pill detection confidence 0.28; the same image also carries the Jimeng wordmark fair-use research fixture; excluded from Apache-2.0
14 google_synthid Google image SynthID data/synthid/originals/Gemini_Generated_Image_633uuy633uuy633u.png available provider oracle recorded in data/synthid/manifest.csv repository
15 openai_synthid OpenAI image SynthID data/synthid/originals/ChatGPT Image May 30, 2026, 10_31_08 AM.png available provider oracle recorded in data/synthid/manifest.csv repository
16 meta_content_seal Meta Muse Image image Content Seal data/contentseal/originals/gen_fox_forest.webp available provider oracle recorded in data/contentseal/manifest.csv repository
17 microsoft_invismark Microsoft Paint image InvisMark data/fixtures/provenance/microsoft-paint-invismark.png available original Paint output supplied by Xusheng Li; valid C2PA names com.microsoft.invismark.1 and watermark ID a4ed421e-7c37-4409-998e-f8788c564572 MIT
18 open_dwt_dct Stable Diffusion / SDXL / FLUX workflows open DWT-DCT data/fixtures/provenance/sdxl-dwt-dct.png available real AI-generated carrier with the official SDXL payload recovered 48/48 by upstream and in-tree decoders repository
19 adobe_trustmark_p Adobe image TrustMark Variant P data/fixtures/provenance/adobe-trustmark-p.png available official Adobe example and expected schema recorded in data/fixtures/README.md MIT
@@ -0,0 +1,439 @@
Attribution-NonCommercial-ShareAlike 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
Public License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-NonCommercial-ShareAlike 4.0 International Public License
("Public License"). To the extent this Public License may be
interpreted as a contract, You are granted the Licensed Rights in
consideration of Your acceptance of these terms and conditions, and the
Licensor grants You such rights in consideration of benefits the
Licensor receives from making the Licensed Material available under
these terms and conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. BY-NC-SA Compatible License means a license listed at
creativecommons.org/compatiblelicenses, approved by Creative
Commons as essentially the equivalent of this Public License.
d. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
e. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
f. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
g. License Elements means the license attributes listed in the name
of a Creative Commons Public License. The License Elements of this
Public License are Attribution, NonCommercial, and ShareAlike.
h. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
i. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
j. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
k. NonCommercial means not primarily intended for or directed towards
commercial advantage or monetary compensation. For purposes of
this Public License, the exchange of the Licensed Material for
other material subject to Copyright and Similar Rights by digital
file-sharing or similar means is NonCommercial provided there is
no payment of monetary compensation in connection with the
exchange.
l. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
m. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
n. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part, for NonCommercial purposes only; and
b. produce, reproduce, and Share Adapted Material for
NonCommercial purposes only.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. Additional offer from the Licensor -- Adapted Material.
Every recipient of Adapted Material from You
automatically receives an offer from the Licensor to
exercise the Licensed Rights in the Adapted Material
under the terms and conditions of the Adapter's License
You apply.
c. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties, including when
the Licensed Material is used other than for NonCommercial
purposes.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
b. ShareAlike.
In addition to the conditions in Section 3(a), if You Share
Adapted Material You produce, the following conditions also apply.
1. The Adapter's License You apply must be a Creative Commons
license with the same License Elements, this version or
later, or a BY-NC-SA Compatible License.
2. You must include the text of, or the URI or hyperlink to, the
Adapter's License You apply. You may satisfy this condition
in any reasonable manner based on the medium, means, and
context in which You Share Adapted Material.
3. You may not offer or impose any additional or different terms
or conditions on, or apply any Effective Technological
Measures to, Adapted Material that restrict exercise of the
rights granted under the Adapter's License You apply.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of databases for NonCommercial purposes
only;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material,
including for purposes of Section 3(b); and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.
Creative Commons may be contacted at creativecommons.org.
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Xusheng Li
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,22 @@
Jimeng published-image research fixture
=======================================
File:
data/fixtures/visible/jimeng_pill/provider-published-example.jpg
Source:
Zhonglun W&D, Bank Finance Legal Newsletter, 2026, page 29
https://www.zhonglunwende.com/uploads/20260202/97f9a8f64892eac6b7e04e5e7ea45d0e.pdf
Extraction:
The file is the JPEG image embedded in the source PDF, extracted byte-for-byte
with pdfimages. SHA-256:
1182fba74d3753785345f8a35d09a8c9b52ea8383ebe9ee999d0d027437f03fd
Use in this repository:
This single image is retained as a minimal research and interoperability
fixture for identifying and removing the visible Jimeng AI-generation marks.
It is not offered under this repository's Apache-2.0 license. Copyright and
all other rights remain with their respective holders. No license for uses
outside the repository's research, testing, criticism, and documentation is
granted here.
+8
View File
@@ -0,0 +1,8 @@
key,provider,watermark_type,path,status,verification,license
sora,OpenAI Sora,visible moving mascot and wordmark,data/fixtures/visible/sora/provider-original.mp4,available,"Apache-2.0 upstream original; detected by Sora engine on 300/300 frames; C2PA claim names Sora and OpenAI/Truepic, with valid integrity and signature but an expired untrusted signer certificate",Apache-2.0
veo,Google Veo,visible fixed text,data/fixtures/visible/veo/provider-original.mp4,available,"authenticated Google Flow Veo 3.1 Fast original; detected by Veo engine on 192/192 frames; valid C2PA and provider-confirmed SynthID",repository
seedance,ByteDance Seedance,visible boxed AI label,,missing,"OpenRouter Seedance 2.0 Mini with watermark=true produced BytePlus C2PA but no visible boxed AI label; direct consumer original still needed",
doubao,ByteDance Doubao,visible fixed text,,missing,no redistributable provider original found,
dola,ByteDance Dola,visible fixed text,,missing,no redistributable provider original found,
hailuo,MiniMax Hailuo AI,visible composite label,data/fixtures/visible/hailuo/provider-original.mp4,available,"authenticated Hailuo AI MiniMax H3 original downloaded with With Watermark selected; detected by Hailuo engine on 124/124 frames; TC260 AIGC label names MiniMax",repository
kling,Kuaishou Kling AI,visible fixed logo and wordmark,data/fixtures/visible/kling/provider-original.mp4,available,"authenticated Kling AI VIDEO 3.0 original; detected by Kling engine on 121/121 frames; no locally readable provenance metadata",Kling AI terms; noncommercial fixture
1 key provider watermark_type path status verification license
2 sora OpenAI Sora visible moving mascot and wordmark data/fixtures/visible/sora/provider-original.mp4 available Apache-2.0 upstream original; detected by Sora engine on 300/300 frames; C2PA claim names Sora and OpenAI/Truepic, with valid integrity and signature but an expired untrusted signer certificate Apache-2.0
3 veo Google Veo visible fixed text data/fixtures/visible/veo/provider-original.mp4 available authenticated Google Flow Veo 3.1 Fast original; detected by Veo engine on 192/192 frames; valid C2PA and provider-confirmed SynthID repository
4 seedance ByteDance Seedance visible boxed AI label missing OpenRouter Seedance 2.0 Mini with watermark=true produced BytePlus C2PA but no visible boxed AI label; direct consumer original still needed
5 doubao ByteDance Doubao visible fixed text missing no redistributable provider original found
6 dola ByteDance Dola visible fixed text missing no redistributable provider original found
7 hailuo MiniMax Hailuo AI visible composite label data/fixtures/visible/hailuo/provider-original.mp4 available authenticated Hailuo AI MiniMax H3 original downloaded with With Watermark selected; detected by Hailuo engine on 124/124 frames; TC260 AIGC label names MiniMax repository
8 kling Kuaishou Kling AI visible fixed logo and wordmark data/fixtures/visible/kling/provider-original.mp4 available authenticated Kling AI VIDEO 3.0 original; detected by Kling engine on 121/121 frames; no locally readable provenance metadata Kling AI terms; noncommercial fixture
+3 -2
View File
@@ -112,8 +112,9 @@ one observed cross-source spread):
Derived by the shipped rule: 0.08 + (0.08 - 0.04) = 0.12, rounded to the
measured rung **0.125**, which was then oracle-verified clean on both worst
sources. Oracle: the public `ai.azure.com/nextgen/validate` page; its
`Inconclusive` verdict is the site's watermark-negative, weaker than an
API-level `Watermark: false`, the same caveat the qwen cohort carries.
`Inconclusive` verdict is the site's clean outcome and provides the provider
oracle used for this certification. The API exposes a more detailed report,
not a different class of certification.
## Fidelity at the floors (scripts/fidelity_metrics.py)
+5
View File
@@ -19,3 +19,8 @@ This page records notices required by source dependencies and licensed derivativ
TrustMark Variant P example, licensed under MIT. Its source commit, digest,
and reproduced license are recorded beside the fixture in
`data/fixtures/README.md` and `data/licenses/adobe-trustmark-MIT.txt`.
- `data/fixtures/provenance/microsoft-paint-invismark.png` is an original
Microsoft Paint output supplied by Xusheng Li and licensed by him under MIT.
Its source research, digest, expected signal, and reproduced license are
recorded in `data/fixtures/README.md` and
`data/licenses/xusheng-li-paint-invismark-MIT.txt`.
+3 -3
View File
@@ -373,9 +373,9 @@ supported AI provenance metadata without transcoding streams.
`video visible` and `remove_video_visible` additionally support the moving
Sora 2 mascot and wordmark, the current Veo four-point diamond, the legacy
`Veo` text, the Seedance boxed `AI` label, the fixed `Dola AI` text, the Hailuo AI
MINIMAX/Hailuo AI composite label, and the bottom-right Kling AI label with its
version suffix. Detection requires a recurring visual candidate across
adjacent frames. Fixed-mark candidates must remain anchored rather than
MINIMAX/Hailuo AI composite label, and the bottom-right Kling AI `KLING AI` or
`KlingAI` label with its version suffix. Detection requires a recurring visual
candidate across adjacent frames. Fixed-mark candidates must remain anchored rather than
drifting with a scene object. Kling AI also requires a bright low-saturation
candidate near the expected frame edge. Provider provenance can recover
low-contrast runs only after visual evidence exists for the marks that define a
+21 -9
View File
@@ -333,7 +333,7 @@ synthetic silhouettes for the current four-point diamond and legacy `Veo`
text. Seedance uses a synthetic rounded boxed-`AI` silhouette, while Dola uses
an OpenCV-font `Dola AI` silhouette. Hailuo AI uses a synthetic waveform,
MINIMAX/Hailuo AI text, separator, and ring. Kling AI combines synthetic font
variants with a ring approximation of its swirl; the logo path rescues
and capitalization variants with a ring approximation of its swirl; the logo path rescues
wordmarks whose version or font differs, while the edge and white-label gates
reject recurring scene texture. All fixed-mark searches are bounded to the
expected lower-frame area and calibrated independently. A strong relocated Veo
@@ -345,7 +345,7 @@ normalized representations across all detectors, and caches resized synthetic
template features for the fixed stream geometry. Provider confidence scales
are not comparable: selection applies each provider's temporal arbiter and
takes the first stable result in specificity order (`sora`, `veo`, `seedance`,
`dola`, `hailuo`, `kling`). An explicit mark uses the same scan path with one
`doubao`, `dola`, `hailuo`, `kling`). An explicit mark uses the same scan path with one
candidate. Removal also collects authoritative per-frame timestamps for the
encoder, while identification omits that unused ffprobe pass.
@@ -1199,8 +1199,8 @@ the platform sentence `identify` reports for it, and the metadata signals that
confirm its vendor. `identify._VISIBLE_MARK_PLATFORM` and the signal mapping in
`api.visible_provenance` are derived from those rows rather than hand-maintained
beside them, so registering a mark is one edit. Two marks carry no platform of
their own: the Gemini sparkle has its own higher-confidence path, and the
capture-less pill is too weak to attribute.
their own: the Gemini sparkle has its own higher-confidence path, and the pill
alone is too weak to attribute.
The set of marks that veto the pill is DERIVED from the registry rows: every mark
under the same label regime (`tc260`) belonging to a different product. It used to
@@ -1285,6 +1285,15 @@ be represented by the shared base:
- [`liblib_engine.py`](../src/remove_ai_watermarks/liblib_engine.py)
- [`microsoft_engine.py`](../src/remove_ai_watermarks/microsoft_engine.py)
Kling keeps two independently gated silhouettes under one registry key. The
older measured cohort uses `可灵AI 3.0`; a direct authenticated IMAGE 3.0 export
on 2026-09-04 uses `KlingAI 3.0`. The Latin template scored 0.429 on that 1024 x
1024 provider original. Its narrow 0.9/1.0/1.1 scale ladder scored at most 0.349
across the 94 available neighboring real and synthetic image controls, while an
adversarial solid corner blob reached 0.379, so its strict gate is 0.40. This is
one-positive provisional calibration, intentionally separate from the older
CJK detector rather than presented as broad recall evidence.
The measured Microsoft badge variant (2026-08-27 registration) is the first
`tr`-corner mark and the first `long`-side scale basis: the pill tracks the
render dimension, so a
@@ -1406,12 +1415,15 @@ correlate with textured corners, not with pills: the edge-NCC keys on "text-like
structure top-left". As with the Jimeng wordmark's silhouette, no threshold repairs
this; a bare arm needs a detector that keys on the pill's rounded-rectangle
geometry, and until one exists the bare-arm question is settled by this
measurement, not by gate tuning. Harness: `data/spaces/_pill_bare_measure.py`
measurement, not by gate tuning. The local calibration harness is not tracked.
(cohorts A/B/C over the no-signal pool).
The capture-less Jimeng pill lives in
The weak Jimeng pill detector lives in
[`pill_engine.py`](../src/remove_ai_watermarks/pill_engine.py). It uses a
synthetic silhouette for detection and a fixed top-left footprint.
synthetic silhouette for detection and a fixed top-left footprint. Its public,
published real-world regression carrier is
`data/fixtures/visible/jimeng_pill/provider-published-example.jpg`; the direct
detector scores the pill at 0.28 and the same image's Jimeng wordmark at 0.61.
Each engine has a corresponding test module under [`tests/`](../tests/).
Shared behavior is covered by:
@@ -1434,10 +1446,10 @@ negative, so the bar stays at 0.35. Through the shipped arbiter
(`identify_video`): 32/39 selected as doubao, 0/25 false; the remainder split
between no stable run (5) and cross-template ties handed to earlier table
entries (sora 1, kling 1) -- the registry's known order-decides behavior, not a
doubao-specific defect. Harness: `data/spaces/_doubao_seq_measure.py`.
doubao-specific defect. The local sequence-calibration harness is not tracked.
Two further video candidates were measured and PARKED the same day (2026-08-28,
`data/spaces/_rh_jm_video_calibrate.py`): the Jimeng CN wordmark does not
local RunningHub/Jimeng video calibration): the Jimeng CN wordmark does not
separate on video (1/5 positives vs 2/25 negatives hold a 12-frame 0.35 run --
the same silhouette-generality the image jimeng engine documents), and
RunningHub video separates (1/2 positives, 0/25 negatives) but a two-positive
+18 -11
View File
@@ -14,12 +14,12 @@ The `visible` command registers these mark keys:
| `doubao` | `豆包AI生成` | Bottom right | Vendor specific text detector. |
| `jimeng` | `★ 即梦AI` | Bottom right | Vendor specific text detector. |
| `qwen` | `千问AI生成` | Bottom right | Strict visual gate. |
| `kling` | `可灵AI 3.0` | Bottom right | Only calibrated variants are covered. |
| `kling` | `可灵AI 3.0` or `KlingAI 3.0` | Bottom right | Separate calibrated silhouettes cover the older CJK and current IMAGE 3.0 Latin variants. |
| `yuanbao` | `元宝` over `AI生成` | Bottom right | Standard two-line variant only. |
| `samsung` | `✦ Contenuti generati dall'AI` | Bottom left | Calibrated for the Italian text variant. |
| `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. |
| `liblib` | `LiblibAI` | Bottom center | Includes a minimum image size gate. A 2026-09-05 Smart Image V2 provider original instead carries a smaller top-left `AI生成` pill and is not detected by this historical-wordmark engine. |
| `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. |
@@ -30,9 +30,16 @@ 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.
Synthetic canonical examples for every registered image and video mark live in
the [visible-mark gallery](../data/fixtures/visible/README.md), alongside
documented real provider originals where redistribution is cleared. The
canonical examples demonstrate detector geometry and house style; provider
originals exercise vendor raster fidelity.
Provider formats can change independently of the registered key. In particular,
the current LiblibAI sample in the gallery preserves a real new-format output
and its LiblibAI TC260 producer metadata, but is deliberately not listed as a
positive detector fixture.
### Visible video marks
@@ -44,12 +51,12 @@ detectors' canonical geometry and house style, not vendor raster fidelity.
| `seedance` | Boxed `AI` label | Fixed bottom-right corner | Requires an anchored recurring match; the full localized box is filled because a thinner synthetic shape mask leaves the real translucent rim behind. |
| `dola` | `Dola AI` text | Fixed bottom-right corner | Requires an anchored recurring match; ByteDance or BytePlus provenance can relax only an existing visual run. |
| `hailuo` | `MINIMAX \| hailuo AI` composite label | Fixed lower edge | Uses a synthetic waveform, text, separator, and ring silhouette; the complete recurring label box is filled. A TC260 label naming MiniMax as producer can relax only an existing stable run. |
| `kling` | Kling AI swirl, `KLING AI`, version, and optional `PRO` suffix | Fixed bottom-right edge | Combines a synthetic logo rescue with font variants, an edge gate, a white-label gate, and anchored temporal recurrence. |
| `kling` | Kling AI swirl, `KLING AI` or `KlingAI`, version, and optional `PRO` suffix | Fixed bottom-right edge | Combines a synthetic logo rescue with font and capitalization variants, an edge gate, a white-label gate, and anchored temporal recurrence. |
`video identify`, `video visible`, and `video all` share this registry and the
same temporal arbiter. It is separate from the image registry because selection
is made over a sequence rather than one raster. The default `auto` mode scans
all six entries in one decode pass and selects the first temporally stable
all seven entries in one decode pass and selects the first temporally stable
match in table order; an explicit mark restricts the scan to that row.
Accepted fills are motion-aligned across adjacent frames by default. The prior
fill contributes only where its warped mask covers the current removal mask and
@@ -181,10 +188,10 @@ inspection cannot independently verify the output. Microsoft's official
is the external oracle: it reports pixel `Watermark` and embedded `C2PA` results
separately; a control-positive, output-negative pair is the available per-file
verification path. The API needs Azure credentials; the page a human can check
without an account is <https://ai.azure.com/nextgen/validate>. Its verdict is
weaker than the API's: it collapses watermark and C2PA into one rendered result
and tops out at `Inconclusive` rather than a watermark-negative, so treat
`Inconclusive` on a processed file as "not confirmed", not as "detected still".
without an account is <https://ai.azure.com/nextgen/validate>. That page is the
public provider oracle used for the measured removal certification. Its clean
outcome is rendered as `Inconclusive`; the authenticated API exposes separate
watermark and C2PA fields but is not a different class of certification.
Meta Muse Image stamps every output with Content Seal, a proprietary invisible
pixel watermark, and ships no visible mark (the legacy `Imagined with AI`
+63 -3
View File
@@ -25,8 +25,16 @@ icon-only variants need separate calibrated silhouettes.
Some applications sign C2PA through an upstream model or infrastructure
provider. For AI claims, exact product mappings in `claim_generator` therefore
take precedence over issuer attribution. Supported mappings include Higgsfield
AI, Topaz Labs Image API, and TikTok Ad Creative Toolbox; an unknown claim
generator still falls back to the certificate issuer.
AI, Recraft, Topaz Labs Image API, and TikTok Ad Creative Toolbox; an unknown
claim generator still falls back to the certificate issuer.
**OpenRouter raster metadata survey (2026-09-02):** a paid Recraft V3 API
output carried a valid C2PA claim with `claim_generator` `recraft.ai`, a
`c2pa.created` action, and `trainedAlgorithmicMedia`; that exact generator now
attributes the platform to Recraft. Paid Krea 2 Medium Turbo and Sourceful
Riverflow V2 Fast API outputs carried no locally readable C2PA, XMP, EXIF, or
container metadata. Their absence is evidence about those exact files and API
routes, not a claim that every Krea or Riverflow export is metadata-free.
**ByteDance Volcano Engine (Volcengine)** — the cloud behind Doubao / Jimeng — signs its AI image output with a cert from `certificate_center@volcengine.com` + `trainedAlgorithmicMedia` (issuer `b"volcengine"` → "ByteDance (Volcano Engine)", platform "ByteDance Volcano Engine"); note this is the C2PA-signed surface, distinct from the XMP/PNG TC260 `AIGC` label Doubao also uses. ByteDance's **international brand (BytePlus / Seedream / Seededit)** signs the same content as **"Byteplus Pte. Ltd."**. The bare `volcengine` needle missed it, so BytePlus output was mis-attributed to "Adobe Firefly" through an incidental "Adobe XMP" toolkit string. Issuer `b"Byteplus"` maps directly to "BytePlus (ByteDance)". ByteDance's consumer app **Dreamina** (the international Jimeng brand) signs as **"Bytedance Pte. Ltd."** with a `Dreamina/x.y` claim generator but, unlike the Volcano Engine surface, ships **no `trainedAlgorithmicMedia`**. Issuer `b"Dreamina"` maps to "ByteDance Dreamina" with **`asserts_ai=True`**. Registering the broader **issuer** `b"Bytedance Pte"` was deliberately avoided because that same entity also signs non-AI CapCut edits; keying on the `Dreamina` generator token is precise.
- **EXIF/XMP/PNG-text generator tag (caught by `exif_generator`):** **Ideogram** writes EXIF `Make="Ideogram AI"` (collected 2026-05-24 — no C2PA, no SynthID, no imwatermark; the Make tag is the only signal). Additional verified generator stamps include **NovelAI** (`Software`, `Source`, and `Title` PNG text chunks), **Reve** (`Software` or XMP `CreatorTool` = `reve.com`), and **Aphrodite AI** (`Make` or `Software` = `Aphrodite AI`).
@@ -66,7 +74,7 @@ payloads. Removal remuxes either container through ffmpeg with stream copy.
encoder and the carrier remains decodable.
- **Invisible but NOT locally detectable (proprietary, API/oracle only — same wall as SynthID):** Amazon Titan Image Generator + Nova Canvas (Bedrock `DetectGeneratedContent` API), Kakao (new SynthID image adopter, May 2026), NVIDIA Cosmos (SynthID video). No local detector possible; treat like SynthID.
- **C2PA 2.4 "Durable Content Credentials" (April 2026; verified against the spec) raise the bar for metadata stripping.** 2.4 defines soft bindings (an invisible watermark or a content fingerprint) plus a server-side manifest repository and a new `c2pa.repository-receipt` assertion. Per the spec: "if a C2PA manifest is removed from an asset, but a copy of that manifest remains in a provenance store elsewhere, the manifest and asset may be matched using available soft bindings." So our local `metadata --remove` deletes the *embedded* manifest, but a fingerprint/watermark soft binding can still re-link the image to its manifest in a repository server-side. Stripping the file is becoming necessary-but-not-sufficient against durable provenance. (Our parsers target the stable embedded-manifest format documented in C2PA 2.1 §11; that format is unchanged in 2.4 -- the new pieces are repository/soft-binding infra, not the on-file box layout, so no parser change is implied.) Spec: https://spec.c2pa.org/specifications/specifications/2.4/specs/C2PA_Specification.html We READ the soft-binding `alg`, preserve the structured assertion's signed `value`, and generate the display/type registry from the official C2PA JSON with `scripts/sync_c2pa_soft_bindings.py`. The packaged snapshot keeps runtime inspection offline and pins the exact upstream revision. Registration is name-only support: Adobe TrustMark remains the one locally decoded scheme (`trustmark_detector`) unless another compatible decoder is independently verified. Fingerprint entries remain re-linkability signals and are not mislabeled as pixel watermarks.
- **Microsoft Paint and Photos InvisMark (reverse-engineered 2026-08-20):** Paint receives a per-generation GUID from remote prompt moderation, embeds it into locally generated pixels, and records the same value in `c2pa.soft-binding` under `com.microsoft.invismark.1`. The C2PA soft-binding registry independently identifies that algorithm as Microsoft Responsible AI InvisMark for image and video. Paint's 144-bit writer framing does not match the public repository's 100-bit pretrained checkpoint interface, so compatibility is not assumed. The parser reports the signed identifier; there is no validated local pixel decoder. Microsoft's external Content Provenance Detection API is the removal oracle because it reports `Watermark` separately from `C2PA`; a metadata-stripped, pixel-identical control must remain watermark-positive before an output-negative result is attributed to pixel removal. The no-account web page for the same check is <https://ai.azure.com/nextgen/validate> (its `Inconclusive` verdict is weaker than the API's separate watermark result). Sources: https://xusheng.dev/posts/reversing/mspaint_invisible_watermark/main/, https://github.com/c2pa-org/softbinding-algorithm-list/blob/main/softbinding-algorithm-list.json, and https://learn.microsoft.com/en-us/azure/ai-services/content-safety/how-to/how-to-provenance-detection
- **Microsoft Paint and Photos InvisMark (reverse-engineered 2026-08-20):** Paint receives a per-generation GUID from remote prompt moderation, embeds it into locally generated pixels, and records the same value in `c2pa.soft-binding` under `com.microsoft.invismark.1`. The C2PA soft-binding registry independently identifies that algorithm as Microsoft Responsible AI InvisMark for image and video. Paint's 144-bit writer framing does not match the public repository's 100-bit pretrained checkpoint interface, so compatibility is not assumed. The parser reports the signed identifier; there is no validated local pixel decoder. Microsoft's external Content Provenance Detection API is the removal oracle because it reports `Watermark` separately from `C2PA`; a metadata-stripped, pixel-identical control must remain watermark-positive before an output-negative result is attributed to pixel removal. The no-account web page for the same check is <https://ai.azure.com/nextgen/validate>; its clean outcome is rendered as `Inconclusive`, and that public provider oracle is the surface used for the measured removal certification. Sources: https://xusheng.dev/posts/reversing/mspaint_invisible_watermark/main/, https://github.com/c2pa-org/softbinding-algorithm-list/blob/main/softbinding-algorithm-list.json, and https://learn.microsoft.com/en-us/azure/ai-services/content-safety/how-to/how-to-provenance-detection
- **Built in the dated batch:** soft-binding vendor detection, IPTC Photo
Metadata AI-disclosure fields, C2PA detection and stripping for supported
ISOBMFF video, the optional Adobe TrustMark decoder, and temporally stabilized
@@ -131,6 +139,58 @@ instead uses Florence-2 to identify arbitrary watermarks before LaMa
inpainting. That is broader, but it carries a much heavier model and a less
auditable detection boundary than the provider-specific synthetic path here.
**Aggregator video export survey (2026-09-03).** Four paid, neutral-prompt
text-to-video jobs tested whether API access could reproduce the registered
consumer-facing marks. None did. OpenRouter `bytedance/seedance-2.0-mini`
(4 s, 480p, `watermark=true`, USD 0.0568316) returned a valid BytePlus C2PA
manifest but no visible boxed-`AI` mark; SHA-256
`19c1e323fbb085e5679374a8b646d65a5e902292ff65e33462828ba211d089f4`.
OpenRouter `minimax/hailuo-3-max` (5 s, 480p,
`aigc_watermark=true`, USD 0.25) returned a native TC260 AIGC label but no
visible `MINIMAX | hailuo AI` composite; SHA-256
`c850c79df6475e12a219ab6a4927b874c82a3ab783175f75acc6aa7513d2c303`.
WaveSpeedAI `openai/sora-2/text-to-video` (4 s, 720p) and
`kwaivgi/kling-v1.6-t2v-standard` (5 s, 720p) returned neither their registered
visible marks nor locally readable provenance; SHA-256 respectively
`85c29592fdfdac882a6536f6c27781c5d4f6a19b49681808eb744d0e33b63281`
and `392c4bd279ac94fa573f051b514b243c933637fdc7d0dae2df4af1f2312a85d0`.
Each result was checked through the explicit provider video path, not only the
auto arbiter, and returned no stable mark. The files are not fixtures: the two
clean exports assert no supported signal, while the OpenRouter outputs need
their model-specific redistribution terms settled before public inclusion.
For fixture collection, use the consumer surfaces that apply the marks rather
than repeating these aggregator routes. In particular, the provider option
names `watermark` and `aigc_watermark` proved to request machine-readable
provenance on these routes, not the visible overlays their names might imply.
**Direct Google Flow control (2026-09-03).** Two authenticated Veo 3.1 Fast
generations used the same neutral blue-mug scene at 720p for 20 Flow credits
each. With the account's `Visible watermarking` setting off, the downloaded
8-second MP4 had valid Google C2PA and SynthID but no visible mark (SHA-256
`f15454c2adda75fd79fcb8561e24088fa715f560e3c300c9f68e22fbc7275a20`).
That clean control exposed a false positive: the current auto arbiter selected
`hailuo` on all 192 frames and consequently misattributed the platform to
MiniMax, while the explicit `veo` path correctly found no mark. With the
setting on, the second 8-second MP4 carried low-contrast `Veo` text at the fixed
bottom-right position, and the shipped detector selected `veo` on all 192
frames (SHA-256
`e778194b545e34dafd2d5be58691031a070862890c41e6c527a652a114a7c7a4`).
The marked file is committed as the real provider original; the clean control
remains outside the repository until its Hailuo false positive is fixed and
locked by a negative regression test. Google documents both the user-controlled
visible-watermark setting and invisible SynthID on every Flow output.
**Direct Hailuo AI control (2026-09-03).** An authenticated MiniMax H3
generation used a neutral blue-mug scene, 2K output, and 60 top-up credits. The
download menu separately offered `Remove watermark` and `With Watermark`; the
latter produced a 2944 x 1248, 5-second MP4 carrying the complete lower-right
`MINIMAX | hailuo AI` composite and a TC260 AIGC label naming MiniMax as the
producer. The shipped detector selected `hailuo` on all 124 frames (SHA-256
`6152609e0339c443af79e244440e1b1050f3c615a50f76bfaf6146584e68142f`).
This provider original is committed as the real Hailuo regression fixture and
confirms that the direct consumer export differs from the metadata-only
OpenRouter result above.
**Learned detectors change the visible-mark precision/recall tradeoff; they do not establish a universal separator.** The visible-watermark-detection literature has moved to learned segmentation and object detection (WDNet WACV'21 arXiv:2012.07616; SLBR ACM MM'21, open code and weights; the PRCV'18 large-scale detector; Su et al. survey 2025). The cited arXiv:1705.08593 paper reports a method that significantly reduces false matches and eliminates them after rejecting a small fraction of matches on its electron-microscopy task; it does not prove an information-theoretic limit for watermark detection. Learned watermark detectors also rely on large, pattern-diverse labeled datasets built with synthetic composites (PRCV'18: 60k images / 80 watermark classes; CLWD: 60k / 160 marks), and transfer depends on the diversity represented in training. Inference can be cheap (WDNet reports roughly 8 ms at 256x256), while the labeled-data and evaluation pipeline remain the larger project cost. A learned detector is therefore a possible future operating point, not evidence that the current NCC gate is theoretically optimal.
**Visible-mark landscape beyond the registry.** Since Muse Image (2026-07) Meta's own generation output carries no visible mark, only the invisible proprietary Content Seal; the legacy visible "Imagined with AI" mark belongs to the pre-2026 Meta AI / "Imagine" pipeline and remains unregistered (string verified, position not). For third-party images Meta relies on C2PA / IPTC, not a visible mark. The Samsung detector covers only the measured Italian text wordmark; no separate icon-only variant has been verified. Visible overlays and embedded metadata can usually be removed without changing the underlying generation model, which is the tool's premise.
+11 -4
View File
@@ -38,11 +38,13 @@ MARKS = {
# stacked and ITALIC-SLANTED. A rare one-line variant exists, but the stacked block
# is dominant.
"yuanbao_alpha.png": "元宝\nAI生成",
# Kling (可灵) stamps a thin light-gray one-line "可灵AI 3.0" bottom-right (an
# "Omni" suffix variant and a latin "KlingAI 3.0" variant also exist; the CJK
# run without the suffix is the common core). The leading spiral logo is NOT
# rendered (logos vary; the text run discriminates).
# Older Kling (可灵) exports stamp a thin light-gray one-line "可灵AI 3.0"
# bottom-right. The leading spiral logo is NOT rendered (logos vary; the text
# run discriminates).
"kling_alpha.png": "可灵AI 3.0",
# Current IMAGE 3.0 exports use the Latin ``KlingAI 3.0`` run. Keep it as a
# second installed silhouette rather than replacing the measured CJK variant.
"kling_latin_alpha.png": "KlingAI 3.0",
# The "cat-logo" candidate stamps an outline cat-head plus bold "AI生成",
# bottom-right. It remains unregistered pending sufficient calibration coverage.
"catlogo_alpha.png": "cat logo + AI生成",
@@ -96,8 +98,13 @@ MARK_OPTS: dict[str, dict[str, Any]] = {
"qingyan_alpha.png": {"font": "/System/Library/Fonts/Hiragino Sans GB.ttc", "font_index": 2},
# LibLibAI uses an Arial-class grotesque.
"liblib_alpha.png": {"font": "/System/Library/Fonts/Supplemental/Arial.ttf"},
"kling_latin_alpha.png": {"font": "/System/Library/Fonts/HelveticaNeue.ttc", "font_index": 1},
}
# ``kling_latin`` is a variant of the registered ``kling`` row, not a second
# registry key, so it cannot be derived from ``mark_keys()`` above.
_REGISTERED.add("kling_latin_alpha.png")
def _fit_font(
font_path: str,
@@ -123,6 +123,7 @@ C2PA_CLAIM_GENERATOR_PLATFORMS: tuple[tuple[str, str], ...] = (
("firefly", "Adobe Firefly"),
("dreamina", "ByteDance Dreamina"),
("higgsfield ai", "Higgsfield AI"),
("recraft.ai", "Recraft"),
("topaz labs image api", "Topaz Labs"),
("tiktok ad creative toolbox", "TikTok Ad Creative Toolbox"),
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

+72 -30
View File
@@ -1,19 +1,21 @@
"""Kling AI (可灵AI, Kuaishou) visible watermark detector/localizer.
Kling AI stamps its generations with a thin, light-gray "可灵AI 3.0" text strip in the
bottom-right corner, preceded by the vendor's spiral logo (not part of the detection
silhouette -- logos vary between releases, the text run is what discriminates).
Known variants: an "Omni" suffix release, a latin "KlingAI 3.0" release, and a
version-less "可灵AI" -- the silhouette targets the common "可灵AI 3.0" core, so the
suffix variants are only caught when the core run is bold enough (measured below).
Kling AI stamps its generations with a thin, light-gray text strip in the
bottom-right corner, preceded by the vendor's spiral logo (not part of the
detection silhouette -- logos vary between releases, the text run is what
discriminates). Separate silhouettes cover the older "可灵AI 3.0" release and
the current IMAGE 3.0 "KlingAI 3.0" release. An "Omni" suffix and a version-less
"可灵AI" remain outside the calibrated pair.
Detection matches the bundled glyph silhouette against the corner. Removal unions
the pixel-derived footprint with that silhouette aligned to the winning match box,
the pixel-derived footprint with the known silhouettes aligned to the winning match box,
then sends the hybrid mask through the shared **localize -> fill** path in
``region_eraser``; it is not reverse-alpha. The asset
(``assets/kling_alpha.png``) is a font-rendered synthetic silhouette from
``scripts/render_vendor_silhouettes.py``, never cut from an upload. The detector also
feeds ``identify`` as the medium-confidence ``visible_kling`` signal via the registry.
``region_eraser``; it is not reverse-alpha. This module supplies two tuned
:class:`TextMarkConfig` instances (``assets/kling_alpha.png`` and
``assets/kling_latin_alpha.png`` -- font-rendered synthetic silhouettes from
``scripts/render_vendor_silhouettes.py``, never cut from an upload). The detector
also feeds ``identify`` as the medium-confidence ``visible_kling`` signal via the
registry.
EVERY tuned number below was measured on the vendor cohort (30 TC260 carriers whose
producer USCC 91110108335469089C names the entity, 2026-07-21; harness
@@ -25,13 +27,14 @@ producer USCC 91110108335469089C names the entity, 2026-07-21; harness
box fractions below are fitted from the measured absolute mark rects.
* ``alpha_height_frac`` comes from the silhouette aspect (0.239) at the fitted
width, matching the aspect the fit converged on (0.25).
* Gate 0.35, one step above the clean arm's max: on the cohort-vs-clean run
* The CJK gate is 0.35, one step above the clean arm's max: on the cohort-vs-clean run
(cohort-contamination-guarded, 286 hand-labeled clean frames) the clean arm
scored p99 0.304 / max 0.320, and every cohort frame >= 0.35 carries a visible
可灵AI 3.0 mark (9 of ~19 eyeballed visible marks fire = ~47% recall of visible
marks; the misses are the faint "Omni"-suffix release, the latin "KlingAI"
release and the version-less "可灵AI", which score 0.17-0.25 and cannot be
reached without engulfing the clean arm).
marks; the misses are the faint "Omni"-suffix release and the version-less
"可灵AI", which score 0.17-0.25 and cannot be reached without engulfing the
clean arm). The separately measured Latin IMAGE 3.0 variant is documented
beside ``_LATIN_CONFIG`` below.
* STRICT ONLY (``provenance_ncc_factor`` 1.0): the sub-gate band holds real Kling
variants AND the clean arm's top (clean p90 0.220 vs variant marks at 0.17-0.25
-- they overlap), so a provenance-relaxed arm cannot separate them. No
@@ -49,6 +52,7 @@ producer USCC 91110108335469089C names the entity, 2026-07-21; harness
from __future__ import annotations
from dataclasses import replace
from typing import TYPE_CHECKING, Any
import cv2
@@ -121,6 +125,23 @@ _CONFIG = TextMarkConfig(
provenance_ncc_factor=1.0,
)
# Direct Kling IMAGE 3.0 export, 2026-09-04: the current Latin run measures
# 0.095 x 0.028 of the short side inside the same bottom-right locate box. The
# narrow ladder covers small rasterization shifts without admitting any of the
# 94 available neighboring real/synthetic image controls (max 0.349, versus
# 0.429 on the provider original). A deliberately adversarial solid corner blob
# reaches 0.379, so the 0.40 gate stays above both measured negative arms. Keep
# this a separate silhouette and gate:
# replacing the CJK template would discard the older measured release.
_LATIN_CONFIG = replace(
_CONFIG,
asset_name="kling_latin_alpha.png",
alpha_width_frac=0.095,
alpha_height_frac=0.028,
ladder=(0.9, 1.0, 1.1),
detect_ncc_threshold=0.40,
)
def _alpha_template() -> NDArray[Any] | None:
"""The bundled Kling AI alpha template (float [0,1]), or None."""
@@ -133,10 +154,29 @@ def _glyph_silhouette() -> NDArray[Any] | None:
class KlingEngine(TextMarkEngine):
"""Detect/localize the visible Kling AI "可灵AI 3.0" watermark (locate -> mask; mask feeds the fill)."""
"""Detect/localize Kling AI's CJK and Latin 3.0 marks (locate -> mask -> fill)."""
def __init__(self) -> None:
super().__init__(_CONFIG)
self._latin = TextMarkEngine(_LATIN_CONFIG)
@staticmethod
def _best(*detections: TextMarkDetection) -> TextMarkDetection:
"""Prefer an accepted variant, then retain the strongest rejected score."""
return max(detections, key=lambda detection: (detection.detected, detection.confidence))
def detect(self, image: NDArray[Any], *, provenance: bool = False) -> TextMarkDetection:
"""Return the strongest CJK or Latin Kling wordmark verdict."""
return self._best(
super().detect(image, provenance=provenance),
self._latin.detect(image, provenance=provenance),
)
def detect_both(self, image: NDArray[Any] | None) -> tuple[TextMarkDetection, TextMarkDetection]:
"""Scan each immutable variant once and combine strict/relaxed verdicts."""
cjk_strict, cjk_relaxed = super().detect_both(image)
latin_strict, latin_relaxed = self._latin.detect_both(image)
return self._best(cjk_strict, latin_strict), self._best(cjk_relaxed, latin_relaxed)
def footprint_mask(
self,
@@ -146,7 +186,7 @@ class KlingEngine(TextMarkEngine):
dilate: int | None = None,
detection: TextMarkDetection | None = None,
) -> NDArray[Any] | None:
"""Add the detector-aligned Kling core to the pixel-derived footprint.
"""Add detector-aligned Kling cores to the pixel-derived footprint.
The legacy mask remains in the union because it can cover variant-specific
strokes outside the synthetic core. The aligned alpha closes holes caused by
@@ -159,17 +199,19 @@ class KlingEngine(TextMarkEngine):
det = detection if detection is not None else self.detect(image)
legacy = super().footprint_mask(image, force=False, dilate=dilate, detection=det)
alpha = _alpha_template()
if alpha is None:
return legacy
radius = _FOOTPRINT_DILATE if dilate is None else max(0, dilate)
core = self._aligned_alpha_mask(
image,
det,
alpha,
alpha_floor=_FOOTPRINT_ALPHA_FLOOR,
dilate=radius,
)
if core is None:
return legacy
return core if legacy is None else cv2.bitwise_or(legacy, core)
result = legacy
for config in (_CONFIG, _LATIN_CONFIG):
alpha = _text_mark_engine.load_alpha_template(config.asset_name)
if alpha is None:
continue
core = self._aligned_alpha_mask(
image,
det,
alpha,
alpha_floor=_FOOTPRINT_ALPHA_FLOOR,
dilate=radius,
)
if core is not None:
result = core if result is None else cv2.bitwise_or(result, core)
return result
+6 -2
View File
@@ -820,6 +820,7 @@ def synthid_source(image_path: Path, *, c2pa_info: dict[str, Any] | None = None)
from remove_ai_watermarks._internal.c2pa import (
c2pa_info_has_invalid_credential,
extract_c2pa_info,
soft_binding_registry_entries_in,
synthid_evidence_vendors_in,
)
@@ -832,6 +833,11 @@ def synthid_source(image_path: Path, *, c2pa_info: dict[str, Any] | None = None)
vendors = c2pa.get("synthid_vendors")
if vendors:
return ", ".join(vendors)
soft_binding_algorithm = c2pa.get("soft_binding_algorithm")
if soft_binding_algorithm:
entries = soft_binding_registry_entries_in(str(soft_binding_algorithm).encode())
if any(entry.kind == "watermark" for entry in entries) or not entries:
return None
# Non-PNG containers (JPEG APP11, WebP, AVIF/HEIF/JXL uuid box) keep the
# C2PA manifest where the PNG parser can't reach it. Binary-scan for the
@@ -842,8 +848,6 @@ def synthid_source(image_path: Path, *, c2pa_info: dict[str, Any] | None = None)
ai_source = b"trainedAlgorithmicMedia" in data or b"TrainedAlgorithmicMedia" in data
if not (has_c2pa and ai_source):
return None
from remove_ai_watermarks._internal.c2pa import soft_binding_registry_entries_in
# A declared watermark blocks a second, generic watermark attribution. A
# content fingerprint does not suppress independent SynthID evidence.
if any(entry.kind == "watermark" for entry in soft_binding_registry_entries_in(data)):
+25 -24
View File
@@ -271,31 +271,32 @@ def _hailuo_template() -> NDArray[Any]:
@lru_cache(maxsize=1)
def _kling_templates() -> tuple[NDArray[Any], ...]:
"""Return synthetic font variants for the Kling wordmark core."""
canvas = Image.new("L", (430, 104), 0)
draw = ImageDraw.Draw(canvas)
draw.text(
(2, 12),
"KLING AI",
font=_scalable_default_font(64),
fill=255,
stroke_width=1,
stroke_fill=255,
)
templates = [_crop_nonzero(np.asarray(canvas, dtype=np.uint8))]
for font in (cv2.FONT_HERSHEY_SIMPLEX, cv2.FONT_HERSHEY_DUPLEX):
cv_template = np.zeros((100, 500), dtype=np.uint8)
cv2.putText(
cv_template,
"KLING AI",
(2, 72),
font,
2.2,
255,
3,
cv2.LINE_AA,
"""Return synthetic font and capitalization variants for the Kling wordmark core."""
templates: list[NDArray[Any]] = []
for text in ("KLING AI", "KlingAI"):
canvas = Image.new("L", (430, 104), 0)
ImageDraw.Draw(canvas).text(
(2, 12),
text,
font=_scalable_default_font(64),
fill=255,
stroke_width=1,
stroke_fill=255,
)
templates.append(_crop_nonzero(cv_template))
templates.append(_crop_nonzero(np.asarray(canvas, dtype=np.uint8)))
for font in (cv2.FONT_HERSHEY_SIMPLEX, cv2.FONT_HERSHEY_DUPLEX):
cv_template = np.zeros((100, 500), dtype=np.uint8)
cv2.putText(
cv_template,
text,
(2, 72),
font,
2.2,
255,
3,
cv2.LINE_AA,
)
templates.append(_crop_nonzero(cv_template))
return tuple(templates)
@@ -22,7 +22,7 @@ Entries:
- ``doubao`` -- ByteDance Doubao "豆包AI生成" text strip, bottom-right.
- ``jimeng`` -- ByteDance Jimeng / Dreamina "★ 即梦AI" wordmark, bottom-right.
- ``qwen`` -- Alibaba Cloud Qwen "千问AI生成" text strip, bottom-right.
- ``kling`` -- Kuaishou Kling AI "可灵AI 3.0" text strip, bottom-right.
- ``kling`` -- Kuaishou Kling AI "可灵AI 3.0" / "KlingAI 3.0" strip, bottom-right.
- ``yuanbao`` -- Tencent Yuanbao "元宝 / AI生成" two-line mark, bottom-right.
- ``samsung`` -- Samsung Galaxy AI "Contenuti generati dall'AI" strip, bottom-left.
- ``jimeng_pill`` -- Jimeng-basic "AI生成" pill, top-left (capture-less).
@@ -726,9 +726,9 @@ _REGISTRY: tuple[KnownMark, ...] = (
),
_text_mark(
"kling",
"Kling AI 可灵AI 3.0 text",
"Kling AI 可灵AI / KlingAI 3.0 text",
"bottom-right",
platform="Kuaishou Kling AI (visible 可灵AI 3.0 mark detected)",
platform="Kuaishou Kling AI (visible 可灵AI / KlingAI 3.0 mark detected)",
tc260_producer_codes=("91110108335469089C",),
),
_text_mark(
+111
View File
@@ -7,6 +7,7 @@ against the real committed C2PA / IPTC fixtures in data/fixtures/provenance/.
from __future__ import annotations
import base64
import hashlib
import json
import subprocess
import sys
@@ -463,6 +464,7 @@ class TestIdentifyNonPng:
("claim_generator", "platform"),
[
("Higgsfield AI", "Higgsfield AI"),
("recraft.ai", "Recraft"),
("Topaz Labs Image API", "Topaz Labs"),
("TIKTOK AD Creative Toolbox", "TikTok Ad Creative Toolbox"),
],
@@ -491,6 +493,78 @@ class TestIdentifyNonPng:
assert report.platform == platform
assert claim_generator in next(s.detail for s in report.signals if s.name == "c2pa")
def test_real_recraft_c2pa_fixture(self):
report = identify(SAMPLES_DIR / "recraft-v3.webp", check_visible=False)
assert report.is_ai_generated is True
assert report.confidence == "high"
assert report.platform == "Recraft"
assert any(signal.name == "c2pa" and "recraft.ai" in signal.detail for signal in report.signals)
def test_real_krea_api_fixture_has_no_local_provenance(self):
report = identify(SAMPLES_DIR / "krea-2-medium-turbo.png", check_visible=False)
assert report.is_ai_generated is None
assert report.platform is None
assert report.signals == []
def test_real_direct_kling_fixture_has_tc260_and_current_visible_mark(self):
path = SAMPLES_DIR.parent / "visible" / "kling" / "provider-original-direct.png"
report = identify(path, check_visible=True)
assert report.is_ai_generated is True
assert any(
signal.name == "aigc" and "001191110108335469089C10100" in signal.detail for signal in report.signals
)
assert any(signal.name == "visible_kling" for signal in report.signals)
@pytest.mark.parametrize("filename", ["qwen-image.png", "seedream-v4.jpg"])
def test_real_wavespeed_api_fixture_has_no_local_provenance(self, filename):
report = identify(SAMPLES_DIR / filename, check_visible=False)
assert report.is_ai_generated is None
assert report.platform is None
assert report.signals == []
def test_real_wavespeed_hunyuan_fixture_has_fal_c2pa(self):
path = SAMPLES_DIR / "hunyuan-image-3.png"
assert hashlib.sha256(path.read_bytes()).hexdigest() == (
"6ae2ac073ac79b02b0934c905364f200420c2ff60fea232bd9e0a5fca785cc09"
)
report = identify(path, check_visible=False, check_invisible=False)
assert report.is_ai_generated is True
assert report.platform == "fal.ai"
assert report.confidence == "high"
assert any(signal.name == "c2pa" and "fal-ai/hunyuan-image" in signal.detail for signal in report.signals)
def test_real_wavespeed_kling_fixture_has_tc260_metadata(self):
path = SAMPLES_DIR / "kling-image-v3.png"
assert hashlib.sha256(path.read_bytes()).hexdigest() == (
"501ab865b47ba59b64bc0f68118300744b0d5c7be2fa4e2ecbb3a78de5d8a82b"
)
report = identify(path, check_visible=False, check_invisible=False)
assert report.is_ai_generated is True
assert report.confidence == "high"
aigc = next(signal for signal in report.signals if signal.name == "aigc")
assert "001191110108335469089C10100" in aigc.detail
def test_real_qwen_create_fixture_has_tc260_metadata(self):
path = SAMPLES_DIR / "qwen-create-qwen-image-2.png"
assert hashlib.sha256(path.read_bytes()).hexdigest() == (
"60c39ed8aedb081193c62289be549785e74d31fc8d071961003fabd74712429d"
)
report = identify(path, check_visible=False, check_invisible=False)
assert report.is_ai_generated is True
assert report.confidence == "high"
aigc = next(signal for signal in report.signals if signal.name == "aigc")
assert "001191440101MA9Y9T4H7A00000" in aigc.detail
def test_dreamina_attributed_without_source_type(self, tmp_path: Path):
# Dreamina (ByteDance's international Jimeng brand) signs C2PA as
# "Bytedance Pte. Ltd." with a "Dreamina/x.y" claim generator and NO
@@ -1333,6 +1407,43 @@ class TestIdentifySoftBinding:
assert watermark_id in invismark.detail
assert any("may remain in the pixels" in caveat for caveat in report.caveats)
@pytest.mark.parametrize(
("filename", "watermark_id", "sha256"),
[
(
"microsoft-invismark.png",
"19b070a1-23f0-40eb-826c-cade306337eb",
"be27ac7c351e7f381dc69e140ca3611ecd9db81e40926c68050ceffdd0ce6c04",
),
(
"microsoft-paint-invismark.png",
"a4ed421e-7c37-4409-998e-f8788c564572",
"74eda22d210d534fcb5b75bedf8acd3bd110b48a95f4b9e546cd34f546effc05",
),
(
"microsoft-bing-image-creator.jpg",
"aec9a34e-665f-4549-9c02-9d29337a5de6",
"55cad5355b6eeeb25f84ea074f85c0ea55b82d63c27f43e3429bb75f28734ac7",
),
(
"../visible/microsoft/provider-original.png",
"975290d4-9c2f-4359-ad5a-92c5b5b850a4",
"0e76fb8aa0540b18fee4d63ac4b5fd63c7eee310474e896d21d27e668403afe7",
),
],
)
def test_real_microsoft_invismark_fixture(self, filename, watermark_id, sha256):
path = SAMPLES_DIR / filename
assert hashlib.sha256(path.read_bytes()).hexdigest() == sha256
report = identify(path, check_visible=False)
assert report.platform == "Microsoft (Copilot / Designer)"
soft_binding = next(signal for signal in report.signals if signal.name == "soft_binding")
assert "com.microsoft.invismark.1" in soft_binding.detail
assert watermark_id in soft_binding.detail
assert any(signal.name == "invismark" for signal in report.signals)
class TestIdentifyIptcAi:
"""IPTC 2025.1 AISystemUsed drives an AI verdict + platform attribution."""
+5 -4
View File
@@ -6,15 +6,12 @@ both identify them and match the upstream decoder bit for bit.
from __future__ import annotations
from typing import TYPE_CHECKING
from pathlib import Path
import cv2
import numpy as np
import pytest
if TYPE_CHECKING:
from pathlib import Path
from remove_ai_watermarks.invisible_watermark import (
_BITS_48,
_SD1_STRING,
@@ -101,6 +98,10 @@ class TestRaveledHaarPass:
class TestDetect:
def test_committed_sdxl_carrier(self):
path = Path(__file__).parents[1] / "data/fixtures/provenance/sdxl-dwt-dct.png"
assert detect_invisible_watermark(path) == "Stable Diffusion XL"
def test_in_tree_decoder_matches_upstream(self, tmp_path: Path):
from imwatermark import WatermarkDecoder
+13 -1
View File
@@ -1,4 +1,4 @@
"""Tests for the Kling (可灵AI 3.0) visible-watermark engine (localize -> fill).
"""Tests for the Kling CJK/Latin 3.0 visible-watermark engine (localize -> fill).
Every tuned constant in ``kling_engine`` was measured on the 30-frame vendor
cohort (2026-07-21, ``scripts/vendor_mark_calibrate.py``); these tests pin the
@@ -18,6 +18,7 @@ from remove_ai_watermarks import watermark_registry as registry
from remove_ai_watermarks.kling_engine import (
_ALPHA_HEIGHT_FRAC,
_ALPHA_WIDTH_FRAC,
_LATIN_CONFIG,
KlingEngine,
_alpha_template,
_glyph_silhouette,
@@ -78,6 +79,11 @@ class TestConfig:
# gate must sit above that with margin.
assert KlingEngine().config.detect_ncc_threshold > 0.32
def test_latin_variant_keeps_its_measured_narrow_ladder(self):
assert _LATIN_CONFIG.asset_name == "kling_latin_alpha.png"
assert _LATIN_CONFIG.ladder == (0.9, 1.0, 1.1)
assert _LATIN_CONFIG.detect_ncc_threshold == 0.40
def test_registry_row(self):
mark = registry.get_mark("kling")
assert mark.location == "bottom-right"
@@ -126,6 +132,12 @@ class TestDetect:
assert eng.detect(wm).detected
assert not eng.detect(cv2.resize(wm, (150, 112))).detected
def test_metadata_only_kling_api_export_is_not_a_visible_mark(self):
path = Path(__file__).resolve().parents[1] / "data" / "fixtures" / "provenance" / "kling-image-v3.png"
image = cv2.imread(str(path))
assert image is not None
assert not KlingEngine().detect(image).detected
class TestFootprintMaskAndRemoval:
def test_removes_composed_mark(self):
+5
View File
@@ -560,6 +560,11 @@ class TestSynthIDSource:
assert synthid_source(SAMPLES_DIR / "firefly-1.png") is None
assert "synthid_watermark" not in get_ai_metadata(SAMPLES_DIR / "firefly-1.png")
def test_microsoft_invismark_is_not_openai_synthid(self):
path = SAMPLES_DIR / "microsoft-paint-invismark.png"
assert synthid_source(path) is None
assert "synthid_watermark" not in get_ai_metadata(path)
def test_non_ai_image_is_not_synthid_source(self, clean_photo: Path):
assert synthid_source(clean_photo) is None
+5 -2
View File
@@ -1,5 +1,8 @@
"""Jimeng-basic 'AI生成' pill: capture-less mark (detect via synthetic silhouette
edge-NCC, remove via inpaint). No model download -- cv2 fallback / pure logic only."""
"""Jimeng-basic 'AI生成' pill: synthetic-silhouette edge-NCC and inpainting.
No model download -- cv2 fallback / pure logic only. The separate visible-example
suite exercises the detector against a published real-world carrier.
"""
from __future__ import annotations
+36 -1
View File
@@ -6,7 +6,9 @@ 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).
committed silhouettes onto a generated base). Documented provider originals and
provider frames are separate regression inputs and remain byte-for-byte copies of
their sources.
"""
from __future__ import annotations
@@ -18,11 +20,26 @@ import pytest
from remove_ai_watermarks import watermark_registry as wr
from remove_ai_watermarks.image_io import imread
from remove_ai_watermarks.video import VIDEO_VISIBLE_MARKS, identify_video
from remove_ai_watermarks.video_visible import detect_sora_frame
_ROOT = Path(__file__).resolve().parents[1]
_GALLERY = _ROOT / "data" / "fixtures" / "visible"
_IMAGE_KEYS = [m.key for m in wr.known_marks()]
_PROVIDER_ORIGINALS = {
"baidu": "provider-original.jpeg",
"jimeng_pill": "provider-published-example.jpg",
"kling": "provider-original-direct.png",
"microsoft": "provider-original.png",
"qwen": "provider-original.png",
"yuanbao": "provider-original.png",
}
_VIDEO_PROVIDER_ORIGINALS = {
"hailuo": "provider-original.mp4",
"kling": "provider-original.mp4",
"sora": "provider-original.mp4",
"veo": "provider-original.mp4",
}
class TestGallery:
@@ -42,6 +59,13 @@ class TestGallery:
extra = sorted(p.name for p in _GALLERY.iterdir() if p.name not in known)
assert extra == [], f"gallery holds unregistered examples: {extra}; remove or register them"
@pytest.mark.parametrize("key", sorted(_PROVIDER_ORIGINALS))
def test_engine_detects_provider_original(self, key: str) -> None:
img = imread(str(_GALLERY / key / _PROVIDER_ORIGINALS[key]))
assert img is not None, key
det = wr.get_mark(key).detect(img, provenance=False)
assert det.detected, f"{key}: confidence {det.confidence:.3f} on provider original"
class TestVideoGallery:
def test_every_registered_video_mark_has_an_example(self) -> None:
@@ -55,3 +79,14 @@ class TestVideoGallery:
for key in VIDEO_VISIBLE_MARKS:
rep = identify_video(_GALLERY / key / "example.mp4", check_visible=True)
assert rep.visible_mark == key, f"{key}: selection returned {rep.visible_mark!r}"
@pytest.mark.parametrize("key", sorted(_VIDEO_PROVIDER_ORIGINALS))
def test_selection_accepts_provider_original(self, key: str) -> None:
rep = identify_video(_GALLERY / key / _VIDEO_PROVIDER_ORIGINALS[key], check_visible=True)
assert rep.visible_mark == key, f"{key}: selection returned {rep.visible_mark!r}"
def test_sora_frame_localizer_accepts_provider_frame(self) -> None:
img = imread(str(_GALLERY / "sora" / "provider-frame.jpg"))
assert img is not None
det = detect_sora_frame(img)
assert det.confidence >= 0.60, f"sora: confidence {det.confidence:.3f} on provider frame"