--- name: image_grid kind: function lang: py domain: ml version: "1.0.0" purity: impure signature: "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" description: "Combina una lista de PIL Images en un grid NxM con gap configurable, fondo oscuro y labels opcionales sobre cada celda. rows se calcula como ceil(n/cols). Retorna una sola PIL.Image RGB." tags: [image, grid, pil, pillow, visualization, ml, montage, collage] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: [Pillow] params: - name: images desc: "lista de PIL.Image.Image a colocar en el grid (todas deben tener el mismo tamano o se usa el maximo)" - name: cols desc: "numero de columnas del grid (default 4)" - name: labels desc: "lista opcional de strings para etiquetar cada celda en la esquina superior izquierda" - name: gap_px desc: "espacio en pixeles entre celdas y en los bordes del canvas (default 8)" - name: bg_color desc: "color RGB de fondo del canvas como tupla (R, G, B), default (20,20,20) casi negro" output: "PIL.Image.Image: imagen RGB con el grid montado. Lista con n imagenes en cols columnas y ceil(n/cols) filas." tested: true tests: - "grid de 4 imagenes 16x16 cols=2 produce ancho/alto correcto" - "grid de 4 imagenes cols=2 gap_px=8 tiene dimensiones correctas con gap" - "grid de 1 imagen 1 col" - "el resultado es una imagen RGB" - "labels opcionales no lanza excepcion" - "sin labels funciona correctamente" - "lista vacia levanta ValueError" test_file_path: "python/functions/ml/tests/test_image_grid.py" file_path: "python/functions/ml/image_grid.py" --- ## Ejemplo ```python from PIL import Image from ml.image_grid import image_grid from ml.image_save_png import image_save_png # Generar 6 imagenes de prueba con colores distintos colors = [(255,0,0),(0,255,0),(0,0,255),(255,255,0),(0,255,255),(255,0,255)] imgs = [Image.new("RGB", (256, 256), c) for c in colors] grid = image_grid( imgs, cols=3, labels=["rojo", "verde", "azul", "amarillo", "cyan", "magenta"], gap_px=10, bg_color=(30, 30, 30), ) # grid.size == (3*256 + 4*10, 2*256 + 3*10) == (788, 542) image_save_png(grid, "outputs/preview_grid.png") ``` ## Notas - Impure: asigna memoria para el canvas y ejecuta `ImageDraw` (efectos en objetos PIL internos). Aunque no hace I/O de disco, las allocations PIL y el draw tienen side-effects sobre objetos mutables. - Las imagenes en modo no-RGB (RGBA, L, P, palette) se convierten a RGB automaticamente con `.convert("RGB")` antes de pegar. - Si la lista tiene menos imagenes que `cols * rows`, las celdas sobrantes quedan en blanco (solo el color de fondo). - El label usa `ImageFont.load_default()` (fuente bitmap monospace de PIL, sin dependencias externas). Para fuentes TTF customizadas usar `ImageFont.truetype(path, size)` externamente y pasar un `font` propio. - Pillow se importa lazy para no bloquear `fn index`.