Ship the measured Meta Content Seal cohort with auto routing and --vendor override

Full Meta Muse Image support in the invisible-removal path:

- QWEN_ZIMAGE_META_STRENGTH = 0.1: derived by the standard
  worst-boundary-plus-cross-source-spread method over five oracle-bracketed
  generations (data/contentseal/manifest.csv)
- Auto mode: vendor_for_strength routes a file whose only provenance is the
  standalone AI IPTC trainedAlgorithmicMedia tag onto the meta cohort; C2PA
  issuers win first, so Google/OpenAI/Microsoft routing is unchanged. Muse
  WebP outputs place the XMP in a tail chunk, so the scan uses the shared
  chunk-aware metadata.scan_head rather than a plain head read
- Explicit override: --vendor on invisible/all/batch and
  InvisibleOptions.vendor name the cohort on stripped files; naming a cohort
  asserts the watermark is present, so the no-signal gate treats it like
  --force at both the CLI and API seams
- sdxl-zimage has no measured Meta rung: an explicit meta vendor falls to
  the conservative unknown 0.25 rather than inventing one
- identify emits a Content Seal caveat pointing at the removal path
- The legacy visible 'Imagined with AI' mark stays unregistered: a dedicated
  sample hunt (newsroom mockups, community posts, press screenshots, dead
  imagine.meta.com, broken Wayback captures) found no pixel-verifiable
  capture, and the registry rule forbids encoding a corner without one.
  erase --region remains its removal path; outcome recorded in the landscape

