# Video Wan2.1

Genera videos de alta calidad con los modelos de texto-a-video y imagen-a-video Wan2.1 de Alibaba en GPUs de CLORE.AI.

{% hint style="success" %}
Todos los ejemplos se pueden ejecutar en servidores GPU alquilados a través de [CLORE.AI Marketplace](https://clore.ai/marketplace).
{% endhint %}

## ¿Por qué Wan2.1?

* **Alta calidad** - Generación de video de vanguardia
* **Múltiples modos** - Texto a video, imagen a video
* **Varias tamaños** - De 1.3B a 14B parámetros
* **Videos largos** - Hasta 81 fotogramas
* **Pesos abiertos** - Licencia Apache 2.0

## Variantes de modelo

| Modelo          | Parámetros | VRAM | Resolución | Fotogramas |
| --------------- | ---------- | ---- | ---------- | ---------- |
| Wan2.1-T2V-1.3B | 1.3B       | 8GB  | 480p       | 81         |
| Wan2.1-T2V-14B  | 14B        | 24GB | 720p       | 81         |
| Wan2.1-I2V-14B  | 14B        | 24GB | 720p       | 81         |

## Despliegue rápido en CLORE.AI

**Imagen Docker:**

```
pytorch/pytorch:2.5.1-cuda12.4-cudnn9-devel
```

**Puertos:**

```
22/tcp
7860/http
```

**Comando:**

```bash
pip install diffusers transformers accelerate gradio && \
python -c "
import gradio as gr
import torch
from diffusers import WanPipeline
from diffusers.utils import export_to_video

pipe = WanPipeline.from_pretrained('alibaba-pai/Wan2.1-T2V-1.3B', torch_dtype=torch.float16)
pipe.to('cuda')
pipe.enable_model_cpu_offload()

def generate(prompt, steps, frames, seed):
    generator = torch.Generator('cuda').manual_seed(seed) if seed > 0 else None
    output = pipe(prompt, num_frames=frames, num_inference_steps=steps, generator=generator)
    export_to_video(output.frames[0], 'output.mp4', fps=16)
    return 'output.mp4'

gr.Interface(
    fn=generate,
    inputs=[
        gr.Textbox(label='Prompt'),
        gr.Slider(20, 100, value=50, label='Steps'),
        gr.Slider(16, 81, value=49, step=8, label='Frames'),
        gr.Number(value=-1, label='Seed')
    ],
    outputs=gr.Video(),
    title='Wan2.1 - Text to Video'
).launch(server_name='0.0.0.0', server_port=7860)
"
```

## Accediendo a tu servicio

Después del despliegue, encuentra tu `http_pub` URL en **Mis Pedidos**:

1. Ir a **Mis Pedidos** página
2. Haz clic en tu pedido
3. Encuentra la `http_pub` URL (por ejemplo, `abc123.clorecloud.net`)

Usa `https://TU_HTTP_PUB_URL` en lugar de `localhost` en los ejemplos abajo.

## Requisitos de hardware

| Modelo   | GPU mínima    | Recomendado   | Óptimo    |
| -------- | ------------- | ------------- | --------- |
| 1.3B T2V | RTX 3070 8GB  | RTX 3090 24GB | RTX 4090  |
| 14B T2V  | RTX 4090 24GB | A100 40GB     | A100 80GB |
| 14B I2V  | RTX 4090 24GB | A100 40GB     | A100 80GB |

## Instalación

```bash
pip install diffusers transformers accelerate torch
```

## Texto a Video

### Uso básico (1.3B)

```python
import torch
from diffusers import WanPipeline
from diffusers.utils import export_to_video

pipe = WanPipeline.from_pretrained(
    "alibaba-pai/Wan2.1-T2V-1.3B",
    torch_dtype=torch.float16
)
pipe.to("cuda")
pipe.enable_model_cpu_offload()

prompt = "Un gato jugando con una pelota en un jardín soleado"

output = pipe(
    prompt=prompt,
    num_frames=49,
    num_inference_steps=50,
    guidance_scale=7.0
)

export_to_video(output.frames[0], "cat_video.mp4", fps=16)
```

### Alta calidad (14B)

```python
import torch
from diffusers import WanPipeline
from diffusers.utils import export_to_video

pipe = WanPipeline.from_pretrained(
    "alibaba-pai/Wan2.1-T2V-14B",
    torch_dtype=torch.float16
)
pipe.to("cuda")
pipe.enable_model_cpu_offload()
pipe.enable_vae_tiling()

prompt = "Toma cinematográfica de un dragón volando sobre montañas al atardecer, 4K, detallado"

output = pipe(
    prompt=prompt,
    negative_prompt="borroso, baja calidad, distorsionado",
    num_frames=81,
    height=720,
    width=1280,
    num_inference_steps=50,
    guidance_scale=7.0
)

export_to_video(output.frames[0], "dragon.mp4", fps=24)
```

## Imagen a video

### Animar una imagen

```python
import torch
from diffusers import WanI2VPipeline
from diffusers.utils import load_image, export_to_video

pipe = WanI2VPipeline.from_pretrained(
    "alibaba-pai/Wan2.1-I2V-14B",
    torch_dtype=torch.float16
)
pipe.to("cuda")
pipe.enable_model_cpu_offload()

# Cargar imagen de entrada
image = load_image("input.jpg")

prompt = "La persona en la imagen comienza a caminar hacia adelante"

output = pipe(
    prompt=prompt,
    image=image,
    num_frames=49,
    num_inference_steps=50,
    guidance_scale=7.0
)

export_to_video(output.frames[0], "animated.mp4", fps=16)
```

## Imagen a Video con Wan2.1-I2V-14B

{% hint style="info" %}
Wan2.1-I2V-14B anima una imagen estática usando un prompt de texto para guiar el movimiento. Requiere **24GB de VRAM** (Se recomienda RTX 4090 o A100 40GB).
{% endhint %}

### Detalles del modelo

| Propiedad          | Valor                            |
| ------------------ | -------------------------------- |
| ID del modelo      | `Wan-AI/Wan2.1-I2V-14B-480P`     |
| Parámetros         | 14 mil millones                  |
| VRAM requerida     | **24GB**                         |
| Resolución máxima  | 480p (854×480) o 720p (1280×720) |
| Máx. de fotogramas | 81                               |
| Licencia           | Apache 2.0                       |

### Requisitos de hardware

| GPU       | VRAM | Estado          |
| --------- | ---- | --------------- |
| RTX 4090  | 24GB | ✅ Recomendado   |
| RTX 3090  | 24GB | ✅ Compatible    |
| A100 40GB | 40GB | ✅ Óptimo        |
| A100 80GB | 80GB | ✅ Mejor calidad |
| RTX 3080  | 10GB | ❌ Insuficiente  |

### Script CLI rápido

Guardar como `generate_i2v.py` y ejecuta:

```bash
python generate_i2v.py --model Wan-AI/Wan2.1-I2V-14B-480P --image input.jpg --prompt "la cámara se aleja lentamente"
```

### generate\_i2v.py — Script completo

```python
#!/usr/bin/env python3
"""
Script CLI de Wan2.1 Image-to-Video.
Uso: python generate_i2v.py --model Wan-AI/Wan2.1-I2V-14B-480P \
           --image input.jpg --prompt "la cámara se aleja lentamente"
"""

import argparse
import os
import sys
import torch
from diffusers import WanImageToVideoPipeline
from diffusers.utils import load_image, export_to_video
from PIL import Image


def parse_args():
    parser = argparse.ArgumentParser(description="Generador Wan2.1 Image-to-Video")
    parser.add_argument(
        "--model",
        type=str,
        default="Wan-AI/Wan2.1-I2V-14B-480P",
        help="ID del modelo en Hugging Face (predeterminado: Wan-AI/Wan2.1-I2V-14B-480P)",
    )
    parser.add_argument(
        "--image",
        type=str,
        required=True,
        help="Ruta a la imagen de entrada (JPEG o PNG)",
    )
    parser.add_argument(
        "--prompt",
        type=str,
        required=True,
        help='Prompt de texto que describe el movimiento deseado (por ejemplo "la cámara se aleja lentamente")',
    )
    parser.add_argument(
        "--negative-prompt",
        type=str,
        default="borroso, baja calidad, distorsionado, movimiento entrecortado, artefactos",
        help="Prompt negativo para evitar artefactos no deseados",
    )
    parser.add_argument(
        "--frames",
        type=int,
        default=49,
        help="Número de fotogramas de video a generar (predeterminado: 49, máx.: 81)",
    )
    parser.add_argument(
        "--steps",
        type=int,
        default=50,
        help="Número de pasos de difusión (predeterminado: 50)",
    )
    parser.add_argument(
        "--guidance",
        type=float,
        default=7.0,
        help="Escala de guidance sin clasificador (predeterminado: 7.0)",
    )
    parser.add_argument(
        "--seed",
        type=int,
        default=-1,
        help="Semilla aleatoria para reproducibilidad (-1 = aleatorio)",
    )
    parser.add_argument(
        "--fps",
        type=int,
        default=16,
        help="FPS del video de salida (predeterminado: 16)",
    )
    parser.add_argument(
        "--output",
        type=str,
        default="output_i2v.mp4",
        help="Ruta del archivo de video de salida (predeterminado: output_i2v.mp4)",
    )
    parser.add_argument(
        "--height",
        type=int,
        default=480,
        help="Altura del video de salida en píxeles (predeterminado: 480)",
    )
    parser.add_argument(
        "--width",
        type=int,
        default=854,
        help="Anchura del video de salida en píxeles (predeterminado: 854)",
    )
    parser.add_argument(
        "--cpu-offload",
        action="store_true",
        default=True,
        help="Habilitar descarga del modelo a CPU para ahorrar VRAM (predeterminado: True)",
    )
    parser.add_argument(
        "--vae-tiling",
        action="store_true",
        default=False,
        help="Habilitar tiling de VAE para salidas de alta resolución",
    )
    return parser.parse_args()


def load_and_resize_image(image_path: str, width: int, height: int) -> Image.Image:
    """Cargar imagen desde la ruta y redimensionarla a las dimensiones objetivo."""
    if not os.path.exists(image_path):
        print(f"[ERROR] Imagen no encontrada: {image_path}", file=sys.stderr)
        sys.exit(1)

    img = Image.open(image_path).convert("RGB")
    original_size = img.size
    img = img.resize((width, height), Image.LANCZOS)
    print(f"[INFO] Imagen cargada: {image_path} ({original_size[0]}x{original_size[1]}) → redimensionada a {width}x{height}")
    return img


def load_pipeline(model_id: str, cpu_offload: bool, vae_tiling: bool):
    """Cargar el pipeline I2V de Wan con optimizaciones de memoria."""
    print(f"[INFO] Cargando modelo: {model_id}")
    print(f"[INFO] CUDA disponible: {torch.cuda.is_available()}")
    if torch.cuda.is_available():
        vram_gb = torch.cuda.get_device_properties(0).total_memory / 1e9
        print(f"[INFO] GPU: {torch.cuda.get_device_name(0)} ({vram_gb:.1f} GB de VRAM)")
        if vram_gb < 23:
            print("[WARN] Detectado menos de 24GB de VRAM — habilita --cpu-offload o usa el modelo 1.3B")

    pipe = WanImageToVideoPipeline.from_pretrained(
        model_id,
        torch_dtype=torch.float16,
    )

    if cpu_offload:
        print("[INFO] Habilitando descarga del modelo a CPU")
        pipe.enable_model_cpu_offload()
    else:
        pipe.to("cuda")

    if vae_tiling:
        print("[INFO] Habilitando tiling de VAE para generación en alta resolución")
        pipe.enable_vae_tiling()

    return pipe


def generate_video(pipe, args) -> None:
    """Ejecuta el pipeline I2V y guarda el video de salida."""
    image = load_and_resize_image(args.image, args.width, args.height)

    generator = None
    if args.seed >= 0:
        generator = torch.Generator("cuda").manual_seed(args.seed)
        print(f"[INFO] Usando semilla: {args.seed}")
    else:
        print("[INFO] Usando semilla aleatoria")

    print(f"[INFO] Generando {args.frames} fotogramas a {args.width}x{args.height}")
    print(f"[INFO] Pasos: {args.steps} | Guidance: {args.guidance} | FPS: {args.fps}")
    print(f"[INFO] Prompt: {args.prompt}")

    output = pipe(
        prompt=args.prompt,
        negative_prompt=args.negative_prompt,
        image=image,
        num_frames=args.frames,
        height=args.height,
        width=args.width,
        num_inference_steps=args.steps,
        guidance_scale=args.guidance,
        generator=generator,
    )

    export_to_video(output.frames[0], args.output, fps=args.fps)
    print(f"[INFO] Video guardado en: {os.path.abspath(args.output)}")
    duration = args.frames / args.fps
    print(f"[INFO] Duración: {duration:.1f}s a {args.fps}fps ({args.frames} fotogramas)")


def main():
    args = parse_args()

    if not torch.cuda.is_available():
        print("[ERROR] GPU CUDA no encontrada. Wan2.1-I2V-14B requiere una GPU compatible con CUDA.", file=sys.stderr)
        sys.exit(1)

    pipe = load_pipeline(args.model, args.cpu_offload, args.vae_tiling)
    generate_video(pipe, args)
    print("[DONE] ¡Generación de imagen a video completada!")


if __name__ == "__main__":
    main()
```

### Pipeline I2V avanzado (API de Python)

```python
import torch
from diffusers import WanImageToVideoPipeline
from diffusers.utils import load_image, export_to_video
from PIL import Image

# ── Cargar pipeline ──────────────────────────────────────────────────────────────
pipe = WanImageToVideoPipeline.from_pretrained(
    "Wan-AI/Wan2.1-I2V-14B-480P",
    torch_dtype=torch.float16,
)
pipe.enable_model_cpu_offload()   # mantiene la VRAM por debajo de 24GB
pipe.enable_vae_tiling()          # opcional: ayuda para 720p

# ── Cargar y preparar imagen de entrada ────────────────────────────────────────────
image = load_image("input.jpg").resize((854, 480))

# ── Generar ──────────────────────────────────────────────────────────────────────
prompt = "la cámara se aleja lentamente, revelando todo el paisaje"
negative_prompt = "borroso, baja calidad, distorsionado, parpadeo, artefactos"

generator = torch.Generator("cuda").manual_seed(42)

output = pipe(
    prompt=prompt,
    negative_prompt=negative_prompt,
    image=image,
    num_frames=49,          # ~3 segundos a 16fps
    height=480,
    width=854,
    num_inference_steps=50,
    guidance_scale=7.5,
    generator=generator,
)

export_to_video(output.frames[0], "i2v_output.mp4", fps=16)
print("Guardado: i2v_output.mp4")
```

### Consejos de prompt para I2V

| Objetivo                | Ejemplo de prompt                                                |
| ----------------------- | ---------------------------------------------------------------- |
| Movimiento de cámara    | `"la cámara se aleja lentamente del sujeto"`                     |
| Efecto de paralaje      | `"movimiento de paralaje sutil, cambio de profundidad de campo"` |
| Animación de personajes | `"la figura gira la cabeza y sonríe"`                            |
| Animación de naturaleza | `"las hojas se mecen con una brisa suave, la luz cambia"`        |
| Movimiento abstracto    | `"colores que giran y se mezclan, movimiento fluido"`            |

### Consejos de memoria para I2V (GPUs de 24GB)

```python
# Obligatorio en GPUs de 24GB
pipe.enable_model_cpu_offload()

# Opcional: reduce el pico de VRAM ~10%
pipe.enable_vae_tiling()
pipe.enable_vae_slicing()

# Limpiar entre ejecuciones
import gc
gc.collect()
torch.cuda.empty_cache()
```

## Ejemplos de prompts

### Naturaleza y paisajes

```python
prompts = [
    "Time-lapse de nubes moviéndose sobre picos montañosos, iluminación dramática",
    "Olas del océano rompiendo en las rocas, cámara lenta, cinematográfico",
    "Auroras boreales danzando en el cielo nocturno, colores vibrantes",
    "Bosque en otoño con hojas cayendo, atmósfera tranquila"
]
```

### Animales y personajes

```python
prompts = [
    "Un golden retriever corriendo por un campo de flores",
    "Una mariposa emergiendo de su capullo, toma macro",
    "Guerrero samurái desenvainando su espada, iluminación dramática",
    "Robot caminando por calles de una ciudad futurista"
]
```

### Abstracto y artístico

```python
prompts = [
    "Pintura colorida girando en el agua, arte abstracto",
    "Formas geométricas transformándose y morfándose, colores neón",
    "Gotas de tinta expandiéndose en leche, fotografía macro"
]
```

## Ajustes avanzados

### Calidad vs Velocidad

```python
# Vista previa rápida
output = pipe(
    prompt=prompt,
    num_frames=17,
    num_inference_steps=25,
    guidance_scale=5.0
)

# Equilibrado
output = pipe(
    prompt=prompt,
    num_frames=49,
    num_inference_steps=50,
    guidance_scale=7.0
)

# Máxima calidad
output = pipe(
    prompt=prompt,
    num_frames=81,
    num_inference_steps=100,
    guidance_scale=7.5
)
```

### Opciones de resolución

```python
# 480p (modelo 1.3B)
output = pipe(prompt, height=480, width=854, num_frames=49)

# 720p (modelo 14B)
output = pipe(prompt, height=720, width=1280, num_frames=49)

# 1080p (modelo 14B, alta VRAM)
output = pipe(prompt, height=1080, width=1920, num_frames=33)
```

## Generación por lotes

```python
import os
import torch
from diffusers import WanPipeline
from diffusers.utils import export_to_video

pipe = WanPipeline.from_pretrained("alibaba-pai/Wan2.1-T2V-1.3B", torch_dtype=torch.float16)
pipe.to("cuda")
pipe.enable_model_cpu_offload()

prompts = [
    "Un cohete despegando hacia el espacio",
    "Peces nadando en un arrecife de coral",
    "Lluvia cayendo en una calle de la ciudad por la noche"
]

output_dir = "./videos"
os.makedirs(output_dir, exist_ok=True)

for i, prompt in enumerate(prompts):
    print(f"Generando {i+1}/{len(prompts)}: {prompt[:40]}...")

    output = pipe(
        prompt=prompt,
        num_frames=49,
        num_inference_steps=50
    )

    export_to_video(output.frames[0], f"{output_dir}/video_{i:03d}.mp4", fps=16)
    torch.cuda.empty_cache()
```

## Interfaz Gradio

```python
import gradio as gr
import torch
from diffusers import WanPipeline
from diffusers.utils import export_to_video
import tempfile

pipe = WanPipeline.from_pretrained("alibaba-pai/Wan2.1-T2V-1.3B", torch_dtype=torch.float16)
pipe.to("cuda")
pipe.enable_model_cpu_offload()

def generate_video(prompt, negative_prompt, frames, steps, guidance, seed):
    generator = torch.Generator("cuda").manual_seed(seed) if seed > 0 else None

    output = pipe(
        prompt=prompt,
        negative_prompt=negative_prompt,
        num_frames=frames,
        num_inference_steps=steps,
        guidance_scale=guidance,
        generator=generator
    )

    with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as f:
        export_to_video(output.frames[0], f.name, fps=16)
        return f.name

demo = gr.Interface(
    fn=generate_video,
    inputs=[
        gr.Textbox(label="Prompt", lines=2),
        gr.Textbox(label="Negative Prompt", value="borroso, baja calidad"),
        gr.Slider(17, 81, value=49, step=8, label="Frames"),
        gr.Slider(20, 100, value=50, step=5, label="Steps"),
        gr.Slider(3, 12, value=7, step=0.5, label="Guidance"),
        gr.Number(value=-1, label="Semilla")
    ],
    outputs=gr.Video(label="Video generado"),
    title="Wan2.1 - Generación de texto a video",
    description="Genera videos a partir de prompts de texto. Ejecutándose en CLORE.AI."
)

demo.launch(server_name="0.0.0.0", server_port=7860)
```

## Optimización de memoria

```python
# Habilitar todas las optimizaciones
pipe.enable_model_cpu_offload()
pipe.enable_vae_tiling()
pipe.enable_vae_slicing()

# Para VRAM muy baja
pipe.enable_sequential_cpu_offload()

# Limpiar caché entre generaciones
torch.cuda.empty_cache()
```

## Rendimiento

| Modelo | Resolución | Fotogramas | GPU       | Tiempo    |
| ------ | ---------- | ---------- | --------- | --------- |
| 1.3B   | 480p       | 49         | RTX 4090  | \~2 min   |
| 1.3B   | 480p       | 49         | A100 40GB | \~1.5 min |
| 14B    | 720p       | 49         | A100 40GB | \~5 min   |
| 14B    | 720p       | 81         | A100 80GB | \~8 min   |

## Estimación de costos

Tarifas típicas del mercado de CLORE.AI:

| GPU           | Tarifa por hora | \~49 videos de fotogramas/hora |
| ------------- | --------------- | ------------------------------ |
| RTX 3090 24GB | \~$0.06         | \~20 (1.3B)                    |
| RTX 4090 24GB | \~$0.10         | \~30 (1.3B)                    |
| A100 40GB     | \~$0.17         | \~40 (1.3B) / \~12 (14B)       |
| A100 80GB     | \~$0.25         | \~8 (14B alta resolución)      |

*Los precios varían. Consulta* [*CLORE.AI Marketplace*](https://clore.ai/marketplace) *para las tarifas actuales.*

## Solución de problemas

### Memoria insuficiente

```python
# Use un modelo más pequeño
pipe = WanPipeline.from_pretrained("alibaba-pai/Wan2.1-T2V-1.3B")

# Habilitar todas las optimizaciones
pipe.enable_model_cpu_offload()
pipe.enable_vae_tiling()

# Reducir fotogramas
output = pipe(prompt, num_frames=17)

# Reducir resolución
output = pipe(prompt, height=480, width=854)
```

### Calidad pobre

* Aumentar pasos (75-100)
* Escribe prompts más detallados
* Usa prompts negativos
* Prueba el modelo 14B para mejor calidad

### Video demasiado corto

* Aumente `num_frames` (máx 81)
* Usa interpolación RIFE para interpolación de fotogramas
* Encadena múltiples generaciones

### Artefactos/Parpadeo

* Aumenta la escala de guidance
* Usa semilla fija para consistencia
* Post-procesa con estabilización de video

## Wan2.1 vs Otros

| Función            | Wan2.1     | Hunyuan   | SVD    | CogVideoX |
| ------------------ | ---------- | --------- | ------ | --------- |
| Calidad            | Excelente  | Excelente | Bueno  | Genial    |
| Velocidad          | Rápido     | Medio     | Rápido | Lento     |
| Máx. de fotogramas | 81         | 129       | 25     | 49        |
| Resolución         | 720p       | 720p      | 576p   | 720p      |
| Soporte I2V        | Sí         | Sí        | Sí     | Sí        |
| Licencia           | Apache 2.0 | Abrir     | Abrir  | Abrir     |

**Usa Wan2.1 cuando:**

* Necesites generación de video de código abierto
* Quieras velocidad de generación rápida
* Se requiera licencia Apache 2.0
* Se necesite un equilibrio calidad/velocidad

## Próximos pasos

* [Hunyuan Video](https://docs.clore.ai/guides/guides_v2-es/generacion-de-video/hunyuan-video) - Alternativa T2V
* [OpenSora](https://docs.clore.ai/guides/guides_v2-es/generacion-de-video/opensora) - Alternativa Open Sora
* [Stable Video Diffusion](https://docs.clore.ai/guides/guides_v2-es/generacion-de-video/stable-video-diffusion) - Animación de imagen
* [Interpolación RIFE](https://docs.clore.ai/guides/guides_v2-es/procesamiento-de-video/rife-interpolation) - Interpolación de fotogramas
