---
name: comfyui_build_sprite_from_sketch_workflow
kind: function
lang: py
domain: ml
purity: pure
version: 1.0.0
signature: "def comfyui_build_sprite_from_sketch_workflow(sketch_image: str, subject: str, *, control_type: str = \"lineart\", checkpoint: str = \"IMG_dreamshaper_8.safetensors\", style: str = \"game asset, clean, centered\", strength: float = 0.8, size: int = 512, seed: int = 0, lora: str | None = None, lora_strength: float = 1.0, preprocess: bool = True, controlnet_name: str | None = None, negative: str | None = None, steps: int = 28, cfg: float = 7.0, sampler_name: str = \"dpmpp_2m\", scheduler: str = \"karras\", filename_prefix: str = \"sprite_from_sketch\") -> dict"
description: "Construye el dict (API format) del workflow ComfyUI que PINTA un sprite a partir del BOCETO del dev guiado por ControlNet: recibe el dibujo tosco (boceto, lineart o garabato) + un prompt de QUE es, y genera un sprite en estilo de juego que CONSERVA la forma dibujada. Es el tercer eje del catalogo gamedev-2d, distinto de txt2img (enemy_creature, item_icon: inventan la forma desde texto en blanco) y de img2img (asset_variant: reescribe una imagen YA pintada): aqui el dev marca la silueta con su dibujo y la IA pone material/color/acabado. Mecanismo: txt2img base (ruido, denoise 1.0) condicionado por un ControlNet atado al mapa de lineas del boceto. control_type elige el preprocesador (LineArtPreprocessor / ScribblePreprocessor / CannyEdgePreprocessor) y, por defecto, el modelo CN emparejado. Compone comfyui_build_txt2img_workflow + comfyui_inject_controlnet + comfyui_inject_lora (estilo opcional); el unico codigo propio es el helper que interpone el preprocesador entre el boceto y el ControlNet. Pura, sin red ni I/O. class_types verificados contra /object_info (8GB lowvram)."
tags: [comfyui, ml, gamedev-2d, controlnet, sketch, lineart, scribble, canny, sprite, asset-transform, stable-diffusion, workflow]
uses_functions: [comfyui_build_txt2img_workflow_py_ml, comfyui_inject_controlnet_py_ml, comfyui_inject_lora_py_ml]
uses_types: []
returns: []
returns_optional: false
error_type: ""
params:
  - name: sketch_image
    desc: "Nombre del archivo del boceto dentro de la carpeta input/ del servidor ComfyUI (lo carga el nodo LoadImage). Subelo antes con POST /upload/image o copialo a ~/ComfyUI/input/. Puede ser un boceto a lapiz, un lineart limpio o un garabato; el preprocesador lo normaliza a un mapa de lineas. No puede estar vacio."
  - name: subject
    desc: "Descripcion de QUE representa el boceto, para que la IA sepa que pintar sobre la forma (ej. 'armored knight', 'treasure chest', 'fire goblin'). Es el prompt positivo: NO inventa la silueta (eso lo fija el ControlNet), pone material, color y acabado. No puede estar vacio."
  - name: control_type
    desc: "Preprocesador y modelo ControlNet a usar. 'lineart' (default, LineArtPreprocessor) para dibujos a lapiz/tinta; 'scribble' (ScribblePreprocessor) para garabatos sueltos con mas libertad a la IA; 'canny' (CannyEdgePreprocessor) para bocetos con bordes nitidos y es el unico cuyo modelo CN dedicado esta instalado en el server 8GB actual (ver Gotchas). keyword-only."
  - name: checkpoint
    desc: "Checkpoint del servidor. 'IMG_dreamshaper_8.safetensors' (SD1.5, holgado en 8GB lowvram) por defecto. keyword-only."
  - name: style
    desc: "Descriptor de estilo que mantiene coherentes los sprites de un set (ej. 'game asset, clean, centered', 'dark fantasy creature', 'pixel art'). Mismo style + checkpoint + (lora) para todos los sprites del mismo juego. keyword-only."
  - name: strength
    desc: "Fuerza del ControlNet (cuanto respeta el boceto). 0.0 = ignora el dibujo (txt2img puro); 1.0 = se ciñe estrictamente a las lineas; 0.8 (recomendado) deja algo de margen a la IA. Se clampa a [0.0, 2.0]. keyword-only."
  - name: size
    desc: "Lado en px del sprite generado (EmptyLatentImage size x size). El preprocesador normaliza el boceto a esta resolucion. 512 por defecto (SD1.5). keyword-only."
  - name: seed
    desc: "Semilla del KSampler. keyword-only."
  - name: lora
    desc: "LoRA de estilo opcional en models/loras (ej. 'SD15_dark_fantasy.safetensors'). None = sin LoRA. keyword-only."
  - name: lora_strength
    desc: "Fuerza del LoRA sobre model y clip. Se clampa a [0.0, 2.0]. keyword-only."
  - name: preprocess
    desc: "Si True (default), inserta el preprocesador del control_type entre el boceto y el ControlNet (recomendado: el boceto crudo casi nunca es un mapa de control limpio). Si False, el boceto se pasa DIRECTO al ControlNet como mapa de lineas (util solo cuando sketch_image YA es un lineart blanco-sobre-negro limpio). keyword-only."
  - name: controlnet_name
    desc: "Override explicito del modelo ControlNet en models/controlnet. None = usa el modelo emparejado al control_type. Pasa 'control_v11p_sd15_canny_fp16.safetensors' aqui para degradar lineart/scribble al modelo canny disponible en el server actual (ver Gotchas). keyword-only."
  - name: negative
    desc: "Prompt negativo. None usa el negativo por defecto pensado para sprites (una figura, fondo limpio, sin texto). keyword-only."
  - name: steps
    desc: "Pasos de sampling del KSampler. keyword-only."
  - name: cfg
    desc: "Classifier-free guidance scale. keyword-only."
  - name: sampler_name
    desc: "Nombre del sampler (ej. 'dpmpp_2m', 'euler'). keyword-only."
  - name: scheduler
    desc: "Scheduler del sampler (ej. 'karras', 'normal'). keyword-only."
  - name: filename_prefix
    desc: "Prefijo del archivo de salida en SaveImage. keyword-only."
