"""Transforms that simulate what a scraper/training pipeline does to an image. `scraper_sim` (JPEG Q85 + light blur) is baked into fitness so the overlay is selected to survive realistic preprocessing. `hard_transforms` (heavy blur, downscale, rotation) are the robustness *ceiling* used only in the eval harness. """ from __future__ import annotations import io from PIL import Image, ImageFilter from veil_pgd.config import RobustnessSim, get_settings def jpeg_recompress(image: Image.Image, quality: int) -> Image.Image: buf = io.BytesIO() image.convert("RGB").save(buf, format="JPEG", quality=quality) buf.seek(0) return Image.open(buf).convert("RGB") def scraper_sim(image: Image.Image, cfg: RobustnessSim | None = None) -> Image.Image: cfg = cfg or get_settings().robustness out = image if cfg.gaussian_blur_radius > 0: out = out.filter(ImageFilter.GaussianBlur(cfg.gaussian_blur_radius)) out = jpeg_recompress(out, cfg.jpeg_quality) return out def hard_transforms(image: Image.Image) -> dict[str, Image.Image]: """The robustness ceiling: transforms known to weaken typographic overlays.""" w, h = image.size return { "heavy_blur": image.filter(ImageFilter.GaussianBlur(2.5)), "downscale_half": image.resize((max(1, w // 2), max(1, h // 2))).resize((w, h)), "rotate_15": image.rotate(15, resample=Image.BICUBIC, expand=False), "jpeg_q50": jpeg_recompress(image, 50), }