Diffusers documentation

SANA-WM

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.42.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

SANA-WM

SANA-WM es un modelo de mundo de imagen a video controlado por cámara, construido sobre SANA. Dada una imagen de primer fotograma, un prompt de texto y una trayectoria de cámara (ya sea una pose c2w explícita o una cadena de acciones WASD/IJKL), genera un video cuyo movimiento sigue la trayectoria de cámara solicitada.

La inferencia se ejecuta en dos etapas:

  1. Etapa 1 — SANA-WM DiT. Un DiT bidireccional de 1.6B parámetros con atención lineal GDN-Triton y una rama de control de cámara UCPE. El muestreo utiliza un planificador Euler de flow-matching estilo LTX con pasos de tiempo por token; el primer marco latente es la ancla de condicionamiento.
  2. Etapa 2 — Refinador LTX-2 (opcional). Un pipeline de refinamiento Euler bidireccional con sink independiente (SanaWMLTX2Refiner) que envuelve SanaWMLTX2RefinerTransformer3DModel + LTX2TextConnectors y un codificador de texto Gemma-3, ejecutado durante 3 pasos de sigma destilados.

Ambas etapas decodifican a través del VAE AutoencoderKLLTX2Video.

Modelos disponibles:

Modelodtype recomendado
Efficient-Large-Model/SANA-WM_bidirectional-diffuserstorch.bfloat16
Efficient-Large-Model/SANA-WM_bidirectional-diffusers-refinertorch.bfloat16

SANA-WM se entrena a una resolución fija de 704×1280. El dtype recomendado para los pesos del transformer es torch.bfloat16; mantenga el codificador de texto en torch.bfloat16 y el VAE en torch.float32 para obtener la mejor precisión numérica. El pipeline espera las intrínsecas de la cámara [fx, fy, cx, cy] en coordenadas de píxeles de la imagen original; la transformación de redimensionado y recorte centrado se aplica internamente.

Inferencia

import torch
from PIL import Image

from diffusers import SanaWMPipeline
from diffusers.utils import export_to_video

pipe = SanaWMPipeline.from_pretrained(
    "Efficient-Large-Model/SANA-WM_bidirectional-diffusers",
    torch_dtype=torch.bfloat16,
)
pipe.enable_model_cpu_offload()  # ~45 GB of weights — offload between stages

# SANA-WM was trained on the LTX-2 VAE in framewise mode with tiling enabled. Without these
# settings the VAE encodes the whole (B, C, T, H, W) clip in one shot, which gives subtly
# different numerics from the released checkpoint.
pipe.vae.enable_tiling()
pipe.vae.use_framewise_encoding = True
pipe.vae.use_framewise_decoding = True
pipe.vae.tile_sample_stride_num_frames = 64
pipe.vae.tile_sample_min_num_frames = 96

prompt = "A car driving across a vast desert plain at golden hour."
output = pipe(
    image=Image.open("input.png").convert("RGB"),
    prompt=prompt,
    action="w-80,jw-40,w-40",        # WASD-style action DSL: forward 80f, jump+forward 40f, forward 40f
    intrinsics=[800.0, 800.0, 845.0, 464.0],  # fx, fy, cx, cy in original-image pixels
    num_frames=161,
    num_inference_steps=60,
    guidance_scale=5.0,
    generator=torch.Generator(device="cuda").manual_seed(42),
    output_type="latent",            # hand the latents to the refiner below
)

Pasa action=None y proporciona tus propias poses c2w (un array numpy de (F, 4, 4)) para controlar la trayectoria de la cámara de forma explícita. Omita output_type="latent" para obtener el video directamente de la etapa 1 y saltarse el refinador.

Etapa 2 — el refinador LTX-2

SanaWMLTX2Refiner se distribuye como su propio repositorio, de la misma manera que SDXL separa el modelo base y el refiner. Pasa el VAE del pipeline base para que los pesos se compartan en lugar de cargarse dos veces:

from diffusers import SanaWMLTX2Refiner

refiner = SanaWMLTX2Refiner.from_pretrained(
    "Efficient-Large-Model/SANA-WM_bidirectional-diffusers-refiner",
    vae=pipe.vae,
    torch_dtype=torch.bfloat16,
)
refiner.enable_model_cpu_offload()

