Files
fn_registry/python/functions/ml/crop_to_content.md
T
egutierrez c79f33265e fix(comfyui): pixelart_real_oneshot — sprite llena el frame + fondo transparente
Arregla los dos defectos reportados del pipeline comfyui_pixelart_real_oneshot:
el sujeto salía diminuto respecto al frame y siempre traía fondo (sin opción de
transparencia).

Causa raíz: comfyui_pixelize_image hacía convert("RGB") y descartaba el alpha;
comfyui_build_pixelart_workflow no inyectaba rembg (a diferencia de sus hermanos
item_icon/enemy_creature); y no había ningún paso de auto-crop al contenido.

Orden correcto del pipeline ahora:
generar (rembg) -> autocrop al bbox + cuadrar -> downscale (alpha aparte por
PixelOE) -> cuantización alpha-aware -> PNG RGBA transparente.

Piezas:
- comfyui_pixelize_image (1.1.0): keep_alpha/alpha_threshold. Con RGBA cuantiza
  solo el RGB (fondo transparente relleno con la moda del sujeto, fuera de la
  paleta) y preserva/binariza el alpha aparte. RGB sin alpha intacto.
- crop_to_content (NUEVA, pura PIL): bbox del contenido (alpha o diff-fondo) ->
  recorta -> margen -> cuadra centrando. No-throw; imagen vacía -> copia intacta.
- comfyui_build_pixelart_workflow (1.1.0): transparent=True + rembg_model.
  Inyecta nodo Image Rembg tras VAEDecode (patrón de item_icon).
- comfyui_pixelart_real_oneshot (1.1.0): transparent + autocrop + crop_pad_ratio
  + rembg_model. Recombina el alpha aparte tras PixelOE (trabaja en RGB). Campos
  nuevos: has_alpha, autocrop_applied.

Verificado en GPU (knight 64px): RGBA con 4 esquinas alpha==0, contenido cubre
88% del frame (antes 48%), 16 colores, 64x64. 32 tests offline en verde.
Report: reports/0218-2026-06-28-pixelart-sprite-fix.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 15:59:26 +02:00

4.4 KiB

name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, params, output, tested, tests, test_file_path, file_path
name kind lang domain version purity signature description tags uses_functions uses_types returns returns_optional error_type imports params output tested tests test_file_path file_path
crop_to_content function py ml 1.0.0 pure def crop_to_content(img, *, pad_ratio: float = 0.06, square: bool = True, alpha_threshold: int = 10, bg_tolerance: int = 16) Recorta una imagen PIL al bounding box de su contenido y la cuadra, para que el sujeto llene el frame antes de un downscale a pixel-art. Detecta el contenido por alpha (region con alpha > alpha_threshold) si la imagen es RGBA/LA, o por diferencia contra el color de fondo de las esquinas (con bg_tolerance) si es RGB. Recorta al bbox, anade un margen pad_ratio y, si square, rellena a cuadrado centrando el sujeto sin deformar (fondo transparente si RGBA, color de fondo si RGB). Pura PIL (opera sobre el objeto PIL.Image, no toca disco ni red, no muta la entrada). Si no hay contenido (todo transparente o todo fondo) devuelve una copia intacta — no crashea.
pil
image
crop
bbox
pixelart
gamedev-2d
ml
alpha
sprite
false
name desc
img PIL.Image de entrada (cualquier modo). No se muta. None lanza ValueError.
name desc
pad_ratio Margen anadido alrededor del sujeto como fraccion del lado mayor del bbox recortado (0.06 = 6%). 0 = sin margen. keyword-only.
name desc
square Si True rellena a un lienzo cuadrado de lado max(w,h)+2*pad con el sujeto centrado (fondo transparente si hay alpha, color de fondo si RGB); si False solo recorta al bbox + margen sin cuadrar. keyword-only.
name desc
alpha_threshold Umbral de alpha (0..255) para considerar un pixel 'contenido' cuando la imagen tiene canal alpha. keyword-only.
name desc
bg_tolerance Tolerancia (0..255) de diferencia contra el color de fondo de las esquinas para imagenes sin alpha (RGB). keyword-only.
PIL.Image nueva recortada (y cuadrada si square) con el sujeto llenando el frame. Si la imagen no tiene contenido detectable, devuelve una copia intacta de la entrada (mismo tamano). true
test_golden_corner_subject_fills_frame
test_edge_centered_subject_not_overcropped
test_edge_rgb_background_bbox
test_edge_no_square_only_crops
test_error_all_transparent_returns_copy
test_error_none_raises
test_does_not_mutate_input
python/functions/ml/crop_to_content_test.py python/functions/ml/crop_to_content.py

Ejemplo

import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from PIL import Image
from ml.crop_to_content import crop_to_content

# Sprite RGBA tras rembg: el sujeto ocupa una esquina -> recortar al bbox y cuadrar.
with Image.open("/tmp/knight_rgba.png") as im:
    out = crop_to_content(im, pad_ratio=0.06, square=True)
out.save("/tmp/knight_cropped.png")   # RGBA cuadrada, sujeto centrado llenando el frame

# CLI directo:
#   ./fn run crop_to_content   (corre los tests)
#   python3 crop_to_content.py /tmp/in.png /tmp/out.png 0.06

Cuando usarla

Antes de bajar una imagen a pixel-art (32/64px): si el sujeto ocupa poca area del lienzo, al downscalear queda diminuto y tosco. crop_to_content recorta el aire alrededor y cuadra para que el sujeto aproveche todos los pixeles del grid. Es el paso de encuadre del pipeline comfyui_pixelart_real_oneshot (autocrop). Funciona con sprites recortados por rembg (detecta por alpha) o con imagenes de fondo plano (detecta por diferencia contra el color de esquina).

Gotchas

  • Pura sobre PIL.Image: recibe y devuelve un objeto PIL.Image, NO rutas. El caller hace el Image.open / .save. No muta la imagen de entrada.
  • Deteccion del contenido: con alpha usa alpha > alpha_threshold; sin alpha usa la moda de las 4 esquinas como color de fondo y bg_tolerance de diferencia. Si el fondo no es uniforme (gradiente) la deteccion RGB puede fallar; para esos casos pasa la imagen ya recortada por rembg (RGBA).
  • Si no hay contenido (todo transparente o todo del color de fondo) devuelve una copia intacta del original (mismo tamano), nunca lanza por una imagen vacia. Solo lanza ValueError si img es None.
  • square=True (default) cuadra a max(w,h)+2*pad: si el sujeto es muy alargado el lienzo crece al lado mayor y el sujeto queda centrado con barras transparentes (o de color de fondo) a los lados — sin deformar.
  • pad_ratio es relativo al lado mayor del bbox, no del lienzo original.