output: "dict en API format listo para comfyui_submit_workflow: txt2img base (CheckpointLoaderSimple '4', EmptyLatentImage '5', CLIPTextEncode '6'/'7', KSampler '3' denoise 1.0, VAEDecode '8', SaveImage '9') + rama ControlNet (LoadImage del boceto -> [Preprocessor del control_type si preprocess] -> ControlNetApply -> KSampler.positive, con ControlNetLoader del modelo CN) + LoraLoader si lora. Es UN sprite; varios objetos del mismo set -> llamar por subject/sketch_image con el mismo style/checkpoint/(lora)."
tested: true
tests: ["estructura txt2img + ControlNet (EmptyLatentImage, ControlNetLoader/Apply)", "lineart: preprocesador + modelo por defecto, ControlNetApply consume el mapa de lineas", "canny: preprocesador + modelo", "preprocess=False pasa el boceto directo al ControlNet", "controlnet_name override + strength reflejado", "strength se clampa a [0,2]", "lora opcional", "errores: sketch/subject vacios, control_type invalido", "determinismo"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_sprite_from_sketch_workflow.py"
file_path: python/functions/ml/comfyui_build_sprite_from_sketch_workflow.py
---

Construye el dict (API format) del workflow ComfyUI que **pinta un sprite a partir del
dibujo del dev**, guiado por ControlNet. Es el **tercer eje** del catálogo `gamedev-2d`:

| Eje | Builders | Punto de partida |
|---|---|---|
| **txt2img** | `enemy_creature`, `item_icon`, `ui_hud`... | TEXTO en blanco — la IA inventa la forma desde ruido |
| **img2img** | `asset_variant` | una imagen YA pintada — la reescribe (material/tier) conservando composición |
| **sketch→ControlNet** | **este builder** | el DIBUJO TOSCO del dev — lo pinta conservando la silueta dibujada |

El dolor real que cubre: el dev hace un boceto/lineart de un personaje u objeto y quiere que
la IA lo pinte en estilo de juego **manteniendo la forma**. El dev marca la silueta; la IA
pone material, color y acabado.

Mecanismo: es `txt2img` (arranca de ruido, `EmptyLatentImage`, `denoise 1.0`) pero su
condicionamiento positivo pasa por un `ControlNetApply` atado al mapa de líneas extraído del
boceto. El resultado tiene la **silueta y estructura del dibujo**, pintadas con el prompt de
`subject` + `style`.

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from ml.comfyui_build_sprite_from_sketch_workflow import comfyui_build_sprite_from_sketch_workflow

# Boceto 'knight_sketch.png' ya subido al input/ del servidor (POST /upload/image).
wf = comfyui_build_sprite_from_sketch_workflow(
    "knight_sketch.png",
    "armored knight, fantasy hero",
    control_type="canny",                 # canny = preproc + modelo, todo instalado hoy
    style="game asset, clean, centered, painted",
    strength=0.85,
    size=512,
    seed=123,
)
# wf -> comfyui_submit_workflow(wf) -> comfyui_wait_result(prompt_id)
# El sprite resultante conserva la silueta del boceto, repintada en estilo de juego.
```

Probado end-to-end (27/06/2026): boceto del goblin → `CannyEdgePreprocessor` → ControlNet
canny → sprite que respeta pose, orejas, hombrera, lanza dentada y espada del dibujo.
`prompt_id ea6fc372-0467-4b4e-9927-069419633eab`, salida `sprite_from_sketch_test_00001_.png`.

## Cuando usarla

- Cuando el dev TIENE un dibujo (boceto, lineart, garabato) y quiere que la IA lo pinte
  respetando la forma — no inventar desde cero (`txt2img`) ni reescribir un asset ya pintado
  (`img2img` / `asset_variant`).
- Para iterar diseño: dibujas la silueta a mano, la IA prueba 5 acabados distintos (varía
  `seed`/`style`/`lora`) sobre la MISMA forma.
- Elige `control_type`: `lineart` para dibujos a lápiz/tinta, `scribble` para garabatos sueltos
  (más libertad), `canny` para bocetos con bordes nítidos (y único 100% instalado hoy).
- Sube el set de un juego usando el mismo `style` + `checkpoint` + (`lora`) para coherencia.

## Gotchas

- **El server 8GB lowvram actual SOLO tiene los modelos ControlNet SD1.5 `canny` / `depth` /
  `openpose`.** Los modelos dedicados `control_v11p_sd15_lineart_fp16` y
  `control_v11p_sd15_scribble_fp16` NO están instalados (pendiente humano descargarlos a
  `~/ComfyUI/models/controlnet/`). Mientras tanto, para `control_type="lineart"`/`"scribble"`:
  o pasa `controlnet_name="control_v11p_sd15_canny_fp16.safetensors"` como override (el modelo
  canny tolera mapas de líneas de cualquier preprocesador, con algo menos de fidelidad que el
  dedicado), o usa `control_type="canny"` que funciona out-of-the-box. Los **preprocesadores**
  (LineArt/Scribble/Canny) SÍ están todos instalados (custom node `comfyui_controlnet_aux`).
- La función es **pura**: NO consulta el servidor ni valida que `sketch_image`, `checkpoint`,
  `controlnet_name` o `lora` existan. Si el modelo CN falta, ComfyUI rechaza el workflow con
  HTTP 400 al enviarlo (`comfyui_submit_workflow` propaga el detalle por nodo). Valida con
  `/object_info` antes de enviar a un server desconocido.
- `preprocess=False` solo tiene sentido si `sketch_image` YA es un mapa de líneas limpio
  (blanco-sobre-negro). Con un boceto a lápiz crudo y `preprocess=False`, el ControlNet recibe
  una foto, no líneas, y el resultado se degrada.
- `strength` alto (~1.0) se ciñe tanto al boceto que puede arrastrar artefactos del dibujo;
  ~0.8 deja a la IA limpiar. `strength=0` desactiva el ControlNet (vuelve a txt2img puro).
- Es `txt2img` guiado (genera desde ruido respetando líneas), NO `img2img`: el sprite NO hereda
  los colores del boceto, solo su FORMA. Para conservar también los colores de una imagen ya
  pintada, usa `comfyui_build_asset_variant_workflow` (img2img).
