---
name: render_openpose_walk_skeletons
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: impure
signature: "def render_openpose_walk_skeletons(out_dir: str, *, frames: int = 4, width: int = 512, height: int = 768, facing: str = 'right', line_width: int = 4, point_radius: int = 6, filename_prefix: str = 'walk_pose') -> dict"
description: "Dibuja con PIL una secuencia de N esqueletos OpenPose COCO-18 (18 keypoints, 17 limbs, colores canonicos) de un ciclo de caminar lateral, una fase del paso por frame, sobre fondo negro, y los guarda como PNG. Son la ENTRADA fija del ControlNet OpenPose (control_v11p_sd15_openpose_fp16) para animar un personaje frame-by-frame: el esqueleto NO lo genera la IA, lo aportas dibujado. Para frames=4 produce las 4 fases canonicas (contact-izq, passing, contact-der, passing); para mas frames muestrea el ciclo parametrico continuo. Piernas en oposicion a los brazos + rebote vertical del cuerpo (walk cycle de manual de animacion). facing='right'|'left' espeja en X. Impura: escribe N PNGs. Devuelve {ok, skeleton_paths, frames, width, height, error}."
tags: [gamedev-2d, comfyui, controlnet, openpose, pose, animation]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
params:
  - name: out_dir
    desc: "directorio destino de los PNG; se crea si no existe. None lanza ValueError (unico caso que lanza)."
  - name: frames
    desc: "numero de fases del ciclo a renderizar (default 4 = las 4 fases canonicas contact/passing/contact/passing); con mas frames se muestrea el ciclo parametrico de forma continua interpolando las fases intermedias down/up. keyword-only."
  - name: width
    desc: "ancho en pixeles de cada PNG (default 512, el tamaño nativo de SD1.5). keyword-only."
  - name: height
    desc: "alto en pixeles de cada PNG (default 768, retrato para personaje de cuerpo entero). keyword-only."
  - name: facing
    desc: "'right' (el personaje mira a +x) o 'left' (espeja el esqueleto en X). Cualquier otro valor devuelve ok=False con error. keyword-only."
  - name: line_width
    desc: "grosor en pixeles de las lineas de los limbs (default 4). keyword-only."
  - name: point_radius
    desc: "radio en pixeles de los circulos rellenos de cada keypoint (default 6). keyword-only."
  - name: filename_prefix
    desc: "prefijo de los archivos; se nombran '<prefix>_<NN>.png' con NN de dos digitos en orden de fase (default 'walk_pose'). keyword-only."
output: "dict con ok (bool, True si todos los PNG se generaron), skeleton_paths (list[str], rutas de los PNG en orden de fase), frames (int, frames generados), width (int), height (int), error (str, vacio si OK)."
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/ml/render_openpose_walk_skeletons.py"
---

## Ejemplo

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

res = render_openpose_walk_skeletons("/tmp/walk_skeletons_demo", frames=4)
# {'ok': True,
#  'skeleton_paths': ['/tmp/walk_skeletons_demo/walk_pose_00.png', ..._03.png],
#  'frames': 4, 'width': 512, 'height': 768, 'error': ''}

# 8 fases mirando a la izquierda, lineas/puntos mas finos:
res8 = render_openpose_walk_skeletons(
    "/tmp/walk_poses_left", frames=8, facing="left",
    line_width=3, point_radius=5,
)
```

Los PNG resultantes se conectan luego con `comfyui_build_controlnet_workflow`
(uno por frame, `control_net_name="control_v11p_sd15_openpose_fp16.safetensors"`)
para generar el personaje animado fotograma a fotograma.

## Cuando usarla

Usala cuando vayas a animar un sprite/personaje 2D con ComfyUI + ControlNet
OpenPose y necesites el insumo que la IA NO genera: la pose-map del esqueleto.
Llamala ANTES de montar el workflow ControlNet — produce las N pose-maps del
walk cycle (el caso mas comun de animacion de personaje) que el modelo seguira
frame a frame. Tambien sirve como base para otras acciones ciclicas si ajustas
las fases. Si necesitas una pose suelta (idle, ataque) en vez de un ciclo,
extrae el patron a una funcion hermana — esta es especifica de caminar.

## Gotchas

- Escribe N PNGs en disco (impura): si `out_dir` no es escribible devuelve
  `ok=False` con el error; si `out_dir` es `None` lanza `ValueError` (unico caso
  que lanza — el resto de fallos se capturan en `error`).
- El orden de los 18 keypoints es COCO-18 EXACTO (0 nose ... 17 left_ear) y los
  colores son los canonicos de OpenPose/controlnet_aux. NO cambies el orden ni la
  paleta: el preprocesador/ControlNet identifica las articulaciones por color y
  posicion; alterarlos degrada o rompe el guiado de pose.
- Es un esqueleto sintetico parametrico, no una captura real: las proporciones
  son humanas estandar y la vista es estrictamente lateral. Para vistas 3/4 o
  proporciones no humanas (chibi, criaturas) habria que reparametrizar.
- Fondo NEGRO solido (RGB 0,0,0) por diseño — es lo que el ControlNet OpenPose
  espera como lienzo. No lo compongas sobre otra imagen.
- `frames=4` da exactamente las 4 fases canonicas; valores que no dividan bien el
  ciclo (p.ej. 3, 5) siguen muestreando t=i/frames de forma uniforme y producen
  fases validas pero no necesariamente las "de manual". Para animacion fluida usa
  multiplos de 4 (8, 12, 16).
- Necesita Pillow (PIL); si no esta instalado devuelve `ok=False` con error en vez
  de lanzar.
