Reconstruction · Human Motion Postprocess¶
本页属于 Reconstruction:它负责 refinement \(\widehat H_t\),输出 Motion Smooth + World Root Contact 后的 human reference。早期 E2E 方案、诊断和 J2D loss 计划已移入
../archive/,需要追溯时再读取。
日期:2026-04-25
本文修正上一版计划。相机是 static,不能把当前视频 overlay 抖动归因成 camera motion。
原计划拆成两步串联:camera-frame smooth → world contact opt。本版合并为 单次 joint world-space 优化:T_c2world 从 depth RANSAC 预计算固定,然后一次 Adam run 同时优化 FK-output world-space smoothness + foot contact。
参考论文:[2512.21573] World-Coordinate Human Motion Retargeting via SAM 3D Body。该论文提供三阶段框架,但没有给出足够工程细节;下面的方案必须以本 repo 的 MHR 参数定义和已有 physics pipeline 为准。
0. 当前已知事实¶
0.1 本项目当前 best baseline¶
现有视觉 best 是 D2b_body_all_euler_w5_s1p0_b0p35:
路径:
submodules/sam-body4d/outputs/postprocess_attribution_b009_0001_20260425/D2b_body_all_euler_w5_s1p0_b0p35
0.2 静态相机下 overlay 抖动的来源¶
SAM-3D-Body overlay 里相机本身不动。投影大致是:
所以 overlay jitter 的候选来源是 motion 参数,而不是 camera motion:
global_rotbody_posehand_posepred_cam_t,注意这是人体在 camera frame 下的 translation / projection 输入,不是相机运动shape/scaleflicker- hand decoder wrist write-back / body decoder wrist merge
0.3 MHR 参数表示不能按普通 SMPL rotvec 处理¶
本 repo 的关键代码事实:
global_rot是 MHR forward 接收的 Euler 参数,不是 rotvec。body_pose缓存为(T, 133),但mhr_forward()实际只使用body_pose[..., :130]。body_pose不是(J, 3)axis-angle,而是 MHR compact 参数展开后的混合表示:3DoF Euler groups、1DoF angles、translation-like params。scale缓存为 28D coefficient,经过scale_mean + scale @ scale_comps映射到论文所说的 68D skeleton scale。- 旋转 smooth / rotation loss 应该通过
mhr_forward(..., return_joint_rotations=True)得到的joint_global_rots来做,而不是把body_pose每 3 维当 SO3。
论文里的 low-dimensional MHR latent space 是成立的,但这里的 latent 指的是 MHR 的结构化低维 rig 参数,而不是 VAE bottleneck 或任意 learned embedding。它低维是相对 mesh vertices、dense skinning state、逐点 surface motion 而言;在我们代码里对应的可优化量主要是 global_rot + body_pose[:130] + scale/shape/expr/hand 这些 MHR 参数。
因此实现上应该:
- 在 MHR 参数空间里优化,保持 fidelity 到原始 per-frame estimate。
- 用
mhr_forward()把参数解码到j3d/joint_global_rots后计算 temporal smoothness。 - 不把
body_pose简化成普通(J, 3)rotvec,也不把pred_pose_raw当作一定可直接复用的稳定 latent,除非确认 forward path 支持。
相关代码:
submodules/sam-body4d/models/sam_3d_body/sam_3d_body/models/heads/mhr_head.py
submodules/sam-body4d/models/sam_3d_body/sam_3d_body/models/modules/mhr_utils.py
1. 总体 Pipeline¶
pre_smooth_mhr.npz
-> [A] Identity / Scale Locking (temporal median shape/scale)
-> [C] World-frame Ground Estimation (depth RANSAC,预计算固定)
-> [B] Joint World-Space Optimization (单次 Adam,batch mhr_forward)
= L_latent + L_smooth(world) + L_contact(world)
-> smoothed_world_mhr.npz + overlay + world_root / physics input
最终目标:
- 视频 overlay 更平滑:L_latent + L_smooth 共同保证。
- world space root 物理合理:L_contact 同时处理 foot slide + penetration。
- 单次优化完成两件事,不需要两次独立 Adam。
Stage C(depth RANSAC)作为预计算,输出固定 T_c2world 给 Stage B。Stage B 把 T_c2world 当 constant,不参与优化。
1.1 代码合并后的默认路线¶
当前工程合并目标改为:
默认 human motion provider = sam3d_body
默认 world frame provider = depth_plane_zup
默认假设 = static camera + metric/aligned depth 能可靠看到一部分地面或支撑平面
这意味着默认路线不再要求先跑 GVHMR 来提供 T_c2world:
RGB/mask/depth/K
-> SAM3D Body per-frame MHR
-> shape/scale lock + MHR parameter smoothing
-> metric depth plane fit in camera frame
-> T_c2world_depth_plane, ground_z=0
-> smoothed camera-frame human -> Z-up world
-> contact-aware root optimization
-> physics input
GVHMR 路线保留为兼容 provider,而不是默认依赖:
human.model = gvhmr
-> 继续使用现有 GVHMR motion/result.pt
-> 继续支持现有 depth ground correction / T_c2world 读取逻辑
这里不设计自动 fallback。sam3d_body 与 gvhmr 由 config 明确选择;当前实验和默认配置按 sam3d_body 走。
2. Stage A:Identity / Scale Locking¶
2.1 目的¶
消除同一 track 内 body shape / skeleton scale flicker。
2.2 实现方式¶
论文说的是 temporal mean。我们实际缓存是:
先做 robust temporal locking:
shape_final = robust_mean_or_median(shape[t] over valid frames)
scale_final = robust_mean_or_median(scale[t] over valid frames)
shape[:] = shape_final
scale[:] = scale_final
建议比较三组:
默认从 A2 开始,因为第一帧可能不是最稳定帧。
3. Stage B:Joint World-Space Optimization¶
3.1 目标¶
单次 Adam 优化同时解决: - Camera-frame overlay 抖动(L_latent + L_smooth) - World-space foot contact / sliding / penetration(L_contact)
在 world space 做 smooth 比 camera frame 更合理:gravity 方向正确,foot contact 约束自然。
3.2 前置条件¶
Stage C 已完成,T_c2world 固定:
3.3 优化变量¶
global_rot: (T, 3) # root orientation
body_pose: (T, 130) # mhr_forward 实际使用部分
pred_cam_t: (T, 3) # 强 fidelity 约束,小 lam
hand_pose 已由 D0 hand smoothing 处理,不参与本次优化。
3.4 Batch mhr_forward¶
关键:每次 iter 对全部 T 帧做一次 batched forward,不逐帧循环:
# params: (T, D) PyTorch tensors with requires_grad=True
j3d_local, joint_global_rots = mhr_forward(global_rot, body_pose) # (T, J, 3), (T, J, 3, 3)
j3d_cam = j3d_local + pred_cam_t[:, None, :] # (T, J, 3)
j3d_world = (R_c2w @ j3d_cam.reshape(T, J, 3, 1)).squeeze(-1) + t_c2w # (T, J, 3)
mhr_forward 必须支持 PyTorch tensor 输入和 autograd。如果当前实现是 numpy,需要:
- 方案 A:port 到 torch(推荐,一劳永逸)
- 方案 B:torch.autograd.Function 包装,forward 走 numpy,backward 手动 Jacobian
3.5 Loss 设计¶
L_latent(参数保真)¶
L_latent =
(1/D_g) * ||global_rot - global_rot0||^2
+ (1/D_b) * ||body_pose - body_pose0 ||^2
+ (1/D_c) * w_cam * ||pred_cam_t - pred_cam_t0||^2
per-group normalize 避免 130D body 压倒 3D global。
L_smooth(FK-output world-space smooth)¶
论文原文公式(Eq. 3-5),在 world space 计算:
linear velocity: v_t^j = j3d_world[t+1,j] - j3d_world[t,j]
linear acceleration: a_t^j = j3d_world[t+2,j] - 2*j3d_world[t+1,j] + j3d_world[t,j]
angular velocity: ω_t^j = Log(R[t+1,j] @ R[t,j].T)
angular acceleration:α_t^j = ω[t+1,j] - ω[t,j]
L_smooth = Σ_j w_j * (
λ_v * Σ_t ρ(||v_t^j||)
+ λ_a * Σ_t ρ(||a_t^j||)
+ λ_ω * Σ_t ρ(||ω_t^j||)
+ λ_α * Σ_t ρ(||α_t^j||)
)
Charbonnier: ρ(x) = sqrt(x^2 + ε)
joint weight w_j(论文原则:trunk > extremities > face):
root / pelvis / spine : 1.0
shoulder / hip : 0.8
elbow / knee : 0.6
wrist / ankle : 0.5
hand / foot joints : 0.1
face : 0.0
主要靠 λ_a(acceleration)和 λ_α(angular acc)。λ_v 不能太大,否则真实动作被压软。
L_contact(foot contact,world space)¶
heel indices:left=17, right=20(MHR70 定义)
d_f = j3d_world[:, foot_idx, 2] - ground_z # (T,),foot height above ground
p_c = exp(-d_f^2 / (2*σ_h^2)) # soft contact prob,σ_h ≈ 0.07m
L_slide = Σ_t p_c[t] * ||j3d_world[t+1,heel,:2] - j3d_world[t,heel,:2]||^2 # XY sliding
L_pen = Σ_t ReLU(-d_f[t])^2 # 穿地
L_contact = Σ_t p_c[t] * d_f[t]^2 # 接触时贴地
总 loss¶
3.6 优化器与时间估算¶
优化器:Adam,lr=1e-3,100 iter(从好的初始点出发,不需要更多)
每次 iter 耗时估算(T=150 帧,J=70 关节,joints only,不跑 mesh skinning):
| 步骤 | CPU 估算 | GPU 估算 |
|---|---|---|
| batch mhr_forward | ~5-20ms | ~0.5-2ms |
| loss 计算(diff、charbonnier) | ~2-5ms | ~0.5ms |
| backward | ~5-20ms | ~0.5-2ms |
| Adam step | ~1ms | ~0.1ms |
| 单 iter 总计 | ~13-46ms | ~1.6-4.6ms |
总优化时间(100 iter):
远快于渲染(per-frame render 通常 >100ms/frame)。作为预处理完全可接受。
加速关键: 1. 必须 batch forward,不能逐帧循环 2. joints only,不算 mesh vertices 3. 不需要超过 100 iter(初始点好,fidelity 项限制收敛域)
3.7 与旧 D2b / IRLS 的关系¶
IRLS(当前实现):
parameter-space smooth,O(1) 解析解,快 (<100ms)
但 FK 输出不被直接约束,j3d_cam jitter 有上限
Joint world opt(本方案):
FK-output world-space smooth + contact,Adam 100 iter,~1-5s CPU
j3d_world 被直接约束,contact 和 smooth 联合优化
IRLS 继续保留为 quick_smooth baseline(D2b 路线)
本方案作为 full_opt 默认路线
3.8 输出¶
smoothed_world_mhr.npz:
global_rot: (T, 3)
body_pose: (T, 133) # 优化后 params + 未优化维度保持原值
pred_cam_t: (T, 3)
j3d_cam: (T, J, 3)
j3d_world: (T, J, 3)
human_root_transl_world: (T, 3)
human_root_orient_world: (T, 3)
contact_weights: (T, 2)
T_c2world: (4, 4)
ground_z: scalar
opt_time_sec: scalar
n_iter: scalar
4. Stage C:World-frame Ground Choice¶
4.1 新默认:SAM3D Body + depth-plane Z-up¶
在当前假设下:
可以直接从 depth 点云拟合 camera-frame ground plane,然后构造 Z-up world:
depth + K
-> backproject to camera-frame point cloud
-> fit support plane: n_c^T X_c + d_c = 0
-> choose normal sign so camera height is positive
-> construct T_c2world_depth_plane
-> ground_z = 0
T_c2world_depth_plane 只需要满足:
world yaw 和 XY origin 在单目静态相机下没有唯一真值。第一版固定一个确定性 convention 即可,例如:
world x = camera x projected onto ground plane and normalized
world y = world z cross world x
world origin = camera-frame plane projection point, transformed so plane z=0
只要人体、物体、depth 点云和 physics 都使用同一个 T_c2world_depth_plane,yaw gauge 不影响 contact/root/physics 的基本合理性。
4.2 共享 ground-plane 估计代码¶
不要为 SAM3D Body 路线复制一份新的 RANSAC。应把当前 ground_align.py 里的 plane fitting 拆成共享模块:
建议导出的核心函数:
@dataclass
class GroundPlaneFit:
normal_cam: np.ndarray # (3,), camera-frame unit normal
offset_cam: float # n_c^T X_c + d_c = 0
inlier_count: int
total_count: int
residual_median_m: float
camera_height_m: float
confidence: float
def backproject_depth_sequence(depth_seq, K_seq, *, stride, subsample, masks=None) -> np.ndarray:
...
def fit_support_plane_camera(points_cam, *, n_iter, dist_thresh, roi_policy) -> GroundPlaneFit:
...
def camera_plane_to_zup_transform(fit: GroundPlaneFit) -> tuple[np.ndarray, np.ndarray]:
# returns T_c2world, T_w2c
...
现有 GVHMR 路线继续复用同一套 plane fitting,但封装成另一个调用:
def correction_from_depth_plane_for_existing_world(
depth_seq,
K_seq,
T_c2world_raw,
) -> GroundCorrection:
# 等价于当前 ground_align.py 的行为:
# 用 raw world 构造点云,在 raw Z-up 里拟合 ground,然后返回 T_correction。
这样共享的是:
区别只是输出:
sam3d_body:
plane camera frame -> direct T_c2world_depth_plane
gvhmr:
plane raw world frame -> T_correction @ T_c2world_raw
4.3 Human motion 加载¶
不需要 provider 抽象层。config 驱动的简单加载函数足够:
def load_mhr_params(cfg) -> dict[str, np.ndarray]:
if cfg.motion.model == "sam3d_body":
return dict(np.load(cfg.motion.sam3d_body.npz_path))
elif cfg.motion.model == "gvhmr":
return convert_gvhmr_result_to_mhr(cfg.motion.gvhmr.result_pt)
调用方只依赖返回 dict 的 keys(global_rot, body_pose, pred_cam_t 等),不需要 dataclass 或 interface。
4.4 Config 选择¶
新增一个明确字段控制 human model。默认值为 sam3d_body:
reconstruction:
preprocess:
motion:
enabled: true
model: "sam3d_body" # choices: sam3d_body, gvhmr
sam3d_body:
enabled: true
run_stage_ab_smoothing: true
shape_scale_lock: "median"
body_smooth_variant: "B2_body_charb_ultra"
output_name: "sam3d_body_mhr"
gvhmr:
enabled: false
use_dpvo: false
hand_refine:
enabled: true
refiner: "dynhamr"
world_frame:
source: "depth_plane_zup" # choices: depth_plane_zup, gvhmr
depth_plane_zup:
stride: 5
subsample: 10
n_iter: 5000
dist_thresh: 0.04
roi_policy: "lower_scene_without_human_object"
兼容旧配置:
等价于:
旧 GVHMR 行为必须显式写:
4.5 Ground 分支保留方式¶
旧实验里的 G0/G1/G2/G3 仍可保留为 ablation,但不再是默认路线:
S0_depth_plane_zup:
默认。SAM3D Body camera-frame human + depth plane direct T_c2world.
G0_gvhmr_depth_corrected:
兼容旧路线。GVHMR T_c2world + depth ground correction.
G1_gvhmr_raw_world:
旧路线 ablation。未做 depth correction 的 GVHMR static world.
G2_contact_offset:
保留 world orientation,只让 contact 优化 scalar ground_z / vertical offset.
G3_contact_plane_optional:
研究项。优化 ground normal + offset;不作为当前合并目标。
当前默认优先级不是 fallback,而是固定配置:
4.6 论文 contact 方案的位置¶
论文式 contact optimizer 仍然放在 Stage D。它不负责估计完整 scene plane;它接收 Stage C 给出的 Z-up world frame 和 ground_z=0,再优化人体 root trajectory、foot sliding 和 penetration。
因此在新默认路线里,contact optimizer 的输入从:
改成:
5. Stage D:已合并入 Stage B¶
Contact-aware world root optimization 已合并进 Stage B(Section 3)的 L_contact 项。不再作为独立 stage。
原 Stage D 的功能对应:
原 delta_root_world 优化 -> Stage B 中 global_rot + pred_cam_t 优化 + L_contact
原 L_slide / L_pen / L_contact_height -> Stage B Section 3.5 的 L_contact
原 soft contact probability -> Stage B Section 3.5 的 p_c
原 Stage D 实验结果(已记录于 Section 10.3)保留作为 baseline 对比。新实现以合并后的 Stage B 为准。
6. Metrics 与实验判断¶
6.1 Camera-frame motion metrics¶
用于判断 Stage B 是否改善 overlay 抖动:
upper2d d3 p95
wrist2d d3 p95
all70 2d d3 p95
j3d_cam acceleration / jerk p95
body/global/pred_cam_t parameter deviation from pre_smooth
必须输出:
6.2 World / physics metrics¶
用于判断 Stage D 是否改善 physics root:
contact foot sliding distance
contact heel height error
ground penetration frame count / max depth
root acceleration p95
root deviation from init
physics retargeting visual result
Ground 对比时,至少输出:
G0_ransac_before_after.json
G2_contact_offset_before_after.json
heel_height_curves.png
foot_slide_curves.png
world_root_compare.mp4 or 3D plot
7. 实现路线图¶
Phase 1:修正前置验证¶
□ 确认 pre_smooth_mhr.npz keys / shapes
□ 确认 mhr_forward 使用 body_pose[:130]
□ 确认 pred_cam_t 进入 projection 的方式
□ 确认 depth.npy 是 metric 或已经通过 human/object depth alignment 变成 metric
□ 确认 camera intrinsics K_seq 可从 motion metadata 或 config 中稳定读取
□ 确认 depth-plane provider 输出 static T_c2world_depth_plane 和 ground_z=0
□ 渲染 D2b baseline,作为所有实验对比对象
Phase 2:Stage A + B¶
新建或扩展:
输入:
实验:
B0_D2b_baseline
B1_shape_scale_median_only
B2_latent_body_global
B3_latent_body_global_predcam
B4_latent_body_global_predcam_hand_light
选择标准:
Phase 3:Stage C + D¶
新建:
pipeline/reconstruction/preprocess/human/providers/base.py
pipeline/reconstruction/preprocess/human/providers/sam3d_body.py
pipeline/reconstruction/preprocess/human/providers/gvhmr.py
pipeline/reconstruction/preprocess/depth/ground_plane.py
scripts/debug/world_root_contact_opt.py
scripts/compare_ground_modes.py
合并顺序:
1. 把 ground_align.py 的 RANSAC / SVD / residual 逻辑抽成 ground_plane.py。
2. 为 sam3d_body provider 生成 smoothed_camera_mhr.npz 和 camera-frame joints/verts。
3. 为 depth_plane_zup provider 直接生成 T_c2world_depth_plane。
4. 修改 world_root_contact_opt.py,使它只依赖 HumanMotionResult + WorldFrameResult。
5. 把旧 GVHMR motion 路径包进 gvhmr provider,保持显式 config 兼容。
选择标准:
depth-plane residual 小
camera height 合理
foot sliding 更小
penetration 更少
root acceleration 不恶化
root deviation 不过大
physics / Isaac 里人体不漂、不穿地
Phase 4:串联最终输出¶
Best Stage B camera-frame human motion
-> depth_plane_zup world frame
-> contact root opt
-> physics input
最终输出需要同时包含:
8. 当前建议默认路线¶
当前默认路线固定为 sam3d_body + depth_plane_zup,不做自动 fallback:
1. preprocess.motion.model = sam3d_body
2. 跑 SAM3D Body per-frame MHR export。
3. 跑 Stage A:temporal median shape/scale lock。
4. 使用 metric/aligned depth + K,在 camera frame 直接拟合 support plane(Stage C)。
5. 构造 T_c2world_depth_plane,使 support plane 成为 Z-up world 的 z=0。
6. 跑 Stage B joint world-space optimization:
- 优化变量:global_rot + body_pose[:130] + pred_cam_t
- 每 iter 做一次 batch mhr_forward(T=150 帧,全部并行)
- loss = L_latent + L_smooth(world) + λ_phy * L_contact(world)
- Adam,lr=1e-3,100 iter,~1-5s CPU
7. 输出 smoothed_world_mhr.npz / overlay_2d.mp4 / world_root.npz / physics_preview.mp4。
原因:
- 当前 case 明确假设 static camera + depth 能可靠看到一部分地面或支撑平面。
- 在该假设下,GVHMR 不是构造 Z-up world frame 的必要依赖。
sam3d_body直接产生 MHR 参数,和 Stage A/B 的优化变量一致,减少 GVHMR->SMPLX->MHR/adapter 之间的额外转换。- depth-plane provider 提供 scene-based ground;contact optimizer 只负责 root trajectory 的物理合理性。
- GVHMR 路线保留为显式兼容配置:
preprocess.motion.model = gvhmr。
9. 参考资料¶
- 主论文:[2512.21573] World-Coordinate Human Motion Retargeting via SAM 3D Body
- MHR 模型:[2511.15586] MHR: Momentum Human Rig
- 本项目 MHR forward:
submodules/sam-body4d/models/sam_3d_body/sam_3d_body/models/heads/mhr_head.py - 本项目 MHR compact 参数:
submodules/sam-body4d/models/sam_3d_body/sam_3d_body/models/modules/mhr_utils.py - 本项目 Z-up camera/world helpers:
pipeline/utils/geom_utils.py - 本项目现有 baseline:
submodules/sam-body4d/outputs/postprocess_attribution_b009_0001_20260425/D2b_body_all_euler_w5_s1p0_b0p35
10. 实验完成记录(2026-04-25)¶
10.1 实现脚本¶
已新增:
运行环境:
本次沙箱里 torch.cuda.is_available() == False,所以实际用 CPU 跑完。
10.2 Stage A/B:camera-frame human motion smoothing¶
输出根目录:
调参结论:
参数:
base:
D0 no hand wrist write-back
body decoder wrist
hand_pose = median3 + gaussian17 sigma3 blend1
shape/scale:
temporal median lock from pre_smooth_mhr.npz
body_pose:
optimize body_pose[:130]
Charbonnier temporal smooth in MHR parameter space
lam_acc = 2.00
lam_jerk = 1.00
blend = 0.85
iterations = 6
global_rot:
unchanged
pred_cam_t:
unchanged
hand_pose:
unchanged after D0 hand smoothing
与 D2b 对比的 motion 指标:
upper2d_focus_d3 ratio = 0.568
wrist2d_focus_d3 ratio = 0.396
j3d_cam_focus_d3 ratio = 0.819
body_pose_d3 ratio = 0.226
pred_cam_focus_d3 ratio = 1.000
deviation_ratio_vs_D2b = 0.581
deviation_penalty = 0.000
解释:
- motion smooth 指标明显优于 D2b,尤其 wrist 2D jerk 和 body_pose jerk。
- 平均 deviation 低于 D2b,没有比 D2b 更偏离 per-frame estimate。
pred_cam_tsmoothing 组虽然能降低部分 j3d/camera-frame jerk,但因为pred_cam_t在 D2b 与 pre_smooth 几乎相同,任何改动都会带来很大的 deviation ratio;本轮不采用。B2_body_charb_ultra_keep_firstraw score 只比B2_body_charb_ultra低约 0.4%,但它回到 first-frame shape/scale,deviation 更高;按“效果相近时优先更少偏离和 temporal median identity”的规则,不作为推荐 best。
Best 输出:
submodules/sam-body4d/outputs/paper_plan_motion_smooth_b009_0001_20260425/B2_body_charb_ultra/smpl_params.npz
submodules/sam-body4d/outputs/paper_plan_motion_smooth_b009_0001_20260425/B2_body_charb_ultra/overlay_2d.mp4
submodules/sam-body4d/outputs/paper_plan_motion_smooth_b009_0001_20260425/B2_body_charb_ultra/4d_visualization.mp4
submodules/sam-body4d/outputs/paper_plan_motion_smooth_b009_0001_20260425/D2b_vs_B2_body_charb_ultra.mp4
submodules/sam-body4d/outputs/paper_plan_motion_smooth_b009_0001_20260425/summary.json
视频编码已验证:
overlay_2d.mp4: h264 / avc1 / yuv420p / 720x1280 / 150 frames
D2b_vs_B2_body_charb_ultra.mp4: h264 / avc1 / yuv420p / 1440x1280 / 150 frames
10.3 Stage C/D:world root/contact optimization¶
输入:
输出根目录:
对比结果:
Ground 对比:
World/contact 指标:
G0_ransac_contact_root_opt:
weighted_slide ratio = 0.804
penetration ratio = 0.000
contact_height ratio = 0.874
root_acc ratio = 0.618
delta_root p95 = 0.0286 m
after penetration frames = 0
G2_contact_offset_contact_root_opt:
weighted_slide ratio = 0.675
penetration ratio = 0.200
contact_height ratio = 1.330
root_acc ratio = 0.784
delta_root p95 = 0.0532 m
after penetration frames = 7
G1_raw_world_contact_root_opt:
weighted_slide ratio = 0.704
penetration ratio = 0.058
contact_height ratio = 2.787
root_acc ratio = 0.813
delta_root p95 = 0.0734 m
after penetration frames = 4
结论:
- 对这个序列,RANSAC ground 更可靠。
G0_ransac_contact_root_opt在不引入穿地的情况下减少 foot sliding、contact height error 和 root acceleration。- Contact-derived ground offset 虽然降低 raw slide,但引入 contact height 变差和少量 penetration,不适合作为默认替换。
- 因此在该轮 GVHMR/RANSAC 对比实验中,world/physics 使用
G0_ransac_contact_root_opt。
Best world 输出:
submodules/sam-body4d/outputs/paper_plan_world_contact_b009_0001_20260425/G0_ransac_contact_root_opt/world_root.npz
submodules/sam-body4d/outputs/paper_plan_world_contact_b009_0001_20260425/G0_ransac_contact_root_opt/report.json
submodules/sam-body4d/outputs/paper_plan_world_contact_b009_0001_20260425/G0_ransac_contact_root_opt/heel_height_and_slide.png
submodules/sam-body4d/outputs/paper_plan_world_contact_b009_0001_20260425/G0_ransac_contact_root_opt/root_xy.png
submodules/sam-body4d/outputs/paper_plan_world_contact_b009_0001_20260425/summary.json
world_root.npz 内容:
human_root_transl_world: (150, 3)
human_root_orient_world: (150, 3)
delta_root_world: (150, 3)
joints_world: (150, 70, 3)
joints_world_before: (150, 70, 3)
contact_weights: (150, 2)
T_c2world: (4, 4)
ground_z: (1,)
11. 代码合并清单:SAM3D Body 默认 world pipeline¶
11.1 Config contract¶
新增或规范化:
reconstruction:
preprocess:
motion:
enabled: true
model: "sam3d_body" # default; choices: sam3d_body, gvhmr
world_frame:
source: "depth_plane_zup"
配置解释:
motion.model=sam3d_body:
默认路径。直接使用 SAM3D Body MHR 参数,接 Stage A/B 和 depth-plane world。
motion.model=gvhmr:
兼容旧路径。调用现有 GVHMR motion/result.pt 逻辑,并通过 adapter 暴露统一结果。
world_frame.source=depth_plane_zup:
默认路径。直接从 metric/aligned depth 和 K 拟合 camera-frame support plane。
不做 runtime fallback。如果 depth plane 不满足质量阈值,当前默认行为应该 fail fast,并在 report 里写清楚残差、inlier 数和 camera height,而不是自动切到 GVHMR。
11.2 新模块与职责¶
pipeline/reconstruction/preprocess/human/providers/base.py
定义 HumanMotionResult 和 provider interface。
pipeline/reconstruction/preprocess/human/providers/sam3d_body.py
封装 SAM3D Body per-frame export、Stage A/B smoothing、camera-frame materialization。
pipeline/reconstruction/preprocess/human/providers/gvhmr.py
封装现有 gvhmr.py,保留旧 motion/result.pt 兼容。
pipeline/reconstruction/preprocess/depth/ground_plane.py
共享 depth backprojection、plane RANSAC、SVD refine、camera-plane -> Z-up transform。
pipeline/reconstruction/preprocess/world_frame.py
定义 WorldFrameResult,并根据 config 调用 depth_plane_zup 或 gvhmr world provider。
ground_align.py 保留 CLI,但内部改为调用 ground_plane.py。这样旧 GVHMR depth correction 和新 SAM3D Body direct Z-up 使用同一套 plane fitting 实现。
11.3 Data contract¶
HumanMotionResult 至少包含:
provider
num_frames
camera_frame_npz
camera_joints
camera_vertices
pred_cam_t
focal_length
frame_paths
mask_paths
metadata
WorldFrameResult 至少包含:
source
T_c2world
T_w2c
ground_z
ground_plane_normal_cam
ground_plane_offset_cam
camera_height_m
fit_residual_median_m
fit_inlier_count
metadata
Stage D 只消费这两个 dataclass,不直接读取 GVHMR 或 SAM3D Body 私有文件。
11.4 SAM3D Body 默认流程¶
1. Human provider:
SAM3D Body -> pre_smooth_mhr.npz
-> temporal median shape/scale lock (Stage A)
2. World provider (Stage C, pre-computation):
depth.npy + K_seq + optional human/object exclusion masks
-> camera-frame support plane fit
-> T_c2world_depth_plane, T_w2c_depth_plane, ground_z=0
-> world_frame_report.json
3. Joint world-space optimization (Stage B):
input: pre_smooth_mhr.npz + T_c2world (fixed constant)
variables: global_rot (T,3) + body_pose (T,130) + pred_cam_t (T,3)
each iter:
batch mhr_forward -> j3d_local, joint_global_rots (T, J, ...)
j3d_world = T_c2w @ j3d_cam (T, J, 3)
loss = L_latent + L_smooth(j3d_world, rots) + L_contact(j3d_world)
Adam, lr=1e-3, 100 iter, ~1-5s CPU
output: smoothed_world_mhr.npz
4. Physics:
consume smoothed_world_mhr.npz (contains j3d_world, world root, contact weights).
11.5 验收标准¶
第一版合并完成必须输出:
pre_smooth_mhr.npz
smoothed_camera_mhr.npz
overlay_2d.mp4
world_frame_report.json
world_root.npz
heel_height_and_slide.png
root_xy.png
world_frame_report.json 必须包含:
human_provider = sam3d_body
world_source = depth_plane_zup
T_c2world
T_w2c
ground_z = 0
camera_height_m
fit_residual_median_m
fit_inlier_count / fit_total_count
roi_policy
合并后的默认配置不再要求 motion/result.pt 来自 GVHMR。只有 motion.model=gvhmr 时,才允许 Stage C/D 读取 GVHMR motion metadata。