chore: auto-commit (95 archivos)
- cmd/fn/doctor.go - cmd/fn/main.go - cpp/apps/primitives_gallery/playground/tables/CMakeLists.txt - cpp/apps/primitives_gallery/playground/tables/data_table.cpp - cpp/apps/primitives_gallery/playground/tables/data_table_logic.cpp - cpp/apps/primitives_gallery/playground/tables/data_table_logic.h - cpp/apps/primitives_gallery/playground/tables/self_test.cpp - cpp/apps/primitives_gallery/playground/tables/tql.cpp - cpp/apps/primitives_gallery/playground/tables/viz.cpp - cpp/apps/primitives_gallery/playground/tables/viz.h - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user