frames = refiner(output.latent, prompt, fps=16)
export_to_video(list(frames), "sana_wm.mp4", fps=16)

Sin un vae, el refiner devuelve latentes refinados en lugar de video, lo cual es útil si deseas decodificarlos tú mismo.

Habilite el offloading en ambos pipelines, o libere la etapa 1 antes de la etapa 2 (pipe.transformer.to("cpu")). Las dos etapas juntas ocupan alrededor de 45 GB en torch.bfloat16, y mantener ambas residentes en una tarjeta de 80 GB deja demasiado poco espacio para las activaciones. Tenga en cuenta también que la etapa 2 respeta torch_dtype: cargar el refiner en torch.float32 duplica aproximadamente su memoria y cambia ligeramente la salida.

Si no tienes las intrínsecas de la cámara, un bloque modular alojado puede estimarlas a partir de un solo fotograma. Se encuentra fuera de diffusers porque incorpora Pi3X, una dependencia adicional y un segundo punto de control, por lo que no se descarga nada hasta que lo solicites:

from diffusers import ModularPipeline

# One-time per image. Requires `pip install pi3-vision`.
estimator = ModularPipeline.from_pretrained(
    "Efficient-Large-Model/pi3x-intrinsics-estimator", trust_remote_code=True
)
estimator.load_components(dtype=torch.bfloat16)
intrinsics = estimator(image=Image.open("input.png").convert("RGB"), output="intrinsics")

Convertir el punto de control publicado

Si tienes la versión de origen de SANA-WM (no la instantánea de diffusers preconvertida), ejecuta el script de conversión una vez:

python scripts/convert_sana_wm_to_diffusers.py \
    --src Efficient-Large-Model/SANA-WM_bidirectional \
    --dst ./SANA-WM_bidirectional-diffusers

Esto escribe dos directorios: el pipeline base en --dst y el refiner de la etapa 2 junto a él en ./SANA-WM_bidirectional-diffusers-refiner (se puede sobrescribir con --dst-refiner). Luego, carga cada uno desde su ruta local como de costumbre.

Componentes

El refiner de la etapa 2 es un repositorio independiente con su propio transformer (SanaWMLTX2RefinerTransformer3DModel), connectors (LTX2TextConnectors), tokenizer, text_encoder Gemma-3 y scheduler. No tiene un vae propio: pase el de la pipeline base.

SanaWMPipeline

clase diffusers.SanaWMPipeline

< >

( tokenizer: GemmaTokenizer | GemmaTokenizerFasttext_encoder: Gemma2PreTrainedModelvae: AutoencoderKLLTX2Videotransformer: SanaWMTransformer3DModelscheduler: FlowMatchEulerDiscreteScheduler )

Parámetros

  • tokenizer ([GemmaTokenizer] o [GemmaTokenizerFast]) — El tokenizador de Gemma-2.
  • text_encoder ([Gemma2PreTrainedModel]) — El codificador de texto de Gemma-2.
  • vae ([AutoencoderKLLTX2Video]) — El VAE de LTX-2.
  • transformer ([SanaWMTransformer3DModel]) — El DiT bidireccional SANA-WM de 1600M.
  • scheduler ([FlowMatchEulerDiscreteScheduler]) — Programador de Euler de flujo de coincidencia (pasos de tiempo por token estilo LTX). latentes directamente.

Pipeline de imagen a video controlado por cámara de SANA-WM.

Genera un video a partir de una imagen de primer fotograma, un prompt de texto y una trayectoria de cámara (poses c2w explícitas o una cadena de acciones WASD/IJKL). Utiliza el SANA DiT bidireccional de 1600M para la muestreo de la etapa 1 y el refinador Euler sink-bidireccional de LTX-2 para el pulido de la etapa 2; ambos se decodifican a través del VAE de LTX-2.

__call__

< >

