"""Combina una lista de PIL Images en un grid NxM con gap y labels opcionales.""" from __future__ import annotations import math def image_grid( images: list["PIL.Image.Image"], cols: int = 4, labels: list[str] | None = None, gap_px: int = 8, bg_color: tuple = (20, 20, 20), ) -> "PIL.Image.Image": """Combina una lista de imagenes en un grid NxM. Asume que todas las imagenes tienen el mismo tamano (usa el maximo ancho/alto detectado). Calcula rows = ceil(n / cols) automaticamente. Args: images: lista de PIL.Image.Image a colocar en el grid. cols: numero de columnas del grid (default 4). labels: lista opcional de strings. Si se proporciona, se escribe un label encima de cada celda usando la fuente default de PIL. gap_px: espacio en pixeles entre celdas y en los bordes (default 8). bg_color: color de fondo RGB del canvas (default casi negro (20,20,20)). Returns: Una sola PIL.Image en modo RGB con el grid montado. Raises: ImportError: si Pillow no esta instalado. ValueError: si images esta vacio. """ try: from PIL import Image, ImageDraw, ImageFont except ImportError as exc: raise ImportError( "Pillow no esta instalado. Instalar con: pip install Pillow" ) from exc if not images: raise ValueError("image_grid: la lista de imagenes no puede estar vacia") n = len(images) rows = math.ceil(n / cols) # Tamano de celda: max ancho y alto de todas las imagenes cell_w = max(img.width for img in images) cell_h = max(img.height for img in images) canvas_w = cols * cell_w + (cols + 1) * gap_px canvas_h = rows * cell_h + (rows + 1) * gap_px canvas = Image.new("RGB", (canvas_w, canvas_h), color=bg_color) draw = ImageDraw.Draw(canvas) if labels else None font = None if draw: try: font = ImageFont.load_default() except Exception: font = None for idx, img in enumerate(images): row = idx // cols col = idx % cols x = gap_px + col * (cell_w + gap_px) y = gap_px + row * (cell_h + gap_px) # Convertir a RGB si hace falta (RGBA, L, P, etc.) paste_img = img.convert("RGB") if img.mode != "RGB" else img canvas.paste(paste_img, (x, y)) if draw and labels and idx < len(labels): label = labels[idx] # Texto en la esquina superior izquierda de la celda draw.text((x + 2, y + 2), label, fill=(255, 255, 255), font=font) return canvas