""" Screenshot / image utility functions. """ from __future__ import annotations import base64 import io from pathlib import Path from typing import Tuple, Union from PIL import Image def load_image(path: Union[str, Path]) -> Image.Image: """ Load an image from a filesystem path. Args: path: Absolute or relative path to the image file. Returns: PIL.Image in RGB mode. Raises: FileNotFoundError: If the file does not exist. IOError: If the file cannot be opened as an image. """ path = Path(path) if not path.exists(): raise FileNotFoundError(f"Image not found: {path}") return Image.open(path).convert("RGB") def save_image(img: Image.Image, path: Union[str, Path]) -> None: """ Save a PIL.Image to a filesystem path, creating parent directories as needed. Args: img: PIL.Image to save. path: Destination file path (.png, .jpg, etc.). """ path = Path(path) path.parent.mkdir(parents=True, exist_ok=True) img.save(str(path)) def resize_image( img: Image.Image, target_size: Tuple[int, int] = (1280, 720), ) -> Image.Image: """ Resize an image to the target (width, height) using Lanczos resampling. Args: img: Input PIL.Image. target_size: (width, height) tuple. Returns: Resized PIL.Image. """ if img.size == target_size: return img return img.resize(target_size, Image.LANCZOS) def base64_to_image(b64_str: str) -> Image.Image: """ Decode a base64-encoded image string to a PIL.Image. Args: b64_str: Base64 string (with or without data URI prefix). Returns: PIL.Image in RGB mode. """ # Strip data URI prefix if present, e.g. "data:image/png;base64,..." if "," in b64_str: b64_str = b64_str.split(",", 1)[1] data = base64.b64decode(b64_str) return Image.open(io.BytesIO(data)).convert("RGB") def image_to_base64(img: Image.Image, fmt: str = "PNG") -> str: """ Encode a PIL.Image to a base64 string. Args: img: Input PIL.Image. fmt: Image format for encoding (default: "PNG"). Returns: Base64-encoded string (no data URI prefix). """ buf = io.BytesIO() img.save(buf, format=fmt) buf.seek(0) return base64.b64encode(buf.read()).decode("utf-8")