( image: PIL.Image.Image | str | Pathprompt: strc2w: np.ndarray | None = Noneaction: str | None = Noneintrinsics: np.ndarray | list[float] | None = Noneheight: int = 704width: int = 1280num_frames: int = 161fps: int = 16num_inference_steps: int = 60guidance_scale: float = 5.0negative_prompt: str = ''generator: torch.Generator | list[torch.Generator] | None = Noneprompt_embeds: torch.Tensor | None = Noneprompt_attention_mask: torch.Tensor | None = Nonenegative_prompt_embeds: torch.Tensor | None = Nonenegative_prompt_attention_mask: torch.Tensor | None = Nonemax_sequence_length: int = 300chi_prompt: list[str] | None = Noneoutput_type: Literal['np', 'pil', 'pt', 'latent'] = 'np'callback_on_step_end: Callable[[DiffusionPipeline, int, int, dict], dict] | None = Nonecallback_on_step_end_tensor_inputs: list[str] = ['latents']return_dict: bool = True )

Parámetros

  • image (PIL.Image.Image o str) — Imagen del primer fotograma (PIL o ruta).
  • prompt (str) — Prompt de texto.
  • c2w (np.ndarray, opcional) — (F, 4, 4) poses de cámara al mundo. Mutuamente excluyente con action.
  • action (str, opcional) — Cadena de Action-DSL, por ejemplo "w-80,jw-40,w-40". Mutuamente excluyente con c2w.
  • intrinsics (np.ndarray o list[float]) — [fx, fy, cx, cy] en coordenadas de píxeles de la imagen original. El pipeline aplica internamente la transformación de redimensionado y recorte.
  • height (int, por defecto 704) — Altura del cuadro de salida (fija para el modelo público).
  • width (int, por defecto 1280) — Ancho del cuadro de salida (fijo para el modelo público).
  • num_frames (int, por defecto 161) — Número de fotogramas objetivo; se ajusta a 8k+1 (restricción del VAE de LTX-2).
  • fps (int, por defecto 16) — Tasa de fotogramas de salida (también se pasa al refiner).
  • num_inference_steps (int, por defecto 60) — Número de pasos de muestreo de DiT de la etapa 1.
  • guidance_scale (float, por defecto 5.0) — Escala de guía libre de clasificador.
  • negative_prompt (str, por defecto "") — Prompt negativo opcional.
  • generator (torch.Generator o list[torch.Generator], opcional) — Uno o más generadores de torch para hacer que la muestreo de ruido sea determinista. Si es None, la muestreo es no determinista.
  • prompt_embeds (torch.Tensor, opcional) — Incrustaciones de texto precalculadas, para omitir el codificador de texto. Debe pasarse junto con prompt_attention_mask.
  • prompt_attention_mask (torch.Tensor, opcional) — Máscara de atención para prompt_embeds.
  • negative_prompt_embeds (torch.Tensor, opcional) — Incrustaciones de texto negativo precalculadas. Debe pasarse junto con negative_prompt_attention_mask.
  • negative_prompt_attention_mask (torch.Tensor, opcional) — Máscara de atención para negative_prompt_embeds.
  • max_sequence_length (int, por defecto 300) — Número máximo de tokens del prompt.
  • chi_prompt (list[str], opcional) — Sobrescribe el prefijo chi-prompt (el valor predeterminado replica la versión pública).
  • output_type (“np”, “pil”, “pt” o “latent”, por defecto “np”) — Formato de salida.
  • callback_on_step_end (Callable, opcional) — Se llama al final de cada paso de desruido como callback_on_step_end(self, step, timestep, callback_kwargs), donde callback_kwargs contiene los tensores nombrados en callback_on_step_end_tensor_inputs. Los tensores en el diccionario devuelto reemplazan los de la pipeline.
  • callback_on_step_end_tensor_inputs (list[str], por defecto [“latents”]) — Qué tensores pasar a callback_on_step_end; debe ser un subconjunto de ._callback_tensor_inputs.
  • return_dict (bool, por defecto True) — Devuelve [SanaWMPipelineOutput] o una tupla.

Genera un video controlado por cámara de SANA-WM.

Ejemplos:

>>> import torch
>>> from PIL import Image
>>> from diffusers import SanaWMPipeline

