mirror of
https://github.com/guillaumemeyer/watermarks-remover.git
synced 2026-08-22 13:11:57 +02:00
Add stdlib-only detection, inspection, and cleaning for four more formats. - BMP: locate the pixel payload via the DIB header and strip trailing non-image metadata (the only place non-standard BMP metadata can live), rewriting the file-size field. - GIF: drop comment and XMP application extensions while preserving NETSCAPE2.0 looping, ICC, graphic-control, and image blocks. - TIFF (classic + BigTIFF): walk IFD chains and drop XMP/EXIF/GPS/IPTC/ Photoshop/MakerNote tags, zeroing orphaned payloads while keeping strip/tile offsets valid. - EPUB: scrub OPF package metadata and XHTML meta/JSON-LD, clean embedded raster/SVG media, apply Layer A to XHTML body text, and pass OCF-encrypted parts through untouched. All four route through format_dispatch, so the unified CLIs, the HTTP service, and the audits pick them up automatically.
73 lines
2.6 KiB
Python
Executable File
73 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Inspect raster images (PNG/JPEG/WebP/AVIF/HEIC/BMP/GIF/TIFF) for C2PA and AI-related metadata."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
|
|
from common import classify_finding_confidence, emit_json
|
|
from image_meta import inspect_image
|
|
|
|
|
|
def main() -> int:
|
|
p = argparse.ArgumentParser(description=__doc__)
|
|
p.add_argument("path", type=Path, help="Image path (PNG/JPEG/WebP/AVIF/HEIC/BMP/GIF/TIFF)")
|
|
p.add_argument("--json", action="store_true")
|
|
p.add_argument(
|
|
"--synthid-dir",
|
|
type=str,
|
|
default=None,
|
|
help="reverse-SynthID checkout root for optional pixel SynthID scoring",
|
|
)
|
|
args = p.parse_args()
|
|
|
|
if not args.path.is_file():
|
|
print(f"not a file: {args.path}", file=sys.stderr)
|
|
return 2
|
|
|
|
report = inspect_image(args.path, synthid_dir=args.synthid_dir)
|
|
if args.json:
|
|
emit_json(report.to_dict())
|
|
else:
|
|
print(f"Path: {report.path}")
|
|
print(f"Format: {report.format}")
|
|
print(f"C2PA: {report.has_c2pa}")
|
|
print(f"AI metadata: {report.has_ai_metadata}")
|
|
if report.findings:
|
|
print("Findings:")
|
|
for f in report.findings:
|
|
print(f" - [{classify_finding_confidence(f)}] {f}")
|
|
ct = report.tools.get("c2patool") or {}
|
|
print(f"c2patool: {'yes' if ct.get('available') else 'no'}")
|
|
et = report.tools.get("exiftool") or {}
|
|
print(f"exiftool: {'yes' if et.get('available') else 'no'}")
|
|
if et.get("interesting_lines"):
|
|
print("exiftool highlights:")
|
|
for line in et["interesting_lines"][:20]:
|
|
print(f" {line}")
|
|
if report.synthid and report.synthid.get("available"):
|
|
label = "yes" if report.synthid.get("is_watermarked") else "no"
|
|
print(
|
|
"SynthID score: "
|
|
f"confidence {report.synthid.get('confidence', 0.0):.3f} "
|
|
f"(watermarked: {label})"
|
|
)
|
|
if report.synthid.get("is_watermarked"):
|
|
print(
|
|
"Hint: optional pixel removal is available via "
|
|
"clean_image.py IMG --remove-pixel ctrlregen "
|
|
"--ctrlregen-dir $NOAI_WATERMARK_DIR"
|
|
)
|
|
elif report.synthid and report.synthid.get("error"):
|
|
print(f"SynthID score: error: {report.synthid['error']}")
|
|
|
|
return 0 if not (report.has_c2pa or report.has_ai_metadata) else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|