Genera videos con el modelo Wan2.1 de Alibaba en GPUs de Clore.ai
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.
Todos los ejemplos se pueden ejecutar en servidores GPU alquilados a través de CLORE.AI Marketplace.
¿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:
Comando:
Accediendo a tu servicio
Después del despliegue, encuentra tu http_pub URL en Mis Pedidos:
Ir a Mis Pedidos página
Haz clic en tu pedido
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
Texto a Video
Uso básico (1.3B)
Alta calidad (14B)
Imagen a video
Animar una imagen
Imagen a Video con Wan2.1-I2V-14B
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).
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:
generate_i2v.py — Script completo
Pipeline I2V avanzado (API de Python)
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"
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)
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)
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)
python generate_i2v.py --model Wan-AI/Wan2.1-I2V-14B-480P --image input.jpg --prompt "la cámara se aleja lentamente"
#!/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()
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")
# 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()
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"
]
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"
]
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"
]
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()
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)
# 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()
# 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)