Генерируйте видео с моделью Wan2.1 от Alibaba на GPU Clore.ai
Генерируйте видеоролики высокого качества с помощью моделей Wan2.1 от Alibaba для преобразования текста в видео и изображения в видео на GPU CLORE.AI.
Все примеры можно запускать на GPU-серверах, арендуемых через CLORE.AI Marketplace.
Почему Wan2.1?
Высокое качество - Современная генерация видео
Несколько режимов - Текст в видео, изображение в видео
Разные размеры - От 1,3B до 14B параметров
Длинные видео - До 81 кадра
Открытые веса - Лицензия Apache 2.0
Варианты моделей
Модель
Параметры
VRAM
Разрешение
Кадры
Wan2.1-T2V-1.3B
1.3B
8GB
480p
81
Wan2.1-T2V-14B
14B
24 ГБ
720p
81
Wan2.1-I2V-14B
14B
24 ГБ
720p
81
Быстрое развертывание на CLORE.AI
Docker-образ:
pytorch/pytorch:2.5.1-cuda12.4-cudnn9-devel
Порты:
Команда:
Доступ к вашему сервису
После развертывания найдите ваш http_pub URL в Моих заказах:
Перейдите на Моих заказах страницу
Нажмите на ваш заказ
Найдите http_pub URL (например, abc123.clorecloud.net)
Используйте https://YOUR_HTTP_PUB_URL вместо localhost в примерах ниже.
Требования к аппаратному обеспечению
Модель
Минимальная GPU
Рекомендуется
Оптимально
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
Установка
Текст в видео
Базовое использование (1.3B)
Высокое качество (14B)
Изображение в видео
Анимировать изображение
Изображение в видео с Wan2.1-I2V-14B
Wan2.1-I2V-14B анимирует статичное изображение с помощью текстового промпта для управления движением. Требуется 24GB видеопамяти (рекомендуется RTX 4090 или A100 40GB).
#!/usr/bin/env python3
"""
CLI-скрипт Wan2.1 для преобразования изображения в видео.
Использование: python generate_i2v.py --model Wan-AI/Wan2.1-I2V-14B-480P \
--image input.jpg --prompt "камера медленно отдаляется"
"""
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="Генератор Wan2.1 Image-to-Video")
parser.add_argument(
"--model",
type=str,
default="Wan-AI/Wan2.1-I2V-14B-480P",
help="ID модели на Hugging Face (по умолчанию: Wan-AI/Wan2.1-I2V-14B-480P)",
)
parser.add_argument(
"--image",
type=str,
required=True,
help="Путь к входному изображению (JPEG или PNG)",
)
parser.add_argument(
"--prompt",
type=str,
required=True,
help='Текстовый промпт, описывающий желаемое движение (например, "камера медленно отдаляется")',
)
parser.add_argument(
"--negative-prompt",
type=str,
default="размыто, низкое качество, искажено, дерганое движение, артефакты",
help="Негативный промпт, чтобы избежать нежелательных артефактов",
)
parser.add_argument(
"--frames",
type=int,
default=49,
help="Количество кадров видео для генерации (по умолчанию: 49, макс.: 81)",
)
parser.add_argument(
"--steps",
type=int,
default=50,
help="Количество шагов диффузии (по умолчанию: 50)",
)
parser.add_argument(
"--guidance",
type=float,
default=7.0,
help="Масштаб classifier-free guidance (по умолчанию: 7.0)",
)
parser.add_argument(
"--seed",
type=int,
default=-1,
help="Случайное зерно для воспроизводимости (-1 = случайное)",
)
parser.add_argument(
"--fps",
type=int,
default=16,
help="FPS выходного видео (по умолчанию: 16)",
)
parser.add_argument(
"--output",
type=str,
default="output_i2v.mp4",
help="Путь к файлу выходного видео (по умолчанию: output_i2v.mp4)",
)
parser.add_argument(
"--height",
type=int,
default=480,
help="Высота выходного видео в пикселях (по умолчанию: 480)",
)
parser.add_argument(
"--width",
type=int,
default=854,
help="Ширина выходного видео в пикселях (по умолчанию: 854)",
)
parser.add_argument(
"--cpu-offload",
action="store_true",
default=True,
help="Включить выгрузку модели на CPU для экономии видеопамяти (по умолчанию: True)",
)
parser.add_argument(
"--vae-tiling",
action="store_true",
default=False,
help="Включить tiling VAE для высоких разрешений",
)
return parser.parse_args()
def load_and_resize_image(image_path: str, width: int, height: int) -> Image.Image:
"""Загрузить изображение по пути и изменить размер до целевых размеров."""
if not os.path.exists(image_path):
print(f"[ERROR] Изображение не найдено: {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] Загружено изображение: {image_path} ({original_size[0]}x{original_size[1]}) → изменён размер до {width}x{height}")
return img
def load_pipeline(model_id: str, cpu_offload: bool, vae_tiling: bool):
"""Загрузить конвейер Wan I2V с оптимизациями памяти."""
print(f"[INFO] Загрузка модели: {model_id}")
print(f"[INFO] CUDA доступна: {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 VRAM)")
if vram_gb < 23:
print("[WARN] Обнаружено менее 24GB VRAM — включите --cpu-offload или используйте модель 1.3B")
pipe = WanImageToVideoPipeline.from_pretrained(
model_id,
torch_dtype=torch.float16,
)
if cpu_offload:
print("[INFO] Включена выгрузка модели на CPU")
pipe.enable_model_cpu_offload()
else:
pipe.to("cuda")
if vae_tiling:
print("[INFO] Включён VAE tiling для генерации в высоком разрешении")
pipe.enable_vae_tiling()
return pipe
def generate_video(pipe, args) -> None:
"""Запустить I2V конвейер и сохранить выходное видео."""
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] Используемое зерно: {args.seed}")
else:
print("[INFO] Используется случайное зерно")
print(f"[INFO] Генерация {args.frames} кадров при {args.width}x{args.height}")
print(f"[INFO] Шаги: {args.steps} | Guidance: {args.guidance} | FPS: {args.fps}")
print(f"[INFO] Промпт: {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] Видео сохранено в: {os.path.abspath(args.output)}")
duration = args.frames / args.fps
print(f"[INFO] Длительность: {duration:.1f}s при {args.fps}fps ({args.frames} кадров)")
def main():
args = parse_args()
if not torch.cuda.is_available():
print("[ERROR] CUDA GPU не найден. Wan2.1-I2V-14B требует GPU с поддержкой CUDA.", file=sys.stderr)
sys.exit(1)
pipe = load_pipeline(args.model, args.cpu_offload, args.vae_tiling)
generate_video(pipe, args)
print("[DONE] Генерация из изображения в видео завершена!")
if __name__ == "__main__":
main()
import torch
from diffusers import WanImageToVideoPipeline
from diffusers.utils import load_image, export_to_video
from PIL import Image
# ── Загрузка конвейера ──────────────────────────────────────────────────────────────
pipe = WanImageToVideoPipeline.from_pretrained(
"Wan-AI/Wan2.1-I2V-14B-480P",
torch_dtype=torch.float16,
)
pipe.enable_model_cpu_offload() # держит VRAM ниже 24GB
pipe.enable_vae_tiling() # опционально: помогает для 720p
# ── Загрузить и подготовить входное изображение ─────────────────────────────────────
image = load_image("input.jpg").resize((854, 480))
# ── Генерация ───────────────────────────────────────────────────────────────────
prompt = "камера медленно отдаляется, открывая весь пейзаж"
negative_prompt = "размыто, низкое качество, искажено, мерцание, артефакты"
generator = torch.Generator("cuda").manual_seed(42)
output = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
image=image,
num_frames=49, # ~3 секунды при 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("Сохранено: i2v_output.mp4")
# Обязательно на GPU 24GB
pipe.enable_model_cpu_offload()
# Опционально: уменьшает пиковое использование VRAM примерно на 10%
pipe.enable_vae_tiling()
pipe.enable_vae_slicing()
# Очищать между запусками
import gc
gc.collect()
torch.cuda.empty_cache()
prompts = [
"Таймлапс облаков над горными пиками, драматическое освещение",
"Океанские волны, разбивающиеся о скалы, замедленная съёмка, кинематографично",
"Северное сияние, танцующее в ночном небе, насыщенные цвета",
"Осенний лес с падающими листьями, спокойная атмосфера"
]
prompts = [
"Золотистый ретривер, бегущий по полю с цветами",
"Бабочка, вылезающая из куколки, макросъёмка",
"Самурай, вытягивающий меч, драматическое освещение",
"Робот, гуляющий по улицам футуристического города"
]
prompts = [
"Яркие краски, закручивающиеся в воде, абстрактное искусство",
"Геометрические формы трансформируются и морфируются, неоновые цвета",
"Чернильные капли, распространяющиеся в молоке, макрофотография"
]
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 = [
"Ракета, стартующая в космос",
"Рыбы, плавающие в коралловом рифе",
"Дождь, падающий на городскую улицу ночью"
]
output_dir = "./videos"
os.makedirs(output_dir, exist_ok=True)
for i, prompt in enumerate(prompts):
print(f"Генерируется {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="размыто, низкое качество"),
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="Seed")
],
outputs=gr.Video(label="Сгенерированное видео"),
title="Wan2.1 - Генерация из текста в видео",
description="Генерируйте видео по текстовым промптам. Запуск на CLORE.AI."
)
demo.launch(server_name="0.0.0.0", server_port=7860)
# Включить все оптимизации
pipe.enable_model_cpu_offload()
pipe.enable_vae_tiling()
pipe.enable_vae_slicing()
# Для очень низкой видеопамяти
pipe.enable_sequential_cpu_offload()
# Очищать кэш между генерациями
torch.cuda.empty_cache()
# Используйте меньшую модель
pipe = WanPipeline.from_pretrained("alibaba-pai/Wan2.1-T2V-1.3B")
# Включить все оптимизации
pipe.enable_model_cpu_offload()
pipe.enable_vae_tiling()
# Уменьшить количество кадров
output = pipe(prompt, num_frames=17)
# Уменьшить разрешение
output = pipe(prompt, height=480, width=854)