Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """ | |
| Upscale MAP.png en 4 passes (4 quadrants) via Vertex AI Imagen upscale. | |
| La map étant trop lourde pour l'API en un seul appel (> 10 Mo), on : | |
| 1. Découpe en 4 quadrants de 2048×2048 | |
| 2. Upscale chaque quadrant ×2 → 4096×4096 chacun | |
| 3. Recolle en une image finale 8192×8192 | |
| Usage : | |
| cd backend && python -m scripts.upscale_map | |
| cd backend && python -m scripts.upscale_map --factor x2 --input static/MAP.png --output static/MAP.png | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import io | |
| import sys | |
| from pathlib import Path | |
| from PIL import Image | |
| _backend = Path(__file__).resolve().parent.parent | |
| if str(_backend) not in sys.path: | |
| sys.path.insert(0, str(_backend)) | |
| from scripts.upscale_cover import upscale_image | |
| def upscale_map( | |
| input_path: Path, | |
| output_path: Path, | |
| factor: str = "x2", | |
| ) -> None: | |
| src = Image.open(input_path).convert("RGB") | |
| w, h = src.size | |
| print(f"Image source : {w}×{h} ({input_path.stat().st_size / 1024 / 1024:.1f} Mo)") | |
| half_w, half_h = w // 2, h // 2 | |
| quadrants = [ | |
| (0, 0, half_w, half_h), # top-left | |
| (half_w, 0, w, half_h), # top-right | |
| (0, half_h, half_w, h ), # bottom-left | |
| (half_w, half_h, w, h ), # bottom-right | |
| ] | |
| labels = ["top-left", "top-right", "bottom-left", "bottom-right"] | |
| upscaled_quadrants: list[Image.Image] = [] | |
| for i, (box, label) in enumerate(zip(quadrants, labels)): | |
| print(f"\n[{i+1}/4] Quadrant {label} {box}") | |
| quad_img = src.crop(box) | |
| tmp_in = Path(f"/tmp/map_quad_{i}.png") | |
| tmp_out = Path(f"/tmp/map_quad_{i}_up.png") | |
| buf = io.BytesIO() | |
| quad_img.save(buf, format="PNG", optimize=False) | |
| size_mb = len(buf.getvalue()) / 1024 / 1024 | |
| print(f" Taille quadrant : {size_mb:.1f} Mo", end="") | |
| if size_mb > 10: | |
| print(" ⚠ > 10 Mo, passage en JPEG lossy pour respecter la limite API") | |
| buf = io.BytesIO() | |
| quad_img.save(buf, format="JPEG", quality=92) | |
| tmp_in = Path(f"/tmp/map_quad_{i}.jpg") | |
| else: | |
| print() | |
| tmp_in.write_bytes(buf.getvalue()) | |
| upscale_image(tmp_in, tmp_out, factor=factor) | |
| upscaled_quadrants.append(Image.open(tmp_out).convert("RGB")) | |
| uw, uh = upscaled_quadrants[0].size | |
| print(f"\nRecomposition : quadrants upscalés {uw}×{uh} → image finale {uw*2}×{uh*2}") | |
| final = Image.new("RGB", (uw * 2, uh * 2)) | |
| final.paste(upscaled_quadrants[0], (0, 0)) | |
| final.paste(upscaled_quadrants[1], (uw, 0)) | |
| final.paste(upscaled_quadrants[2], (0, uh)) | |
| final.paste(upscaled_quadrants[3], (uw, uh)) | |
| output_path.parent.mkdir(parents=True, exist_ok=True) | |
| final.save(output_path, format="PNG", optimize=False) | |
| print(f"\nSauvegardé : {output_path} ({output_path.stat().st_size / 1024 / 1024:.1f} Mo)") | |
| def main() -> None: | |
| default_map = _backend / "static" / "MAP.png" | |
| parser = argparse.ArgumentParser(description="Upscale MAP.png en 4 quadrants via Vertex AI") | |
| parser.add_argument("--input", type=Path, default=default_map) | |
| parser.add_argument("--output", type=Path, default=None) | |
| parser.add_argument("--factor", choices=("x2", "x4"), default="x2") | |
| args = parser.parse_args() | |
| out = args.output or args.input | |
| if not args.input.is_file(): | |
| raise SystemExit(f"Fichier introuvable : {args.input}") | |
| upscale_map(args.input, out, factor=args.factor) | |
| if __name__ == "__main__": | |
| main() | |