Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 

Repository files navigation

soma2amass

Convert SEED / SOMA (MHR) BVH motion capture into AMASS-style SMPL-X .npz files.

SOMA is a canonical 78-joint rig, not SMPL-X, so this is a retarget rather than a format rewrite:

BVH  ->  SOMA rig  ->  SMPL-X surface  ->  SMPL-X parameters  ->  AMASS npz
  1. Parse the BVH into SOMA joint rotations (77 axis-angle, absolute poses) plus the Hips translation.
  2. SOMALayer(mhr).prepare_identity(actor).pose(absolute_pose=True) → a mesh in SOMA topology.
  3. SMPLFamilyTopologyBridge → the same surface in SMPL-X topology (10 475 vertices).
  4. smplfitter.BodyFitter.fit() → SMPL-X {pose, betas, trans}.
  5. Rotate from SOMA's +y-up frame into AMASS's +z-up frame and write the AMASS layout.

Install

# PyTorch first, matching your CUDA (SOMA-X needs CUDA for its Warp kernels)
pip install torch --index-url https://download.pytorch.org/whl/cu128

pip install -r requirements.txt

py-soma-x downloads its rig assets from the Hugging Face model nvidia/soma-x on first use; pass --soma_data_root to point at an existing local snapshot instead.

Assets you must provide yourself

