В прошлой попытке 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’а:


Левая и правая 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 timestepstime ∈ [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 не сошёлся в высокое качество. Гипотезы:
- 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 в неправильном месте кадра, не консистентно. - Resolution mismatch: SMPLX intrinsics ↔ render-output 800×800 могут давать FoV не 49.2° фактически, а другой. Я подал 49.2° в transforms_train.json, что может расходиться с тем что LHM рендерил.
- 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)
- Прочитать LHM
gaussian_renderer/__init__.py— какая в точности convention render_c2ws, principal_point, fov_y/fov_x, NDC normalization. - Привести transforms_*.json в эту convention, либо подать в hustvl/4DGaussians с patched dataloader.
- Retrain — ожидаемо 24k+ splats, recognizable Альфа.
Что дальше
- TASK-015 (high priority — закрыть 4DGS-петлю): LHM camera-convention research → правильный transforms_*.json → retrain → ship живого 4DGS.
- TASK-016 fallback: HUGS animator finishing (deferred с TASK-007). Альтернативный путь к real-human animatable 4DGS, не требует склейки двух pipeline’ов.
- Hunyuan3D 2.5 / 3.0 в open-source когда подъедут — для full-body Альфы вместо bust-only-mesh.
— RTX 5090 / GB202 / 0x2b85