>>> pipe = SanaWMPipeline.from_pretrained(
...     "Efficient-Large-Model/SANA-WM_bidirectional-diffusers", torch_dtype=torch.bfloat16
... ).to("cuda")

>>> output = pipe(
...     image=Image.open("input.png").convert("RGB"),
...     prompt="A car driving across a vast desert plain at golden hour.",
...     action="w-80,jw-40,w-40",
...     intrinsics=[800.0, 800.0, 845.0, 464.0],  # fx, fy, cx, cy in original-image pixels
...     num_inference_steps=60,
... )
>>> # output.frames is (T, H, W, 3) float np.ndarray in [0, 1] (diffusers convention).

check_inputs

< >

( c2w: np.ndarray | Noneaction: str | Noneintrinsics: np.ndarray | list[float] | Nonecallback_on_step_end_tensor_inputs: list[str] | None = None )

Valida las entradas de __call__. Lanza una excepción si la entrada es incorrecta y no devuelve nada.

encode_prompt

< >

( prompt: strnegative_prompt: str = ''device: torch.device | None = Nonemax_sequence_length: int = 300chi_prompt: list[str] | None = Noneprompt_embeds: torch.Tensor | None = Noneprompt_attention_mask: torch.Tensor | None = Nonenegative_prompt_embeds: torch.Tensor | None = Nonenegative_prompt_attention_mask: torch.Tensor | None = None )

Codifica el prompt y el prompt negativo a través de Gemma-2.

Reproduce el truco de prefijo de chi-prompt de SANA: el prompt chi se antepone al prompt del usuario y luego una rebanada select_index = [0, -L+1, ..., -1] toma el token BOS junto con los últimos max_sequence_length - 1 tokens.

prepare_camera_trajectory

< >

( c2w: np.ndarray | Noneaction: str | Noneintrinsics: np.ndarray | list[float]num_frames: int )

Normaliza las entradas de la cámara a (c2w_(F, 4, 4), intrinsics_(F, 4)).

También ajusta num_frames al formato 8k+1 compatible con el VAE y recorta ambos arreglos para que coincidan. La imagen recortada y las intrínsecas reescaladas se proporcionan más adelante, una vez que se conoce la resolución objetivo.

prepare_latents

< >

( first_latent: torch.Tensornum_frames: intheight: intwidth: intdtype: torch.dtypedevice: torch.devicegenerator: torch.Generator )

Muestree los latentes iniciales y fije el primer fotograma como ancla de condicionamiento.

Devuelve (latents, condition_mask), donde condition_mask tiene unos en los tokens del primer fotograma (se mantienen limpios durante todo el muestreo) y ceros en el resto.

SanaWMLTX2Refiner

El refinador de la etapa 2 de LTX-2 es un DiffusionPipeline independiente que recibe los latentes de la etapa 1. Si se le proporciona un vae (el de la pipeline base, para compartir los pesos), decodifica a video; si no, devuelve los latentes refinados.

clase diffusers.SanaWMLTX2Refiner

< >

( transformer: SanaWMLTX2RefinerTransformer3DModelconnectors: LTX2TextConnectorstokenizer: GemmaTokenizer | GemmaTokenizerFasttext_encoder: Gemma3ForConditionalGenerationscheduler: FlowMatchEulerDiscreteSchedulervae: AutoencoderKLLTX2Video | None = Nonetext_max_sequence_length: int = 1024 )

Parámetros

  • transformer ([SanaWMLTX2RefinerTransformer3DModel]) — El DiT de video LTX-2 con caché KV de ventana deslizante causal por bloques.
  • connectors ([LTX2TextConnectors]) — Conectores de texto de LTX-2.
  • tokenizer — Tokenizer de Gemma-3.
  • text_encoder — Codificador de texto de Gemma-3.
  • scheduler ([FlowMatchEulerDiscreteScheduler]) — Programador de Euler para flow-matching. Se construye con shift=1.0 para que las sigmas destiladas pasen sin modificarse.
  • vae ([AutoencoderKLLTX2Video], opcional) — El mismo VAE utilizado por [SanaWMPipeline]; pase vae=pipe.vae para compartir los pesos. Si se proporciona, el refiner decodifica a video; de lo contrario, devuelve latentes refinados.
  • text_max_sequence_length (int, por defecto 1024) — Número máximo de tokens pasados al tokenizador de Gemma-3.

