from __future__ import annotations from pathlib import Path from typing import Iterable from PIL import Image, ImageOps def load_garment_images(file_paths) -> list[Image.Image]: """Load Gradio File outputs into RGB PIL images.""" if file_paths is None: return [] if isinstance(file_paths, (str, Path)): file_paths = [file_paths] images: list[Image.Image] = [] for item in file_paths: if item is None: continue path = item if not isinstance(path, (str, Path)) and hasattr(path, "name"): path = path.name p = Path(path) if not p.exists(): continue with Image.open(p) as img: images.append(ImageOps.exif_transpose(img).convert("RGB")) return images def normalize_pil_image(image: Image.Image | None) -> Image.Image | None: if image is None: return None return ImageOps.exif_transpose(image).convert("RGB") def resize_for_demo(image: Image.Image, max_side: int = 1024) -> Image.Image: """Downscale very large uploads while preserving aspect ratio.""" image = normalize_pil_image(image) if image is None: raise ValueError("image is required") width, height = image.size longest = max(width, height) if longest <= max_side: return image scale = max_side / float(longest) new_size = (max(1, int(width * scale)), max(1, int(height * scale))) return image.resize(new_size, Image.Resampling.LANCZOS) def clamp_primary_index(primary_garment_index: int, garment_count: int) -> int: if garment_count <= 0: raise ValueError("At least one garment reference image is required.") return max(0, min(int(primary_garment_index), garment_count - 1)) def select_primary_garment( garment_images: Iterable[Image.Image], primary_garment_index: int, ) -> tuple[Image.Image, int, list[Image.Image]]: images = list(garment_images) index = clamp_primary_index(primary_garment_index, len(images)) return images[index], index, images