What Where it goes Notes
SMPLX_NEUTRAL.npz --smplx_model_path from the SMPL-X project (registration required)
body_models/smplx/SMPLX_NEUTRAL.* --smplx_model_root model root used by smplfitter
SEED BVH files --src e.g. the extracted soma_proportional/bvh/{date}/*.bvh
Per-actor SOMA shapes --shape_dir A<id>.npz files, matched from the __A<id> tag in each filename

These model/data files are not redistributed here — obtain them from their original sources and follow their licences (see References).


Usage

python soma_to_amass.py \
    --src        /path/to/soma_proportional/bvh \
    --dst        /path/to/output_amass \
    --shape_dir  /path/to/soma_shapes/soma_proportion_fit_mhr_params \
    --smplx_model_path  /path/to/SMPLX_NEUTRAL.npz \
    --smplx_model_root  /path/to/body_models/smplx \
    --target_fps 30

Relative paths under --src are preserved in --dst. Re-running skips outputs that already exist, so an interrupted run resumes.

Options

Flag Default Meaning
--src_fps 120 source frame rate (SEED BVH is 120 fps)
--target_fps (none) downsample target; omitted / >= source keeps the source rate (never upsamples)
--num_betas 10 SMPL-X shape coefficients to fit and store
--fit_iters 1 smplfitter iterations (more does not help this cross-rig fit)
--frame_batch 1000 frames per chunk through SOMA/bridge/fit — lower it if you run out of VRAM
--min_seg 10 shortest valid segment (in source frames) kept when a clip is split around corrupt NaN gaps — see Corrupt (NaN) frames in SEED
--include_mirror off also convert mirrored ..._M.bvh clips
--soma_data_root (auto) local SOMA-X asset snapshot instead of downloading
--device cuda SOMA-X requires CUDA

Output format

One .npz per clip, using the AMASS SMPL-X field layout:

Field Shape Notes
gender 'neutral' (the fit targets the neutral SMPL-X body)
surface_model_type 'smplx'
mocap_frame_rate output fps
betas (num_betas,) mean of the per-frame fitted betas
root_orient (T, 3) global orientation, axis-angle
pose_body (T, 63) 21 body joints
pose_hand (T, 90) left (45) + right (45)
trans (T, 3) metres

Coordinate frame: +z up, metres — matching AMASS. SOMA retargets to a +y-up SMPL-X frame, so a Rot_x(90°) is applied. Because SMPL's global rotation acts about the pelvis rather than the origin, the root is mapped as

R' = C · R                    C = Rot_x(90°)
t' = C · (t + j0) − j0        j0 = pelvis rest joint from betas

Corrupt (NaN) frames in SEED

The SEED release contains corrupt motion data, and any converter reading it has to deal with this — otherwise the corruption silently spreads into the whole output file.

Measured on the soma_proportional set: 353 of 71 132 non-mirrored BVH files (≈0.5 %) contain literal nan tokens in their MOTION block. The corruption comes in two shapes:

  • a trailing run of NaN frames (a truncated export), and — more awkwardly —
  • a large interior block with perfectly valid motion on both sides. One measured example, 230301/dancing_routine_V002_003__A232.bvh, has 292 consecutive NaN frames (frames 708–999 of 1592, ≈2.4 s at 120 fps) sandwiched between good motion.

Affected files cluster by capture session / actor rather than being scattered at random (e.g. an entire door-interaction session, and several object-manipulation sessions).

Why one bad frame ruins the whole clip

The SMPL-X fit uses share_beta=True, i.e. a single shape vector is solved for the whole clip. A NaN in any frame propagates into that shared solve, so the fitted betas come out NaN — and since the root translation is derived from betas (via the pelvis rest joint j0), every frame of the output becomes NaN, not just the corrupt ones. Dropping only the bad frames is therefore not optional; it has to happen before fitting.

How this converter handles it

  1. parse_bvh returns a per-frame finite mask instead of silently passing NaN through.
  2. The clip is split into contiguous all-finite segments. Trailing NaN becomes a clean truncation; an interior gap yields two independent segments, so the good motion on both sides is kept and the two halves are never spliced together (which would fabricate a jump and skew all timing after the gap).
  3. Segments shorter than --min_seg source frames (default 10) are discarded — a handful of frames around a corruption boundary is not a usable motion, and it would otherwise produce near-empty files and an ill-conditioned shape fit. This is what --min_seg is for. Raise it if you only want substantial clips; lower it to keep short fragments.
  4. Multi-segment clips are written as <name>__seg00.npz, <name>__seg01.npz, …; a clip that needed no splitting keeps its plain name. A clip with no usable segment is skipped.
  5. As a backstop, a fitted result that still contains NaN is never written.
  6. Every affected file is recorded in <dst>/_nan_report.csv with columns kind,file,dropped_frames,kept_frames,n_segments, where kind is one of truncated / split / unusable / nan_in_fit.

Timing stays correct: frames are uniformly spaced within each segment, and the resample to --target_fps is applied per segment.


Behaviour notes

  • Absolute poses. SEED BVH joint rotations are absolute, not relative to the canonical T-pose, so the SOMA layer is driven with absolute_pose=True. Getting this wrong yields a folded, lying-down body.
  • Global translation lives on the Hips position channels (the Root joint stays at 0), so every position channel on the root chain is summed.
  • Corrective shapes are off. The SOMA↔SMPL-X residual is ~23 mm mean surface distance and is intrinsic to the two body models — it is not reduced by more fit iterations, more betas, or correctives. Pose and root trajectory are captured well, which is what a motion dataset needs.
  • Corrupt NaN frames in the source are split out rather than propagated — this is a real property of the SEED release, see Corrupt (NaN) frames in SEED.
  • Mirrored clips (..._M.bvh) are skipped by default, since left/right mirroring is usually applied uniformly as a later augmentation step.

References

This tool only glues together other people's work. If you use it, please cite the underlying datasets, models and libraries:

AMASS (target format)

The output follows the AMASS SMPL-X field layout and +z-up convention.

Naureen Mahmood, Nima Ghorbani, Nikolaus F. Troje, Gerard Pons-Moll, Michael J. Black. AMASS: Archive of Motion Capture as Surface Shapes. ICCV 2019. https://amass.is.tue.mpg.de/

@inproceedings{AMASS2019,
  title     = {{AMASS}: Archive of Motion Capture as Surface Shapes},
  author    = {Mahmood, Naureen and Ghorbani, Nima and Troje, Nikolaus F.
               and Pons-Moll, Gerard and Black, Michael J.},
  booktitle = {ICCV},
  year      = {2019}
}

SEED (source dataset)

The input motions are the SEED capture set, distributed as SOMA/MHR-skeleton BVH. Use of the data is governed by the SEED dataset's own licence and terms — obtain it from its official distribution and cite it as its authors request. (Add the official SEED citation here for your fork; it is intentionally not guessed at in this README.)

SOMA-X (source rig / retargeting)

The SOMA (MHR) rig, the SMPL-X layer and the topology bridge come from NVIDIA's SOMA-X.

Please follow the citation and licence stated in the SOMA-X repository.

smplfitter (SMPL-X parameter fitting)

Fitting SMPL-X parameters (including betas) to the bridged surface is done with smplfitter by István Sárándi.

Please use the citation given in that repository.

SMPL-X (body model)

Georgios Pavlakos, Vasileios Choutas, Nima Ghorbani, Timo Bolkart, Ahmed A. A. Osman, Dimitrios Tzionas, Michael J. Black. Expressive Body Capture: 3D Hands, Face, and Body from a Single Image. CVPR 2019. https://smpl-x.is.tue.mpg.de/

@inproceedings{SMPL-X:2019,
  title     = {Expressive Body Capture: {3D} Hands, Face, and Body from a Single Image},
  author    = {Pavlakos, Georgios and Choutas, Vasileios and Ghorbani, Nima
               and Bolkart, Timo and Osman, Ahmed A. A. and Tzionas, Dimitrios
               and Black, Michael J.},
  booktitle = {CVPR},
  year      = {2019}
}

The SMPL-X model files are distributed under the SMPL-X licence and are not included in this repository.


Licence

The code in this repository is provided as-is. The datasets and body models it consumes (SEED, SOMA-X assets, SMPL-X) each carry their own, more restrictive licences — this repository neither redistributes nor relicenses them.

About

SEED / SOMA (MHR) BVH mocap to AMASS-format SMPL-X .npz. Retargets through the SOMA-X rig and a topology bridge, then fits SMPL-X with smplfitter.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages