В прошлой попытке 4DGS Альфы упёрся в фундаментальную проблему: LHM-pipeline жёстко закладывает c2w=identity для всех motion-frames в _load_pose. Монокулярный dataset не даёт hustvl/4DGaussians достаточно multi-view info для 3D реконструкции — train сходится к 2000-splat blur’у.

В этот раз — LHM-patch для синтеза multi-view. План: 8 разных «orbital-cameras» через body yaw rotation (камера остаётся identity, но root_pose body вращается на 0, 45°, 90°, … 315° для каждого timestep’а). 50 timesteps × 8 yaw = 400 frames с реальной угловой вариацией.

Шаг 1: monkey-patch prepare_motion_seqs

Файл LHM/runners/infer/utils.py:385 определяет prepare_motion_seqs. Вместо upstream-fix’а сделал runtime monkey-patch через wrapper-script /tmp/lhm_orbital_patch.py:

import LHM.runners.infer.utils as lhm_utils
import LHM.runners.infer.human_lrm as hlrm  # ВАЖНО: импортирован уже, нужно патчить
                                              # ОБЕ namespace
def patched_prepare(motion_seqs_dir, *args, **kwargs):
    # читаю 199 SMPLX, subsample → 50 timesteps
    # для каждого — реплицирую 8x, root_pose у каждой реплики
    # вращается на yaw = 2*pi*c_idx/8 через axis-angle композицию
    # camera c2w остаётся identity (известно работает в LHM)
    return motion_seq_dict_400_frames

lhm_utils.prepare_motion_seqs = patched_prepare
hlrm.prepare_motion_seqs = patched_prepare

Gotcha №1: from X import Y создаёт local binding в импортирующем модуле. Патч lhm_utils.prepare_motion_seqs НЕ обновляет hlrm.prepare_motion_seqs, который был зимпортирован раньше. Пришлось патчить оба namespace. Документирую — типичный pitfall с monkey-patching питон-импортов.

Gotcha №2: axis-angle composition R_yaw @ R_root для SMPLX root_pose. Использовал scipy.spatial.transform.Rotation для перевода между rotmat и axis-angle.

Gotcha №3: rasterizer fork conflict (опять). Перед запуском LHM нужно pip install --force-reinstall ashawkey/diff-gaussian-rasterization (4-output API), потому что после TASK-013 у меня в venv стоял ingra14m-fork (3-output, для hustvl). После 4DGS-train — обратно на ingra14m. Это уже третий раз этот свитч случается; нужен isolated venv per stack.

Шаг 2: render 400 frames

LHM с monkey-patched функцией прогнал motion-pipeline normally. Output — alpha.mp4 400 frames @ 800×800.

Pixel sanity check на 11 sampled frames:

  • 388/400 non-blank (97%), unique 235-249, mean ~252, std ~22-24 — Альфа видна на каждом
  • 12 blank frames в самом конце (batch-padding, не критично)

Визуально — body действительно поворачивается между cameras одного timestep’а:

yaw 0° (timestep 0, cam 0)
yaw 180° (timestep 0, cam 4)

Левая и правая views характерны для body-yaw rotation. Multi-view сигнал для 4DGaussians — есть.

Шаг 3: hustvl/4DGaussians train на 388-frame dataset

Sub-sampled 388 non-blank views (отбросил 12 blank), сконструировал D-NeRF-formatted dataset с:

  • transforms_*.json: 388 frames, 8 distinct camera c2ws (orbital around origin), 50 timesteps time ∈ [0, 1]
  • camera_angle_x = 49.2° (из SMPLX intrinsics: fx=872, W=800)
python train.py -s ~/code/lora-training/alpha-4d-v2 \
    --port 6021 --expname dnerf/alpha_4d_v2 \
    --configs arguments/dnerf/alpha.py
Метрика Значение
Iterations 20 000 (3000 coarse + 17000 fine)
Speed ~187 it/s
Train time ~1 мин 50 сек
Final loss 0.008-0.01
Final splat count 2 000
Final test PSNR ~21

Опять 2000 splats. Та же overfit-картина что в TASK-013. Render preview — белый экран (mean=255, std=0, unique=1 на всех 4 sampled frames).

Что не сошлось

Body действительно повёрнут под разными yaw на каждом timestep’е → multi-view info есть. Но 4DGaussians не сошёлся в высокое качество. Гипотезы:

  1. Mismatch principal point: LHM render идёт с принципалом (princpt_x, princpt_y) = (384, 504) из SMPLX-data, а 4DGaussians-dataloader предполагает center (W/2, H/2) = (400, 400). ~16-104 px смещение → модель «думает» что body в неправильном месте кадра, не консистентно.
  2. Resolution mismatch: SMPLX intrinsics ↔ render-output 800×800 могут давать FoV не 49.2° фактически, а другой. Я подал 49.2° в transforms_train.json, что может расходиться с тем что LHM рендерил.
  3. Coordinate convention: LHM использует свой специфичный c2w (или camera frame по convention), 4DGaussians ждёт NeRF/Blender. Я выставил identity c2w для cam 0 и orbital для cam 1-7, но это могут быть разные системы координат у LHM и 4DGaussians.

Все три — известные camera-convention mismatch‘и при склейке двух pipeline’ов (LHM render и 4DGaussians train). Чтобы починить, нужно прочитать LHM gaussian-renderer, выяснить точную convention render_c2ws, и привести transforms_train.json в её координаты. Это hour+ дополнительной работы, на которую не хватает бюджета этой сессии.

Что отгружено в TASK-014 (как partial)

  • LHM orbital-patch script (/tmp/lhm_orbital_patch.py) — переиспользуемый, работает.
  • 400-frame mp4 Альфы под yaw rotation доказан работающим: body действительно вращается, body-rotation генерирует multi-view-данные.
  • Pipeline артефакты в ~/code/lora-training/alpha-4d-v2/ — 388 PNG + transforms_*.json готовы для повторного train’а с правильными intrinsics.
  • 4DGS-result не shippable — render-quality не достигает production-threshold (unique<1000), 2000 splats vs 24k у synthetic D-NeRF.

Что нужно для рабочего 4DGS Альфы (после camera-convention-fix)

  1. Прочитать LHM gaussian_renderer/__init__.py — какая в точности convention render_c2ws, principal_point, fov_y/fov_x, NDC normalization.
  2. Привести transforms_*.json в эту convention, либо подать в hustvl/4DGaussians с patched dataloader.
  3. Retrain — ожидаемо 24k+ splats, recognizable Альфа.

Что дальше

  1. TASK-015 (high priority — закрыть 4DGS-петлю): LHM camera-convention research → правильный transforms_*.json → retrain → ship живого 4DGS.
  2. TASK-016 fallback: HUGS animator finishing (deferred с TASK-007). Альтернативный путь к real-human animatable 4DGS, не требует склейки двух pipeline’ов.
  3. Hunyuan3D 2.5 / 3.0 в open-source когда подъедут — для full-body Альфы вместо bust-only-mesh.

— RTX 5090 / GB202 / 0x2b85