feat(comfyui): pipeline comfyui_pixelart_real_oneshot — pixelart REAL (PixelOE + cuantizacion dura)
Materializa el metodo ganador del report 0215: generar a alta-res con SDXL + LoRA SDXL_pixel-art, downscale contrast-aware con PixelOE (engine=pixeloe para sprites/personajes) o nearest (tiles), y cuantizacion dura con comfyui_pixelize_image (16 colores libres o paleta fija pico-8/nes/game-boy). - pixeloe_downscale_py_ml: downscale contrast-aware via lib pixeloe con bridge de interprete (la lib vive en el venv de ComfyUI, no en el del registry). No-throw, fallback limpio si pixeloe no disponible. - comfyui_pixelart_real_oneshot_py_pipelines: one-shot que compone build_pixelart + submit + wait + fetch + pixeloe_downscale + pixelize_image. Fallback automatico pixeloe->nearest. Sweet-spot 64px personajes, 32px iconos. Verificado por PIL: personaje 64x64=16 colores, icono 32x32=16 colores (vs ~33k de la imagen de difusion cruda). 100% grid duro + outline nitido. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
---
|
||||
name: pixeloe_downscale
|
||||
kind: function
|
||||
lang: py
|
||||
domain: ml
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def pixeloe_downscale(src_path: str, dst_path: str, *, mode: str = 'contrast', target_size: int = 64, patch_size: int = 16, thickness: int = 2, color_matching: bool = True, no_upscale: bool = True, comfy_python: str | None = None) -> dict"
|
||||
description: "Downscale contrast-aware (Contrast-Aware Outline Expansion de Kohaku, lib `pixeloe`) que colapsa una ilustracion a un grid de pixel-art pequeno (64 personajes, 32 iconos) conservando contornos/silueta. Es la etapa de downscale del metodo SOTA de pixel-art (report 0215). NO cuantiza la paleta (eso lo hace despues comfyui_pixelize_image). Resuelve el gotcha de que `pixeloe` solo vive en el venv de ComfyUI con un 'bridge' de interprete: si falta en el interprete actual, re-ejecuta su nucleo por subprocess con el python de ComfyUI. No-throw: todo error viaja en `error`. Determinista; impura por I/O de disco + subprocess. Devuelve {ok, out_path, size, mode, target_size, via, error}."
|
||||
tags: [comfyui, gamedev-2d, pixelart, ml, pixeloe, downscale, contrast-aware, image, bridge]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_py_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: src_path
|
||||
desc: "ruta de la imagen de entrada (PNG/JPG/...). Si no existe -> ok=False con error."
|
||||
- name: dst_path
|
||||
desc: "ruta del PNG de salida; se crea el directorio padre si falta."
|
||||
- name: mode
|
||||
desc: "algoritmo de downscale de pixeloe: 'contrast' (SOTA, conserva silueta), 'bicubic', 'nearest', 'center' o 'k-centroid'. keyword-only."
|
||||
- name: target_size
|
||||
desc: "lado del grid resultante en pixeles (64 para personajes, 32 para iconos). keyword-only."
|
||||
- name: patch_size
|
||||
desc: "tamano del patch que pixeloe colapsa por celda del grid. keyword-only."
|
||||
- name: thickness
|
||||
desc: "grosor de la expansion de contorno (outline expansion). keyword-only."
|
||||
- name: color_matching
|
||||
desc: "corrige el color de cada celda contra el original si True. keyword-only."
|
||||
- name: no_upscale
|
||||
desc: "True devuelve el grid real target_size x target_size (lo habitual, para luego cuantizar); False re-escala al tamano original con pixeles duros (preview). keyword-only."
|
||||
- name: comfy_python
|
||||
desc: "ruta a un interprete con `pixeloe` para el bridge cuando el actual no la tiene. Si None: COMFY_PYTHON y luego ~/ComfyUI/.venv/bin/python3. keyword-only."
|
||||
output: "dict con ok (bool), out_path (str), size ([w,h] de la imagen escrita), mode (str usado), target_size (int pedido), via ('inproc' si pixeloe estaba en este interprete, 'bridge' si se delego por subprocess) y error (str, vacio si OK). No lanza excepciones."
|
||||
tested: true
|
||||
tests: [test_golden_downscale_64_or_clean_degrade, test_edge_target_size_32, test_edge_mode_nearest_no_color_matching, test_error_missing_src_no_throw, test_error_no_interpreter_with_pixeloe]
|
||||
test_file_path: "python/functions/ml/pixeloe_downscale_test.py"
|
||||
file_path: "python/functions/ml/pixeloe_downscale.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
|
||||
from ml.pixeloe_downscale import pixeloe_downscale
|
||||
|
||||
# Colapsa el render del caballero (1024x1024) a un grid de pixel-art 64x64
|
||||
# conservando la silueta. NO cuantiza paleta todavia.
|
||||
res = pixeloe_downscale(
|
||||
os.path.expanduser("~/ComfyUI/output/pixel_compare/knight_base_00001_.png"),
|
||||
"/tmp/knight_grid64.png",
|
||||
mode="contrast", target_size=64, no_upscale=True,
|
||||
)
|
||||
# {'ok': True, 'out_path': '/tmp/knight_grid64.png', 'size': [64, 64],
|
||||
# 'mode': 'contrast', 'target_size': 64, 'via': 'bridge', 'error': ''}
|
||||
|
||||
# Despues: dureza de color (cuantizacion) con la funcion hermana.
|
||||
from ml.comfyui_pixelize_image import comfyui_pixelize_image
|
||||
comfyui_pixelize_image("/tmp/knight_grid64.png", "/tmp/knight_q16.png",
|
||||
downscale=1, colors=16, upscale_back=False)
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Primera etapa del metodo SOTA de pixel-art: cuando ya tienes una ilustracion (render
|
||||
SDXL/Flux, sprite, foto) y quieres reducirla a un grid de pixel-art chico **sin perder
|
||||
los contornos** (lo que arruina un resize NEAREST/lanczos normal). Usala **antes** de
|
||||
la cuantizacion dura de paleta con `comfyui_pixelize_image` (paso de color). `target_size`
|
||||
64 para personajes, 32 para iconos. Si solo necesitas el resize+cuantizado rapido sin
|
||||
contornos finos, `comfyui_pixelize_image` sola basta; para el resultado ganador, encadena
|
||||
`pixeloe_downscale` -> `comfyui_pixelize_image`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **`pixeloe` solo esta en el venv de ComfyUI** (`~/ComfyUI/.venv`), no en el del registry.
|
||||
La funcion lo resuelve con un *bridge*: si `import pixeloe` falla, re-ejecuta su nucleo
|
||||
por subprocess con el python de ComfyUI. El campo `via` dice si fue `inproc` o `bridge`.
|
||||
- **El modulo es `pixeloe.legacy.pixelize`**, no `pixeloe.pixelize` (ruta vieja eliminada).
|
||||
- **El nodo `PixelOEPixelize+` de ComfyUI_essentials estaba roto** por ese cambio de import;
|
||||
por eso aqui se llama la lib directa (numpy + PIL, sin cv2).
|
||||
- **NO cuantiza la paleta**: el resultado conserva muchos colores; la dureza retro la aplica
|
||||
despues `comfyui_pixelize_image`. No esperes pocos colores en la salida.
|
||||
- **No-throw**: src inexistente, pixeloe ausente en todos los interpretes, o subprocess
|
||||
caido -> `ok=False` con `error` explicado, nunca excepcion. El pipeline llamante hace
|
||||
fallback mirando `ok`.
|
||||
- Resolucion del interprete del bridge: arg `comfy_python` -> env `COMFY_PYTHON` ->
|
||||
`~/ComfyUI/.venv/bin/python3` (el primero que exista como archivo).
|
||||
- `no_upscale=True` (default) devuelve el grid real `target_size x target_size`; con `False`
|
||||
vuelve al tamano original con pixeles duros (preview), no el grid pequeno.
|
||||
Reference in New Issue
Block a user