mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-10-14 13:30:55 +02:00
132 lines
4.3 KiB
Python
132 lines
4.3 KiB
Python
"""Contracts for the publication-cleared real-image cohort builder."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import csv
|
|
import hashlib
|
|
import importlib.util
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from PIL import Image
|
|
|
|
SCRIPT = Path(__file__).resolve().parents[1] / "scripts" / "watermark_benchmark_real_cohort.py"
|
|
SPEC = importlib.util.spec_from_file_location("watermark_benchmark_real_cohort", SCRIPT)
|
|
assert SPEC is not None
|
|
assert SPEC.loader is not None
|
|
MODULE = importlib.util.module_from_spec(SPEC)
|
|
sys.modules[SPEC.name] = MODULE
|
|
SPEC.loader.exec_module(MODULE)
|
|
|
|
FIELDS = (
|
|
"pair_id",
|
|
"provider",
|
|
"model",
|
|
"file",
|
|
"width",
|
|
"height",
|
|
"content_stratum",
|
|
"sha256",
|
|
"source_payload_sha256",
|
|
"reuse_basis",
|
|
"prompt",
|
|
)
|
|
|
|
|
|
def _source_row(root: Path, provider: str, color: tuple[int, int, int]) -> dict[str, str]:
|
|
path = root / f"{provider}.png"
|
|
Image.new("RGB", (320, 256), color).save(path)
|
|
digest = hashlib.sha256(path.read_bytes()).hexdigest()
|
|
return {
|
|
"pair_id": "00",
|
|
"provider": provider,
|
|
"model": f"{provider}-model",
|
|
"file": path.name,
|
|
"width": "320",
|
|
"height": "256",
|
|
"content_stratum": "landscape",
|
|
"sha256": digest,
|
|
"source_payload_sha256": digest,
|
|
"reuse_basis": MODULE.REUSE_BASIS,
|
|
"prompt": "a test landscape",
|
|
}
|
|
|
|
|
|
def _write_source_manifest(root: Path, rows: list[dict[str, str]]) -> Path:
|
|
manifest = root / "sources.csv"
|
|
with manifest.open("w", newline="", encoding="utf-8") as stream:
|
|
writer = csv.DictWriter(stream, fieldnames=FIELDS)
|
|
writer.writeheader()
|
|
writer.writerows(rows)
|
|
return manifest
|
|
|
|
|
|
def test_real_cohort_records_source_rights_strata_and_standardization(tmp_path: Path) -> None:
|
|
source_manifest = _write_source_manifest(
|
|
tmp_path,
|
|
[
|
|
_source_row(tmp_path, "alpha", (20, 80, 140)),
|
|
_source_row(tmp_path, "beta", (160, 90, 30)),
|
|
],
|
|
)
|
|
|
|
manifest = MODULE.build_real_cohort(
|
|
tmp_path / "cohort",
|
|
source_manifest=source_manifest,
|
|
adapters=("dwt-dct",),
|
|
dwt_schemes=("sdxl",),
|
|
attacks=(),
|
|
size=256,
|
|
)
|
|
rows = MODULE.load_manifest(manifest)
|
|
|
|
assert len(rows) == 4
|
|
assert {(row.arm, row.state) for row in rows} == {
|
|
("matched_negative", "clean"),
|
|
("positive", "marked"),
|
|
}
|
|
assert {row.transform.parameters["source"]["provider"] for row in rows} == {"alpha", "beta"}
|
|
assert {row.transform.parameters["source"]["content_stratum"] for row in rows} == {"landscape"}
|
|
assert {row.transform.parameters["source"]["reuse_basis"] for row in rows} == {MODULE.REUSE_BASIS}
|
|
assert all(len(row.transform.parameters["source"]["cohort_helper_sha256"]) == 64 for row in rows)
|
|
assert all(len(row.transform.parameters["source"]["manifest_helper_sha256"]) == 64 for row in rows)
|
|
assert all(row.source_revision.startswith("engine-selection-content@sha256:") for row in rows)
|
|
for row in rows:
|
|
with Image.open(row.path) as image:
|
|
assert image.size == (256, 256)
|
|
|
|
|
|
def test_real_cohort_rejects_duplicate_pixels_before_creating_output(tmp_path: Path) -> None:
|
|
first = _source_row(tmp_path, "alpha", (20, 80, 140))
|
|
second = _source_row(tmp_path, "beta", (20, 80, 140))
|
|
source_manifest = _write_source_manifest(tmp_path, [first, second])
|
|
output = tmp_path / "cohort"
|
|
|
|
with pytest.raises(ValueError, match="duplicate source sha256"):
|
|
MODULE.build_real_cohort(output, source_manifest=source_manifest, adapters=("dwt-dct",), size=256)
|
|
|
|
assert not output.exists()
|
|
|
|
|
|
def test_real_cohort_requires_explicit_public_reuse_basis(tmp_path: Path) -> None:
|
|
rows = [
|
|
_source_row(tmp_path, "alpha", (20, 80, 140)),
|
|
_source_row(tmp_path, "beta", (160, 90, 30)),
|
|
]
|
|
rows[1]["reuse_basis"] = "unspecified"
|
|
source_manifest = _write_source_manifest(tmp_path, rows)
|
|
|
|
with pytest.raises(ValueError, match="unsupported reuse_basis"):
|
|
MODULE.load_real_carriers(source_manifest)
|
|
|
|
|
|
def test_real_cohort_requires_provider_balance(tmp_path: Path) -> None:
|
|
source_manifest = _write_source_manifest(
|
|
tmp_path,
|
|
[_source_row(tmp_path, "alpha", (20, 80, 140))],
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="at least two providers"):
|
|
MODULE.load_real_carriers(source_manifest)
|