#!/usr/bin/env python """Generate samples with multiple random degradations stacked on one template. Unlike scripts/generate_degradation_gallery.py (which isolates exactly one effect per sample for comparison), this uses the realistic "varied batch" probabilities from configs/generate.yaml unmodified -- every enabled effect independently rolls its own probability per sample, so different samples end up with different *combinations* of simultaneously-applied degradations, all on the same template/box for a fair before/after comparison. Each output image is the standard metrics visualization (full page, crop, ink mask, coverage metrics, difficulty breakdown) plus a caption band listing exactly which effects fired for that sample. Usage: python scripts/generate_multiple_degradation_gallery.py """ from __future__ import annotations import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from PIL import Image, ImageDraw, ImageFont from src.config.loader import load_config from src.generator import GeneratorContext from src.visualization.visualizer import save_visualization ROOT = Path(__file__).resolve().parents[1] OUT_DIR = ROOT / "multiple_degradation" N_SAMPLES = 10 TEMPLATE_ID = "bank_doc_05" # same template used in the single-effect gallery def _load_font(size: int) -> "ImageFont.FreeTypeFont | ImageFont.ImageFont": for candidate in (r"C:\Windows\Fonts\consola.ttf", r"C:\Windows\Fonts\arial.ttf"): if Path(candidate).exists(): return ImageFont.truetype(candidate, size) return ImageFont.load_default() def _annotate_applied_effects(path: Path, metadata: dict) -> list[str]: effects = metadata["augmentation"]["effects"] applied = [name for name, rec in effects.items() if rec["applied"]] difficulty = metadata["difficulty"] caption = ( f"{metadata['document_id']} | applied degradations " f"({len(applied)}/{len(effects)}): {', '.join(applied) if applied else 'none'}" f" | difficulty = {difficulty['score']:.3f} ({difficulty['tier']})" ) img = Image.open(path).convert("RGB") band_h = 40 canvas = Image.new("RGB", (img.width, img.height + band_h), "white") canvas.paste(img, (0, band_h)) draw = ImageDraw.Draw(canvas) font = _load_font(16) draw.rectangle([0, 0, canvas.width, band_h], fill=(15, 42, 67)) draw.text((14, 11), caption, fill="white", font=font) canvas.save(path) return applied def main() -> None: OUT_DIR.mkdir(parents=True, exist_ok=True) overrides = [ f"document.template={TEMPLATE_ID}", "generation.run_validation=false", ] config = load_config(config_path=ROOT / "configs" / "generate.yaml", overrides=overrides) ctx = GeneratorContext(config) for i in range(1, N_SAMPLES + 1): metadata, final_image, _ = ctx.run_sample(i) out_path = OUT_DIR / f"sample_{i}.png" save_visualization(out_path, final_image, metadata) applied = _annotate_applied_effects(out_path, metadata) print(f"[sample_{i}] {len(applied)} effects applied -> {', '.join(applied) or 'none'}") print(f"\nWrote {N_SAMPLES} samples -> {OUT_DIR}") if __name__ == "__main__": main()