LTX-2 sink-bidirectional Euler refiner — SANA-WM etapa 2, como pipeline independiente.

Envuelve los componentes de LTX-2 (transformador refinador + conectores de texto + codificador de texto Gemma-3 + tokenizador) junto con un [FlowMatchEulerDiscreteScheduler] que lleva la programación de sigma destilada y realiza los pasos de Euler. Está registrado como un componente opcional de [SanaWMPipeline] y también puede usarse de forma independiente para refinar los latentes de la etapa 1.

__call__

< >

( sana_latent: torch.Tensorprompt: strfps: floatsink_size: int = 1generator: torch.Generator | None = Noneblock_size: int = 3kv_max_frames: int = 11sigmas: tuple[float, ...] = (0.909375, 0.725, 0.421875, 0.0)output_type: str = 'np' ) → torch.Tensor

Parámetros

  • sana_latent — latente de la etapa 1, (B, C, F, H, W).
  • prompt — prompt de texto.
  • fps — tasa de fotogramas por segundo del video (controla la escalada temporal de RoPE en LTX-2).
  • sink_size — número de fotogramas iniciales de z_sana sin procesar que se fijan como sumidero de atención (canónico: 1).
  • generator — torch.Generator para el ruido del endpoint FM. Por defecto, se usa un generador con semilla 42 para que los resultados sean reproducibles desde el inicio.
  • block_size — fotogramas latentes por bloque AR (canónico: 3).
  • kv_max_frames — número máximo de cuadros de contexto + activos que se conservan en la ventana deslizante (canónico: 11 = 1 sink + 10 recientes).
  • sigmas — plan de Euler descendente que termina en 0.0 (destilado canónico de 3 pasos: (0.909375, 0.725, 0.421875, 0.0)). Se pasa a self.scheduler (sin el 0.0 final, que el scheduler añade por sí mismo).
  • output_type — “latent” devuelve los latentes refinados. Cualquier otro valor se decodifica a través de self.vae y se postprocesa a ese tipo (“np”, “pt”, “pil”); sin un vae, se devuelven los latentes de todos modos.

Devuelve

torch.Tensor

Latentes de VAE refinados de forma (B, C, F, H, W): los primeros sink_size cuadros conservan los latentes de sink de la etapa 1 sin cambios, y el resto contiene la salida refinada.

Ejecuta el refinador LTX-2 y devuelve los latentes VAE refinados.

Utiliza la receta AR de causalidad por bloques con la que se entrenó el modelo (block_size=3, kv_max_frames=11): una ventana deslizante de K/V de [source_sink + recent_history + active_block] se alimenta al transformer un bloque a la vez, de modo que el cómputo por bloque está acotado y el coste total de refinamiento escala linealmente con la longitud del video.

SanaWMPipelineOutput

clase diffusers.SanaWMPipelineOutput

< >

( frames: typing.Union[torch.Tensor, numpy.ndarray, list[list[PIL.Image.Image]]]c2w: numpy.ndarray | None = Nonelatent: typing.Optional[torch.Tensor] = None )

Parámetros

  • frames (torch.Tensor, np.ndarray o list[PIL.Image.Image]) — Video generado: un np.ndarray de tipo float con forma (T, H, W, 3) en [0, 1] para output_type="np", un tensor (T, 3, H, W) para "pt", una lista de T `PIL.Image para "pil", o el tensor latente (B, C, T_lat, H_lat, W_lat) para "latent".
  • c2w (np.ndarray) — Posas de cámara al mundo (T, 4, 4) alineadas con frames (el refiner descarta el marco de ancla de sink; este array se realinea en consecuencia cuando se ejecutó el refiner).
  • latent (torch.Tensor, optional) — Latent tensor in LTX-2 VAE space, shape (B, C, T_lat, H_lat, W_lat). Returned when `output_type=“latent”“.

Clase de salida para el pipeline de imagen a video de SANA-WM.

Update on GitHub