Co-Authored-By: Claude Fable 4.5 <noreply@anthropic.com>
This commit is contained in:
Victor Kuznetsov
2026-08-26 22:59:23 -07:00
co-authored by Claude Fable 4.5
parent a1811b6221
commit ab528ec0e8
12 changed files with 260 additions and 33 deletions
+33
View File
@@ -426,6 +426,39 @@ class TestInvisibleCommand:
assert result.exit_code == 0, result.output
mock_engine.remove_watermark.assert_called_once()
def test_invisible_explicit_vendor_implies_force_and_sets_cohort(self, runner, sample_png, tmp_path):
"""--vendor meta names a cohort the file cannot prove (Content Seal carries
no C2PA), so it must both bypass the no-signal skip and arrive at the engine
as the vendor, where it resolves to the measured Meta floor (not the
area-curve value the same size would otherwise get)."""
mock_cls, mock_engine = _mock_invisible_engine()
output = tmp_path / "clean.png"
with (
patch("remove_ai_watermarks.invisible_engine.is_available", return_value=True),
patch("remove_ai_watermarks.cli.InvisibleEngine", mock_cls, create=True),
patch("remove_ai_watermarks.invisible_engine.InvisibleEngine", mock_cls),
):
result = runner.invoke(main, ["invisible", str(sample_png), "-o", str(output), "--vendor", "meta"])
assert result.exit_code == 0, result.output
kwargs = mock_engine.remove_watermark.call_args.kwargs
assert kwargs["vendor"] == "meta"
assert "0.1" in result.output # the resolved Meta floor, printed
assert "0.094" not in result.output # not the sample's area-curve value (200x200 -> ~0.0944)
def test_invisible_vendor_auto_keeps_detection_semantics(self, runner, sample_png, tmp_path):
"""--vendor auto is the default spelled out: detection still runs and a
no-signal file still skips."""
mock_cls, mock_engine = _mock_invisible_engine()
output = tmp_path / "clean.png"
with (
patch("remove_ai_watermarks.invisible_engine.is_available", return_value=True),
patch("remove_ai_watermarks.cli.InvisibleEngine", mock_cls, create=True),
patch("remove_ai_watermarks.invisible_engine.InvisibleEngine", mock_cls),
):
result = runner.invoke(main, ["invisible", str(sample_png), "-o", str(output), "--vendor", "auto"])
assert result.exit_code == 2, result.output
mock_engine.remove_watermark.assert_not_called()
def test_invisible_runs_without_force_when_signal_present(self, runner, tmp_path):
"""An image carrying an AI metadata signal IS a scrub target, so the run
proceeds with no --force needed."""
+51 -1
View File
@@ -6,6 +6,7 @@ answer and a clean refusal rather than a fallback ladder.
from __future__ import annotations
import struct
from pathlib import Path
from unittest.mock import MagicMock, patch
@@ -15,6 +16,7 @@ from remove_ai_watermarks._internal.utils import get_image_format, is_supported_
from remove_ai_watermarks._internal.watermark_profiles import (
PROFILE_CHOICES,
QWEN_ZIMAGE_GOOGLE_STRENGTH,
QWEN_ZIMAGE_META_STRENGTH,
QWEN_ZIMAGE_OPENAI_STRENGTH,
REMOVAL_MODULES,
SDXL_ZIMAGE_GEMINI_STRENGTH,
@@ -198,6 +200,30 @@ class TestNoReembeddedWatermark:
assert "add_watermarker" not in calls["controlnet"]
# Minimal WebP stub whose XMP chunk carries the IPTC trainedAlgorithmicMedia
# tag exactly as Muse outputs place it (built inline so the test has no binary
# fixture dependency).
_XMP_PAYLOAD = (
b'<x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax#">'
b'<rdf:Description rdf:about="" xmlns:iptcExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" '
b'iptcExt:DigitalSourceType="http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia"/>'
b"</rdf:RDF></x:xmpmeta>"
)
def _webp_stub(xmp: bytes | None) -> bytes:
def chunk(cid: bytes, data: bytes) -> bytes:
return cid + struct.pack("<I", len(data)) + data + (b"\x00" if len(data) % 2 else b"")
body = b"WEBP" + chunk(b"VP8L", b"\x00" * 20)
if xmp is not None:
body += chunk(b"XMP ", xmp)
return b"RIFF" + struct.pack("<I", len(body)) + body
_MUSE_WEBP_WITH_IPTC_TAG = _webp_stub(_XMP_PAYLOAD)
class TestResolveStrength:
"""resolve_strength owns the qwen-zimage and sdxl-zimage policies."""
@@ -222,12 +248,36 @@ class TestResolveStrength:
with pytest.raises(ValueError, match="size is required"):
resolve_strength(None, "openai", "qwen-zimage")
@pytest.mark.parametrize(("vendor", "expected"), [("microsoft", 0.15), ("openai", 0.07675)])
@pytest.mark.parametrize(("vendor", "expected"), [("microsoft", 0.15), ("openai", 0.07675), ("meta", 0.1)])
def test_qwen_zimage_measured_vendors_use_flat_cross_source_margins(self, vendor, expected):
"""Measured providers must not fall below their operating points on small files."""
assert resolve_strength(None, vendor, "qwen-zimage", size=(600, 500)) == pytest.approx(expected)
assert resolve_strength(None, vendor, "qwen-zimage", size=(4000, 3000)) == pytest.approx(expected)
def test_meta_floor_is_size_independent_and_sdxl_falls_to_unknown(self):
"""The Meta floor bypasses the area curve at every size (Content Seal removal
was bracketed on 2.56 MP generations and derived as a flat cross-source
margin, like the other measured cohorts), while sdxl-zimage has no measured
Meta rung and must fall to the conservative unknown value, not invent one."""
for size in ((600, 500), (1600, 1600), (1920, 1280), (4000, 3000)):
assert resolve_strength(None, "meta", "qwen-zimage", size=size) == pytest.approx(QWEN_ZIMAGE_META_STRENGTH)
assert resolve_strength(None, "meta", "sdxl-zimage") == SDXL_ZIMAGE_UNKNOWN_STRENGTH
def test_vendor_for_strength_routes_standalone_iptc_to_meta(self, tmp_path):
"""Auto mode: a file whose only provenance is the AI IPTC tag routes to the
meta cohort (Muse carries no C2PA; the tag is its fallback companion), while
a file without the tag stays on the resolution curve and a C2PA issuer still
wins over the tag."""
from remove_ai_watermarks._internal.watermark_profiles import vendor_for_strength
tagged = tmp_path / "tagged.webp"
tagged.write_bytes(_MUSE_WEBP_WITH_IPTC_TAG)
assert vendor_for_strength(tagged) == "meta"
stripped = tmp_path / "stripped.webp"
stripped.write_bytes(b"RIFF\x24\x00\x00\x00WEBPVP8 \x18\x00\x00\x00" + b"\x00" * 16)
assert vendor_for_strength(stripped) is None
def test_sdxl_zimage_uses_its_flat_vendor_ladder(self):
assert SDXL_ZIMAGE_OPENAI_STRENGTH == 0.15