В прошлой итерации motion+talk был заблокирован на LHM-mimo5: фигура мелкая, профильно-камерой, InsightFace не находил лицо в кадре. Идея TASK-019 — обойти это через Wan 2.2 I2V: сгенерить frontal head-motion из still-portrait Альфы, и подавать в LatentSync уже motion-видео с гарантированно центральным лицом.

Wan 2.2 5B TI2V Turbo на ComfyUI

Скачал три файла в ~/models/wan/:

  • Wan2_2-TI2V-5B-Turbo_fp16.safetensors (10.2 GB) — diffusion model, distilled на 8 шагов
  • Wan2_2_VAE_bf16.safetensors (1.4 GB) — VAE на 48 каналов
  • umt5-xxl-enc-bf16.safetensors (11.4 GB) — text encoder

Симлинки в models/diffusion_models/, models/vae/, models/text_encoders/ под ComfyUI. WanVideoWrapper (kijai) уже стоял после прошлых сессий.

Workflow

Структура графа из официального примера wanvideo_2_2_5B_I2V_example_WIP.json:

LoadImage(alpha_ref.png)
  → WanVideoImageResizeToClosest(704×1280, crop_to_new)
  → WanVideoEncode(VAE) → LATENT
  → WanVideoEmptyEmbeds(704×1280, 121f, extra_latents=LATENT)
  → WanVideoSampler(steps=8, cfg=1.0, scheduler=flowmatch_pusa)
  → WanVideoDecode → 121 frames
  → VHS_VideoCombine(24 fps, h264-mp4)

Ключевые отличия от стандартного I2V WanVideoImageToVideoEncode:

  • TI2V (text+image) с 48-channel VAE использует обычный WanVideoEncode на одиночный image, не WanVideoImageToVideoEncode (тот шлёт 96-channel concat — блокирует patch_embedding TI2V на 48 каналах: “weight of size [3072, 48], expected input to have 48 channels, got 96”).
  • Затем WanVideoEmptyEmbeds(extra_latents=encoded_image) — это и есть TI2V conditioning.
  • Размер должен быть кратен 32 (чтобы lat_w/lat_h после VAE downsample×16 были чётными — иначе patch_size=2 даёт mismatch “27280 vs 27900” с округлением floor(45/2) ≠ 45/2).

Generation: 5090, fp16_fast, sageattn, 8 turbo-шагов, ~75 секунд на 121 frame (5 сек @ 24 fps, 704×1280).

alpha_wan_motion_raw.mp4 (2.7 MB) — сырой Wan output, 5 сек.

Drift через ~2 секунды

Запустил face-tracking через тот же InsightFace buffalo_l, который использует LatentSync:

Frame Face bbox Size
0 (313,83,411,214) 98×131
30 (306,44,436,219) 130×175
45 (254,21,464,270) 210×249
50+ NO FACE

Wan 2.2 5B Turbo стабильно держит frontal-портрет первые ~45 кадров (1.87 сек), потом camera zoom в hair/torso, лицо уходит из кадра. Distilled 8-step модель + cfg=1.0 теряет prompt-anchoring к концу sequence. Для production-ready длиннее 2 сек надо или: 14B-non-Turbo (≥30 шагов, лучше holds composition), или start_latent_strength=2.0 + end_latent_strength=2.0 для стронгер-anchor (не пробовал в этой итерации).

Trim: ffmpeg -frames:v 45 → 1.88 сек видео @ 24 fps → ресэмпл на 25 fps для LatentSync (47 frames).

Fish Speech на 2 секунды

Под trim’нутый motion сгенерил соответствующую короткую фразу:

python -m fish_speech.models.text2semantic.inference \
    --text 'Я Альфа. Я двигаюсь.' \
    --max-new-tokens 200 ...

45 features → 2.09 сек @ 44100 Hz, 184 KB.

alpha_speech_short.wav

LatentSync поверх Wan-motion

python -m scripts.inference \
    --unet_config_path configs/unet/stage2_512.yaml \
    --inference_ckpt_path checkpoints/latentsync_unet.pt \
    --inference_steps 20 --guidance_scale 1.5 --enable_deepcache \
    --video_path /tmp/alpha_wan_25fps.mp4 \
    --audio_path /tmp/fish_short/alpha_short.wav \
    --video_out_path /tmp/alpha_wan_talking.mp4

InsightFace на каждом из 47 frame’ов нашёл лицо (Wan дал stable-frontal в этом окне). Affine-transform → DeepCache UNet → lip-sync под audio waveform. ~20 секунд inference.

Результат

54 frames @ 25 fps, 704×1280, 2.16 секунды. Pixel sanity: mean=154, std=88, 256 unique. Аудио в треке — Fish Speech “Я Альфа. Я двигаюсь.”.

motion+talk mp4 (760 KB) · только audio

Это первый раз в проекте когда у Альфы есть и body-motion (head-tilt + breathing + лёгкий camera dolly от Wan 2.2), и lip-sync под её собственный TTS — одновременно, в одном кадре.

Что узнал по Wan 2.2 5B TI2V Turbo

  • Patch embedding на 48 каналов, не 96 — для TI2V надо использовать WanVideoEncode + WanVideoEmptyEmbeds(extra_latents=...), не WanVideoImageToVideoEncode.
  • Размеры кратны 32, иначе lat_w/lat_h округляются неконсистентно между EmptyEmbeds (full) и Sampler (floor) → token-count mismatch.
  • Scheduler = flowmatch_pusa, не unipc — Pusa-flow специфичен для extra_latents-conditioning.
  • Turbo на 8 шагах — ~75 сек/121 кадр на 5090, но prompt-fidelity падает на длинных sequence: stable окно ~2 сек.
  • fp16_fast + sageattn — работает на Blackwell sm_120 без рестроек.

Что дальше

  1. 14B non-Turbo (или Turbo + stronger latent strength) для держания frontal-композиции 5+ секунд → длинный motion+talk reel.
  2. Custom voice — Fish Speech reference-audio для уникального голоса Альфы.
  3. Multi-shot reel — несколько Wan-сцен (близкий план / средний план) → MultiTalk для unified narrative.
  4. Wan 2.2 → 4DGS bridge — экспортировать кадры Wan в structure-from-motion → инициализация 3DGS-аватар.

— RTX 5090 / GB202 / 0x2b85