SANA-WM
SANA-WM
SANA-WM은 NVLabs의 2.6B 텍스트·이미지-투-비디오 월드 모델로, 720p·분 단위(minute-scale) 생성과 명시적 프레임별 6-DoF 카메라 제어를 지원합니다. 하이브리드 recurrent/softmax 어텐션은 긴 인과 기록을 제한된 상태로 유지하고, LTX-2 리파이너가 빠른 Stage-1 월드 모델이 자체적으로 만들지 못하는 디테일을 공급합니다.
출처: 문서
본문
<DiffusionModelTags tags={["world model", "720p video", "minute-scale", "6-DoF camera control", "realtime"]} />
1. 모델 소개 (Model Introduction)
SANA-WM은 NVLabs의 2.6B 텍스트·이미지-투-비디오 월드 모델로, 720p·분 단위 생성과 명시적 프레임별 6-DoF 카메라 제어를 지원합니다. 하이브리드 recurrent/softmax 어텐션은 긴 인과 기록을 제한된 상태로 유지하고, LTX-2 리파이너가 빠른 Stage-1 월드 모델이 자체적으로 만들지 못하는 디테일을 공급합니다.
최상의 제한-클립(bounded-clip) 품질에는 dense 체크포인트를, 장기 실행·대화형 제어에는 streaming 체크포인트를 선택하세요. Streaming과 realtime은 전역 양방향 컨텍스트를 제한된 상태와 더 낮은 응답 지연으로 맞바꿉니다. realtime WebSocket 경로는 오프라인 배치 스트리밍과 비트 단위 동일하지 않습니다.
| 서빙 모드 (Serving mode) | 체크포인트와 API (Checkpoint and API) | 가장 좋은 적합 (Best fit) | 트레이드오프 (Tradeoff) |
|---|---|---|---|
| Dense bidirectional | /v1/videos를 통한 SANA-WM_bidirectional |
최고 단일-클립 품질 | 제한된 one-shot 생성; 전체 어텐션 비용 |
| Batch streaming | /v1/videos를 통한 SANA-WM_streaming |
하나의 작업으로 반환되는 긴 클립 | 전역 양방향 컨텍스트가 아닌 chunk-causal 컨텍스트 |
| Live realtime | WebSocket을 통한 SANA-WM_streaming |
대화형 카메라 이벤트와 증분 출력 | 상태 저장 세션; 배치 스트리밍과 비트 단위 동일하지 않음 |
아키텍처 & 구성 요소
| 구성 요소 (Component) | 값 (Value) |
|---|---|
| Stage-1 DiT | 2.6B; 20 레이어, hidden 2240, 20 헤드(head_dim 112); ~10 GB |
| 어텐션 (Attention) | 프레임별 Gated DeltaNet + 4블록마다 softmax (하이브리드 선형) |
| 카메라 (Camera) | 이중 분기, UCPE + PRoPE (raymap + Plücker), 6-DoF |
| VAE | LTX-2 causal, strides (T, H, W) = (8, 32, 32); ~2 GB |
| 리파이너 (Refiner) | LTX-2 Stage-2 distilled; ~41 GB |
| 출력 (Output) | 최대 720p (704×1280) @ 16 fps, 분 단위 |
아키텍처와 훈련 세부사항은 SANA-WM 논문과 모델 카드(Apache-2.0)를 참조하세요.
2. 설치 (Installation)
SGLang-diffusion은 하드웨어 플랫폼에 따라 여러 설치 방법을 제공합니다. SGLang Diffusion 설치 가이드를 참조하세요.
SANA-WM은 SanaWMTransformer3DModel + GDN 커널, SanaWMTwoStagePipeline(dense bidirectional + chunk-causal streaming), 그리고 /v1/realtime_video WebSocket 라우터가 있는 SanaWMRealtimePipeline을 추가합니다. 디퓨전 서버를 시작하려면 sglang serve를 사용하세요.
3. 모델 설정 (Model Setup)
두 SANA-WM 체크포인트는 모두 공개(Apache-2.0, 게이팅 없음, 토큰 없음)이며 직접 로드됩니다. 수동 조립 단계가 없습니다. --model-path에 HuggingFace repo id를 전달하면 SGLang이 다운로드·구체화·검증·로드합니다:
| 모드 (Mode) | --model-path |
|---|---|
| Dense bidirectional (§4) | Efficient-Large-Model/SANA-WM_bidirectional |
| Batch streaming (§5) / realtime (§6) | Efficient-Large-Model/SANA-WM_streaming |
두 repo id 모두 SGLang의 내장 model-overlay 레지스트리에 등록되어 있어, 첫 로드 시 overlay가 공식 릴리스를 실행 가능한 Diffusers 디렉터리로 투명하게 구체화합니다. streaming 체크포인트의 경우 DMD self-forcing 체크포인트(sana_dit/model.pt)를 Diffusers transformer/로 변환하고 LTX-2 causal VAE, LTX-2 리파이너, Gemma 인코더를 연결합니다. 환경 변수나 build_model_dir.sh 단계가 필요 없습니다. (로컬의 이미 구체화된 Diffusers 디렉터리를 전달할 수도 있습니다.)
구체화된 체크포인트는 model_index.json이 로드 가능한 컴포넌트를 선언하는 Diffusers 디렉터리입니다:
컴포넌트 (model_index.json) |
클래스 (Class) |
|---|---|
transformer (Stage-1 DiT) |
diffusers.SanaWMTransformer3DModel |
vae |
diffusers.AutoencoderKLCausalLTX2Video |
text_encoder |
transformers.Gemma2Model |
tokenizer |
transformers.GemmaTokenizer |
scheduler |
diffusers.FlowMatchEulerDiscreteScheduler |
로딩 방식:
- 서버는
maybe_download_model(model_path, force_diffusers_model=True)로 체크포인트를 해석하고model_index.json과 필수 컴포넌트 하위 디렉터리(transformer/,vae/)를 포함하는지 검증합니다. text_encoder/tokenizer가 컴포넌트 경로로 제공되지 않으면 파이프라인은 기본 Stage-1 텍스트 인코더Efficient-Large-Model/gemma-2-2b-it(DEFAULT_SANA_WM_TEXT_ENCODER)로 폴백합니다.--pipeline-class-name으로 경로를 선택하세요. 체크포인트의model_index.json_class_name이 기본 파이프라인(SanaWMTwoStagePipeline)을 선택합니다. 명시적으로 고정하려면:/v1/videos경로(§4–5)에는--pipeline-class-name SanaWMTwoStagePipeline, 라이브 realtime(§6)에는--pipeline-class-name SanaWMRealtimePipeline. Diffusers 디렉터리 대신 bare safetensors 파일을--model-path로 가리켜도 고정이 필요합니다.- Stage-2 LTX-2 리파이너는 체크포인트의
refiner/아래에 있습니다:refiner/transformer(transformer_2),refiner/connectors(connectors),refiner/text_encoder(text_encoder_2용 Gemma-3 인코더로, 그 토크나이저가tokenizer_2도 제공). 리파이너는 선택 사항입니다. 환경 플래그SGLANG_SANA_WM_SKIP_REFINER(또는skip_refiner요청 extra)가 설정되거나refiner/가 없으면(transformer_2언로드) 건너뜁니다(Stage-1 전용 출력). 배치 경로에서는--refiner-chunked(공식 스트리밍 경로, 기본 켜짐)로 청크별 실행하거나 없으면 전체-클립 실행합니다. realtime 경로에서 파이프라인은 리파이너가 있을 때만SanaWMChunkedRefinerChainStage를 만들고, 없으면 Stage-1 프레임을 스트리밍합니다.
4. Dense bidirectional (오프라인 /v1/videos)
bidirectional 체크포인트는 전체 클립을 one-shot으로 생성합니다(전체 양방향 어텐션, 청크 아님). 이어서 dense LTX-2 리파이너가 따릅니다. NVlabs dense 레퍼런스와 일치하는 최고 단일-클립 품질입니다.
2단계 파이프라인과 없는 --streaming 플래그로 시작합니다(dense가 기본 — streaming 기본값은 False):
sglang serve \
--model-path Efficient-Large-Model/SANA-WM_bidirectional \
--pipeline-class-name SanaWMTwoStagePipeline \
--host 127.0.0.1 --port 30000
그런 다음 §5에서와 정확히 같이 /v1/videos 에 POST하되, 가장 가까운 패리티를 위해 NVlabs dense 샘플링 기본값을 전달하세요. dense 경로는 증류된 streaming few-step 스케줄보다 더 밀도 있습니다:
curl -s http://127.0.0.1:30000/v1/videos \
-H 'content-type: application/json' -d '{
"prompt": "a camera moving forward and turning left",
"input_reference": "/path/to/first_frame.png",
"num_frames": 321,
"seed": 42,
"fps": 16,
"num_inference_steps": 60,
"guidance_scale": 5.0,
"diffusers_kwargs": {
"action": "w-80,wl-80,l-80,wj-80",
"intrinsics": "/path/to/intrinsics.npy"
}
}'
num_inference_steps/guidance_scale— dense 경로는 CFG를 사용합니다. NVlabs 레퍼런스 기본값은 60 스텝, guidance 5.0(SanaWMSamplingParams기본값은 더 가벼운 20 / 4.5 — dense 패리티를 위해 60 / 5.0을 명시적으로 전달).- Dense 리파이너는 선두 sink 프레임을 버리므로
num_frames=321요청은 320 출력 프레임을 생성합니다.
5. Batch streaming (오프라인 /v1/videos)
streaming 체크포인트는 하나의 요청으로 전체 카메라 제어 클립을 생성합니다 — websocket 없음. 이것이 SGLang의 오프라인 스트리밍 경로입니다. 전체 클립은 내부적으로 청크별 생성, 리파인, 디코딩되어 하나의 비디오로 반환됩니다.
2단계 파이프라인 + 스트리밍 플래그로 시작:
sglang serve \
--model-path Efficient-Large-Model/SANA-WM_streaming \
--pipeline-class-name SanaWMTwoStagePipeline \
--streaming --refiner-chunked \
--host 127.0.0.1 --port 30000
--streaming— chunk-causalforward_longStage-1(§4의 dense one-shot 경로 대비).--refiner-chunked— 청크별 스트리밍 LTX-2 리파이너(기본 켜짐). 대신 전체-클립 dense 리파이너를 사용하려면(유효하며 최고 메모리가 더 높음)--refiner-chunked false를 전달하세요. 플래그를 그냥 생략하면 기본 chunked 리파이너가 유지됩니다.--num-frame-per-block N— 청크당 잠재 프레임(기본3).
그런 다음 /v1/videos 에 POST합니다(아래 JSON 본문. input_reference 파일을 업로드한 multipart/form-data도 동작). 카메라 제어는 diffusers_kwargs — action-DSL 문자열(§8)과 intrinsics — 에 들어갑니다:
curl -s http://127.0.0.1:30000/v1/videos \
-H 'content-type: application/json' -d '{
"prompt": "a camera moving forward and turning left",
"input_reference": "/path/to/first_frame.png",
"num_frames": 321,
"seed": 42,
"fps": 16,
"diffusers_kwargs": {
"action": "w-80,wl-80,l-80,wj-80",
"intrinsics": "/path/to/intrinsics.npy"
}
}'
| 필드 (Field) | 참고 (Notes) |
|---|---|
prompt |
텍스트 프롬프트 |
input_reference |
첫 프레임 이미지 — 서버 측 경로 또는 (multipart) 업로드 파일. JSON 본문의 http(s):// URL에는 별도의 reference_url 필드를 사용(서버가 다운로드해 input_reference에 할당) |
num_frames |
총 픽셀 프레임(예: 321 → 41 잠재 프레임, 13 청크; 출력 704×1280) |
seed |
RNG 시드(기본 42) |
fps |
출력 프레임 레이트 — 16(SANA-WM의 네이티브 레이트)을 전달하세요. 일반 /v1/videos 기본값은 24이며, 같은 프레임을 24 fps로 인코딩해 클립이 ~33% 더 짧게 재생됩니다(16/24 기간) |
diffusers_kwargs.action |
카메라 action-DSL 문자열(§8) |
diffusers_kwargs.intrinsics |
카메라 intrinsics .npy 경로(프레임별 (T,3,3)) 또는 인라인 3×3 / (T,3,3) 리스트 |
응답은 VideoResponse입니다. 반환된 reference 또는 GET /v1/videos/{id}/content로 렌더링된 MP4를 가져옵니다. 스트리밍 하이퍼파라미터(num_frame_per_block, denoising_step_list, sink_size, num_cached_blocks, streaming_cfg_scale)는 파이프라인 구성의 SanaWMPipelineConfig 기본값이지 요청 필드가 아닙니다 — §9 참조.
6. 실시간 서버 시작 (Launch the Realtime Server)
realtime 파이프라인을 고정해서 시작합니다 — 체크포인트 기본값은 SanaWMTwoStagePipeline이므로 realtime을 명시적으로 선택해야 합니다(§3 참조). /v1/realtime_video 라우터는 항상 마운트되며 SanaWMRealtimeConfig에 등록된 realtime 어댑터(SanaWMRealtimeAdapter)가 있어 realtime 구성이 활성화되면 기능합니다.
sglang serve \
--model-path Efficient-Large-Model/SANA-WM_streaming \
--pipeline-class-name SanaWMRealtimePipeline \
--host 127.0.0.1 --port 30000
일반적인 시작 변형:
# recommended multi-GPU realtime profile
sglang serve \
--model-path Efficient-Large-Model/SANA-WM_streaming \
--pipeline-class-name SanaWMRealtimePipeline \
--num-gpus 8 --sp-degree 8 \
--host 127.0.0.1 --port 30000
# single GPU
sglang serve \
--model-path Efficient-Large-Model/SANA-WM_streaming \
--pipeline-class-name SanaWMRealtimePipeline \
--num-gpus 1 --host 127.0.0.1 --port 30000
# offload DiT + text encoder to CPU (tight VRAM)
sglang serve \
--model-path Efficient-Large-Model/SANA-WM_streaming \
--pipeline-class-name SanaWMRealtimePipeline \
--host 127.0.0.1 --port 30000 \
--dit-cpu-offload --text-encoder-cpu-offload
시작 동작 참고:
- 기본 엔드포인트는
127.0.0.1:30000(--host/--port가 오버라이드). - CPU 오프로드 플래그는 선택 사항입니다.
--dit-cpu-offload,--text-encoder-cpu-offload,--image-encoder-cpu-offload를 사용할 수 있습니다. 기본값은 GPU 메모리에서 자동 조정됩니다(30 GB 미만 GPU는 더 공격적 오프로드). - 멀티 GPU realtime. 단일 세션에는 명시적 시퀀스 병렬(
--sp-degree를 GPU 수로)을 선호하세요. realtime 프로필에는 CFG 병렬을 활성화하지 마세요. 기본 요청은guidance_scale=1.0을 사용해 추가 CFG 랭크가 같은 분지만 재계산하기 때문입니다. - FSDP. 메모리를 위한 가중치 샤딩이 구체적으로 필요할 때만
--use-fsdp-inference를 사용하세요. 저지연 realtime 프로필에는 컴포넌트를 상주시키고 SP를 먼저 사용하는 것을 선호하세요. - 워밍업. realtime 파이프라인에는 서버 워밍업이 자동으로 건너뜁니다. 합성 워밍업 요청에는 WebSocket 세션이 없으므로 서버가 등록된 realtime 어댑터를 감지하고 건너뜁니다. 명시적
--warmup-mode설정이 필요 없습니다.
올라오면 realtime WebSocket 엔드포인트는 ws://127.0.0.1:30000/v1/realtime_video/generate에 있습니다(연결하려면 §7의 Python 클라이언트를 사용 — 일반 curl은 ws:// 업그레이드를 말하지 않습니다).
7. Realtime WebSocket API
realtime API는 /v1/realtime_video/generate 의 단일 WebSocket입니다. 모든 메시지 — 클라이언트 → 서버 및 서버 → 클라이언트 — 는 msgpack(msgspec.msgpack.encode / decode)이며 JSON이 아닙니다.
수명주기:
INIT 메시지
RealtimeVideoGenerationsRequest(type은 리터럴 "init"). 핵심 필드:
| 필드 (Field) | 타입 (Type) | 참고 (Notes) |
|---|---|---|
type |
"init" |
필수 리터럴 |
prompt |
str | 텍스트 프롬프트 |
first_frame |
bytes | str | SANA-WM 어댑터에 필수(on_init은 없으면 raise. 일반 요청 스키마는 선택으로 정의). 원시 이미지 바이트, 서버 측 경로, 또는 http(s):// URL(다운로드·캐시) |
condition_inputs |
dict | 카메라/컨디셔닝 입력(아래) |
num_frames |
int | 생성할 총 프레임. 개방형 연속 세션에는 생략 — 어댑터는 num_frames를 설정하지 않고 개방형 실행을 표시(condition_inputs["sana_wm_open_ended"] = True)하며 max_chunks나 클라이언트 연결 해제까지 무기한 균등 청크를 생성. 고정 길이 클립에는 정수 제공 |
seed |
int | RNG 시드(기본 42) |
size |
str | "WIDTHxHEIGHT"; realtime 요청은 지연을 위해 기본값 "832x480". 네이티브 가로 해상도는 "1280x704" 전달 |
max_chunks |
int | 생성할 총 청크의 선택적 상한 |
num_inference_steps |
int | SANA-WM(realtime 어댑터)의 기본값 4 |
guidance_scale |
float | 기본값 1.0 |
realtime_output_format |
"raw" | "webp" | "jpeg" |
출력용 프레임 인코딩(아래) |
realtime_causal_sink_size |
int | 선택적 오버라이드 |
realtime_causal_kv_cache_num_frames |
int | 선택적 오버라이드 |
condition_inputs는 다음을 받습니다(모두 선택. action / camera_actions 중 하나만 전달):
| 키 (Key) | 타입 (Type) | 의미 (Meaning) |
|---|---|---|
camera_actions |
list[list[str]] 또는 {mode: "state", transitions: [...]} |
프레임별 카메라 동작, 또는 상태 기반 전환 |
action |
str | Action-DSL 문자열, 예: "w-10,none-5,a-8"(§8 참조) |
intrinsics_path |
str | 카메라 intrinsics .npy 파일의 서버 측 경로(np.load로 로드; 형태 (4,), (3,3), 또는 (F,3,3)) |
intrinsics |
list | 형태 (4,), (3,3), (F,4), 또는 (F,3,3)의 인라인 intrinsics |
intrinsics_path와 intrinsics를 둘 다 생략하면 SGLang은 첫 프레임 크기에서 유도된 중심 heuristic intrinsics 행렬을 사용합니다. 준비된 궤적과 더 가까운 카메라 패리티가 필요하면 명시적 intrinsics를 전달하세요.
{
"type": "init",
"prompt": "beautiful landscape video",
"first_frame": "<bytes or url>",
"size": "832x480",
"seed": 42,
"max_chunks": 10,
"realtime_output_format": "raw",
"num_inference_steps": 4,
"guidance_scale": 1.0,
"condition_inputs": {
"camera_actions": [["w"], [], ["a", "s"]],
"intrinsics_path": "/path/to/intrinsics.npy"
}
}
라이브 EVENT 메시지
RealtimeEvent(type: "event"). kind + payload를 사용합니다(선택적 event_id가 응답을 이 이벤트에 연관).
{
"type": "event",
"kind": "camera_actions",
"event_id": 1,
"payload": [["w"], ["w"], ["a"], []]
}
{
"type": "event",
"kind": "camera_actions",
"event_id": 2,
"payload": {
"mode": "state",
"transitions": [
{"actions": ["w"], "client_ts_ms": 1000},
{"actions": ["a", "w"], "client_ts_ms": 1500}
]
}
}
{
"type": "event",
"kind": "action",
"event_id": 3,
"payload": "w-10,none-5,a-8,d-10"
}
서버 프레임 출력
서버는 프레임 배치를 스트리밍합니다. 모든 배치는 type: "frame_batch"의 단일 msgpack 메시지로 도착합니다 — 아래 헤더 필드 + 인라인 payload 바이트 필드(와이어 type은 항상 "frame_batch". 별도의 header-then-bytes 메시지는 없습니다).
헤더 필드:
| 필드 (Field) | 의미 (Meaning) |
|---|---|
type |
"frame_batch"(항상) |
request_id |
생성 id |
chunk_index |
청크 인덱스 |
content_type |
application/x-raw-rgb, application/x-raw-rgb-delta-gzip, image/webp, 또는 image/jpeg |
num_frames |
이 배치의 프레임 수 |
total_size |
페이로드 크기(바이트)(len(payload) — delta-gzip의 경우 압축 크기) |
width, height, channels |
프레임 지오메트리(channels: 3) |
bytes_per_frame |
압축하지 않은 프레임당 바이트(width*height*3) |
format |
raw의 경우 rgb24 |
encoding |
raw, delta-gzip, webp, 또는 jpeg |
delta_reference |
previous-frame(delta-gzip에 존재) |
event_id |
조종 event id 반향; INIT 전용 청크의 헤더에서는 생략됨 |
frame_batch_index, num_frame_batches |
청크 내 여러 배치 시퀀스 |
is_final_frame_batch |
true가 청크를 종료 |
{
"type": "frame_batch",
"request_id": "uuid-string",
"chunk_index": 0,
"content_type": "application/x-raw-rgb-delta-gzip",
"num_frames": 3,
"total_size": 1048576,
"width": 1280,
"height": 704,
"channels": 3,
"bytes_per_frame": 2703360,
"format": "rgb24",
"encoding": "delta-gzip",
"delta_reference": "previous-frame",
"event_id": 1,
"frame_batch_index": 0,
"num_frame_batches": 1,
"is_final_frame_batch": true,
"payload": "<gzip-compressed bytes>"
}
인코딩. application/x-raw-rgb는 압축하지 않은 RGB24(3 × uint8, bytes_per_frame = width*height*3)입니다. application/x-raw-rgb-delta-gzip은 앞 프레임에 대한 zlib-압축 프레임별 XOR 델타입니다(배치의 각 프레임은 이전 프레임에 대해 XOR됨; 기본 전송). realtime_output_format: "raw"는 압축하지 않은 RGB를 강제하고, "webp" / "jpeg"는 미리보기 인코딩된 프레임을 보냅니다.
최소 클라이언트 예시
import msgspec
import numpy as np
import websockets # pip install websockets
WS_URL = "ws://127.0.0.1:30000/v1/realtime_video/generate"
async def run():
async with websockets.connect(WS_URL, max_size=None) as ws:
# 1) INIT — omit num_frames for an open-ended session; "raw" = uncompressed RGB24
with open("first_frame.png", "rb") as f:
first_frame = f.read()
await ws.send(msgspec.msgpack.encode({
"type": "init",
"prompt": "a camera moving forward and turning right",
"first_frame": first_frame,
"size": "832x480",
"seed": 42,
"max_chunks": 10,
"realtime_output_format": "raw",
"num_inference_steps": 4,
"guidance_scale": 1.0,
"condition_inputs": {
"action": "w-100,wd-50,d-30",
"intrinsics_path": "/path/to/intrinsics.npy", # optional; centered heuristic if omitted
},
}))
# 2) optional: steer mid-stream
await ws.send(msgspec.msgpack.encode({
"type": "event",
"kind": "camera_actions",
"event_id": 1,
"payload": [["w"], ["w"], ["a"], []],
}))
# 3) receive frame batches (raw RGB24)
async for message in ws:
msg = msgspec.msgpack.decode(message)
if msg.get("type") != "frame_batch":
continue # skip chunk_stats etc.
n, h, w, c = msg["num_frames"], msg["height"], msg["width"], msg["channels"]
frames = np.frombuffer(msg["payload"], dtype=np.uint8).reshape(n, h, w, c)
# ... display/save frames ...
if msg.get("is_final_frame_batch") and msg.get("chunk_index", 0) >= 9:
break
# asyncio.run(run())
8. 카메라 Action DSL (Camera Action DSL)
카메라 궤적은 쉼표로 구분된 <keys>-<frames> 세그먼트의 컴팩트 문자열로 설명됩니다. 예: "w-100,wd-50,d-30,none-10". 이것이 init 시 condition_inputs.action과 kind: "action" 이벤트가 받는 형식입니다.
파싱 규칙(parse_action_string):
- 각 세그먼트는
<keys>-<frames>이며<frames>는 양의 정수여야 합니다. none은 그 구간에 움직임이 없음을 뜻합니다:none-10= 정적 10 프레임.- 키는 대소문자 구분 없음. 결합 키는 동시 적용(
wd= 전진 + 우측 스트레이프). 허용 키는 정확히wasdijkl.
| 키 (Key) | 동작 (Motion) |
|---|---|
w / s |
전진 / 후진 |
a / d |
좌 / 우 스트레이프 |
i / k |
위 / 아래 보기(pitch) |
j / l |
좌 / 우 보기(yaw) |
포즈 생성(action_string_to_c2w):
- 이동(
w/s/a/d)은translation_speed(기본0.04world-units/frame)로 움직입니다. - 회전(
i/kpitch,j/lyaw)은rotation_speed_deg(기본1.2°/frame)로 돌며 pitch는 ±85°로 클램프됩니다. - 스트레이프-요 결합(계수
0.4):d(우) 스트레이프는 요를 오른쪽으로,a(좌)는 왼쪽으로 살짝 돌리므로wd는 순수 횡보가 아니라 곡선 호를 그립니다. (F+1, 4, 4)camera-to-world 행렬을 만듭니다. realtime 단계는 궤적을 요청 프레임 수로 패딩합니다.
예시: "w-100,wd-50,d-30,none-10" = 전진 100 프레임 → 전진 + 우측 스윕 50 프레임 → 우측 스트레이프 30 프레임 → 정적 10 프레임.
9. 구성 참조 (Configuration Reference)
SANA-WM의 기본값은 세 곳에 있습니다: 요청 시점 샘플링 파라미터, 파이프라인 구성(streaming/refiner 노브), realtime 어댑터(init 시점 오버라이드).
요청 시점 — SanaWMSamplingParams (configs/sample/sana_wm.py)
| 필드 (Field) | 기본값 (Default) | 목적 (Purpose) |
|---|---|---|
height |
704 |
출력 높이 |
width |
1280 |
출력 너비 |
num_frames |
49 |
총 픽셀 프레임((num_frames - 1) % 8 == 0 충족 필요) |
fps |
16 |
출력 프레임 레이트(기본 24 오버라이드) |
num_inference_steps |
20 |
Stage-1 스텝 수 |
guidance_scale |
4.5 |
Dense 경로 CFG 스케일 |
negative_prompt |
"" |
네거티브 프롬프트 |
camera_to_world |
None |
인메모리 (T,4,4) c2w extrinsics(action과 상호 배타) |
intrinsics |
None |
인메모리 (T,3,3) pinhole intrinsics |
action |
None |
Action-DSL 문자열(§8 참조) |
translation_speed |
0.04 |
W/S/A/D용 world-units/frame |
rotation_speed_deg |
1.2 |
I/K/J/L용 degrees/frame |
pitch_limit_deg |
85.0 |
Pitch 클램프 |
generator_device는 기본 SamplingParams에서 상속됩니다(기본 None = 파이프라인/모델 기본 사용). /v1/videos HTTP API에서 카메라 필드는 diffusers_kwargs 안으로 전달됩니다(action / intrinsics, §4–5처럼).
파이프라인 구성 — SanaWMPipelineConfig (configs/pipeline_configs/sana_wm.py)
이들은 서버 시작 노브(--streaming / --refiner-chunked / --num-frame-per-block CLI 플래그 또는 파이프라인 구성 오버라이드로 설정)이며 요청 필드가 아닙니다:
| 필드 (Field) | 기본값 (Default) | 목적 (Purpose) |
|---|---|---|
streaming |
False |
Chunk-causal forward_long(§5) vs dense one-shot(§4) |
refiner_chunked |
True |
청크별 스트리밍 리파이너 vs 전체-클립 dense 리파이너 |
num_frame_per_block |
3 |
Stage-1 / refiner 청크당 잠재 프레임 |
num_cached_blocks |
2 |
롤링 KV-캐시 기록 창 |
denoising_step_list |
(1000, 960, 889, 727, 0) |
4-스텝 스트리밍 self-forcing 타임스텝(0으로 끝나야 함) |
streaming_cfg_scale |
1.0 |
증류 streaming 경로의 CFG 스케일(1.0 = off) |
sink_size |
1 |
Sink(리파인되지 않은 컨텍스트) 프레임 |
refiner_block_size |
3 |
리파이너 블록 크기 |
refiner_kv_max_frames |
11 |
리파이너 슬라이딩 KV 창 |
Realtime 어댑터 init 오버라이드 — SanaWMRealtimeAdapter
WebSocket init 시 realtime 어댑터는 위의 요청/샘플링 기본값과 다른 SANA-WM 기본값을 채웁니다:
| 필드 (Field) | Realtime 기본값 (Realtime default) | 참고 (Note) |
|---|---|---|
size |
832x480 |
Realtime 요청 기본값; 네이티브 가로 출력은 1280x704 전달 |
num_frames |
(unset) | 생략 → 개방형 연속 세션(§7) |
num_inference_steps |
4 |
증류 few-step |
guidance_scale |
1.0 |
CFG off |
fps |
16 |
네이티브 레이트 |