mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-08-19 12:07:13 +02:00
feat(metadata): blank AI-generator tokens in AVIF/HEIF Exif meta-box items
Closes a documented coverage gap (P2#9): an AI Software/Make/Artist/ImageDescription token in an EXIF item (its TIFF bytes live in mdat/idat) survived remove_ai_metadata because the top-level box stripper and (absent pillow-heif) the PIL EXIF reader can't reach it. New isobmff.blank_ai_exif_tokens finds EXIF TIFF blocks by their II/MM byte-order header, validates each with piexif (a coincidental II/MM run in pixels won't parse as a TIFF IFD, so it's ignored), and overwrites any AI_GENERATOR_TOKENS- bearing value with same-length spaces -- so box sizes and iloc offsets stay valid and the coded image is untouched (mirrors blank_ai_xmp_packets; no iinf/iloc surgery, no exiftool dep). Camera/editor EXIF without an AI token is preserved. Wired into remove_ai_metadata's ISOBMFF path. Covers the realistic AI-generator-token case; xAI- signature-in-meta-box-EXIF (Grok is JPEG-only) stays out. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3f5d6a0af1
commit
d5845a72f3
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import struct
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
@@ -125,6 +126,24 @@ class TestHasAiMetadata:
|
||||
|
||||
assert not has_ai_metadata(out)
|
||||
|
||||
def test_remove_ai_metadata_blanks_exif_token_item_in_avif(self, tmp_path: Path):
|
||||
"""End-to-end: ``remove_ai_metadata`` blanks an AI-generator EXIF token
|
||||
stored as a meta-box Exif item (bytes in mdat) without re-encoding."""
|
||||
from remove_ai_watermarks.metadata import remove_ai_metadata
|
||||
|
||||
ftyp = b"\x00\x00\x00\x18ftypavif\x00\x00\x00\x00avifmif1"
|
||||
blob = piexif.dump({"0th": {piexif.ImageIFD.Software: b"Midjourney", piexif.ImageIFD.Make: b"NIKON"}})
|
||||
mdat = struct.pack(">I", 8 + len(blob)) + b"mdat" + blob
|
||||
src = tmp_path / "in.avif"
|
||||
src.write_bytes(ftyp + mdat)
|
||||
|
||||
out = tmp_path / "out.avif"
|
||||
remove_ai_metadata(src, out)
|
||||
cleaned = out.read_bytes()
|
||||
assert len(cleaned) == len(ftyp + mdat) # in place, no re-encode
|
||||
assert b"Midjourney" not in cleaned # AI token gone
|
||||
assert b"NIKON" in cleaned # camera tag preserved
|
||||
|
||||
def test_detects_iptc_trained_algorithmic_media_marker(self, tmp_path: Path):
|
||||
"""Some pipelines embed only the IPTC AI marker in XMP, no C2PA manifest."""
|
||||
path = tmp_path / "fake.jpg"
|
||||
|
||||
Reference in New Issue
Block a user