mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-08-10 08:00:32 +02:00
Match the vendor registries against metadata, not coded pixels
The registries are raw substrings and the shortest tokens are four and five bytes (`Bria`, `Adobe`, `Canva`). Over a megabyte of compressed pixel data such a sequence turns up by chance: `Bria` matched inside the entropy-coded scan of 4 of 14,707 corpus JPEGs, in none of which the manifest names Bria. The rate is what a four-byte pattern predicts on that corpus, and the Bria entry asserts AI, so a chance match can declare an image AI-generated rather than merely mislabel its signer. `_metadata_region` gives the registry scans the container's metadata: JPEG marker segments before the coded scan, PNG chunks other than IDAT, both trailers, and whatever `scan_head` appended past the window. Every other check keeps the full buffer -- their markers are long and distinctive. A container that does not parse is returned whole, since dropping real evidence to avoid a chance match is the wrong trade. `c2pa_marker_in` already refuses a bare `c2pa` substring for this reason; this is the same defence for the registries. Verified the way the rules require for a change that MOVES a verdict: over all 48,905 corpus images, exactly one file changed, the one named in advance, from "C2PA Content Credentials (Bria Artificial Intelligence)" to "(unknown signer)". Record-path parity is 0 disagreements, down from 75 when this work started. The audit's own baseline comparison is fixed here too. It compared confidence and signals only, and so reported "0 changed" for the run whose single intended correction was a watermark line -- the change it exists to show. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9a29dcac8a
commit
1124c591be
@@ -1406,3 +1406,53 @@ class TestSynthIdProxyIsDecidedInTheVerdict:
|
||||
report = identify(path, check_visible=False, check_invisible=False)
|
||||
|
||||
assert not any("SynthID" in mark for mark in report.watermarks)
|
||||
|
||||
|
||||
class TestRegistryScansSkipTheCodedPixels:
|
||||
"""The vendor registries match short raw substrings -- the shortest are four and
|
||||
five bytes. Over a megabyte of compressed pixel data such a sequence turns up by
|
||||
chance: `Bria` matched inside the entropy-coded scan of 4 of 14,707 corpus JPEGs,
|
||||
and that entry asserts AI, so a chance match can declare an image AI-generated.
|
||||
`c2pa_marker_in` already refuses a bare `c2pa` substring for the same reason."""
|
||||
|
||||
def _jpeg(self, path: Path, *, in_segment: bytes = b"", in_scan: bytes = b"") -> Path:
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
Image.fromarray(np.zeros((32, 32, 3), dtype=np.uint8)).save(path, "JPEG")
|
||||
data = path.read_bytes()
|
||||
if in_segment:
|
||||
payload = b"jumb c2pa trainedAlgorithmicMedia " + in_segment
|
||||
data = data[:2] + b"\xff\xeb" + (len(payload) + 2).to_bytes(2, "big") + payload + data[2:]
|
||||
if in_scan:
|
||||
# After SOS, i.e. inside the entropy-coded scan the walk skips.
|
||||
sos = data.index(b"\xff\xda")
|
||||
data = data[: sos + 16] + in_scan + data[sos + 16 :]
|
||||
path.write_bytes(data)
|
||||
return path
|
||||
|
||||
def test_a_token_in_a_marker_segment_is_attributed(self, tmp_path: Path):
|
||||
from remove_ai_watermarks.identify import _issuers_in, _metadata_region
|
||||
from remove_ai_watermarks.metadata import scan_head
|
||||
|
||||
path = self._jpeg(tmp_path / "signed.jpg", in_segment=b"Bria")
|
||||
|
||||
assert _issuers_in(_metadata_region(scan_head(path))) == ["Bria Artificial Intelligence"]
|
||||
|
||||
def test_a_token_in_the_coded_scan_is_not(self, tmp_path: Path):
|
||||
from remove_ai_watermarks.identify import _issuers_in, _metadata_region
|
||||
from remove_ai_watermarks.metadata import scan_head
|
||||
|
||||
path = self._jpeg(tmp_path / "chance.jpg", in_segment=b"OpenAI", in_scan=b"Bria")
|
||||
region = _metadata_region(scan_head(path))
|
||||
|
||||
assert _issuers_in(region) == ["OpenAI"]
|
||||
|
||||
def test_a_container_that_does_not_parse_is_left_whole(self, tmp_path: Path):
|
||||
"""Cutting a buffer the walk did not understand would drop real evidence to
|
||||
avoid a chance match, which is the wrong way round."""
|
||||
from remove_ai_watermarks.identify import _metadata_region
|
||||
|
||||
blob = b"\xff\xd8" + b"not really a jpeg, no valid marker chain here" * 4
|
||||
|
||||
assert _metadata_region(blob) == blob
|
||||
|
||||
Reference in New Issue
Block a user