Paper (arXiv 2610.18871) · Project page · Weights (Hugging Face) · Code
Given a 3D mesh and the 3D displacements of a sparse subset of its vertices, Cepheid completes the dense motion of every vertex. It works in three steps. First, harmonic extension spreads the observed displacements across the mesh edges to form a parameter-free base field H. Second, amplitude recovery predicts the amplitude ŝ of the residual from the mesh, the observed displacements and H, using a frozen ridge-regression rule fitted on the training sources. Third, a flow-matching network predicts the dimensionless residual structure, averaging k samples into R̄, so that the completed motion is F̂ = H + ŝ·R̄. Trained once on deformable animals (DT4D), human bodies (AMASS) and a Franka robot arm, without skeletons, templates or cross-mesh correspondence, a single model transfers zero-shot to unseen cloth and legged robots. This repository provides the inference API, the eleven model checkpoints and the scripts that reproduce the paper's tables.
pip install "git+https://github.com/Su-Terry/cepheid.git" # install PyTorch for your CUDA firstfrom cepheid import Cepheid
from cepheid.examples import toy
model = Cepheid.from_pretrained("ohinic/cepheid") # downloads unified/ from the Hub, checks sha256
ex = toy() # a NumPy icosphere, 2,562 vertices, 40 observed
out = model.complete(ex.vertices, faces=ex.faces, observed_idx=ex.observed_idx, observed_disp=ex.observed_disp)
print(out.F.shape, out.s_hat) # completed motion (2562, 3) and the predicted amplitudeWith the defaults (k = 8 samples, 30 flow steps) this takes about 100 s on CPU, a few seconds
on a GPU (about 3 s on one RTX 4090, model loading included). out.H is the harmonic extension, out.samples the k
per-sample completions, and out.amplitude the amplitude recovery (σ_obs, the rule's
features, and the features it clamped to their training range). cepheid.harmonic_extension(...)
and cepheid.recover_amplitude(...) run those two steps on their own. Displacements may be in
any length unit: they are divided by the mesh's largest bounding-box side before the model
sees them, as in training, and the outputs come back in the input's unit.
PyTorch is not a listed dependency, so pip never replaces your CUDA build. The package itself
needs numpy, scipy, PyYAML, safetensors and huggingface_hub. Reproducing the paper's
tables uses the pinned environment in environment.yml (see REPRODUCE.md).
The weights are on the Hugging Face Hub at https://huggingface.co/ohinic/cepheid: the unified
model as safetensors with JSON sidecars (unified/), and the eleven checkpoints the paper's
tables were scored with, byte for byte (paper_checkpoints/). SHA256SUMS lists every file, and
cepheid/assets/CHECKPOINTS.sha256 the eleven checkpoints; every loader checks them.
model = Cepheid.from_pretrained("ohinic/cepheid") # from the Hub (subfolder unified/)
model = Cepheid.from_pretrained("path/to/cepheid") # a local download of the repo, or its unified/ folder
model = Cepheid.from_pretrained("path/to/xm2_unified_model.pt",
config="configs/xm2_unified.yaml") # a paper checkpoint, from a clone of this repositoryTable numbers follow arXiv 2610.18871 v1.
| file | sha256 | paper table, row | training config |
|---|---|---|---|
xm2_unified_model.pt |
b1f29a42… |
Cepheid: Table 2 "ours (unified)"; Table 1 "ours (unified)"; Table 3; Table 4 full row and its k = 1 reading; Table 15 "zero-shot" | configs/xm2_unified.yaml |
xm2_dt4d_model.pt |
5d5cfa90… |
Table 2 "ours (DT4D)" | configs/xm2_dt4d.yaml |
xm2_amass_model.pt |
8ec3b090… |
Table 2 "ours (AMASS)" | configs/xm2_amass.yaml |
xm2_franka_model.pt |
5ced3652… |
Table 2 "ours (Franka)" | configs/xm2_franka.yaml |
xm2_unified_zero_model.pt |
97a94984… |
Table 4 (and 8), harmonic extension removed | configs/xm2_unified_zero.yaml |
xm2_unified_reg_model.pt |
d4251fb0… |
Table 4 (and 8), flow matching removed (regression) | configs/xm2_unified_reg.yaml |
xm2_unified_nobc_model.pt |
64f3bd1b… |
Table 4 (and 8), not conditioned on the harmonic extension | configs/xm2_unified_nobc.yaml |
xmatrix_unified_nonorm500k_model.pt |
04df1793… |
Table 4 (and 8), amplitude recovery removed (ŝ = 1) | configs/xmatrix_unified_nonorm500k.yaml |
crossmesh_knn.pt |
dfb26c48… |
Table 14 "Laplacian + GNN (ours)" | configs/dt4d_crossmesh_knn.yaml |
dt4d_crossmesh_knn_tps.pt |
caf8e983… |
Table 14 "TPS + kNN (ours)" | configs/dt4d_crossmesh_knn_tps.yaml |
xm2_franka_anchork_ft_model.pt |
b765485b… |
Table 15 "fine-tuned" | configs/xm2_franka_anchork_ft.yaml |
Every xm2_* checkpoint carries the same amplitude rule,
cepheid/assets/sfrac_predictor_insample_dt4d_amass_franka.json (sha256 f4d1bc5d…9a7f), fitted
on DT4D, AMASS and Franka (leave-cloth-out fold).
cepheid/is the library: the API incepheid/api.py, the example input incepheid/examples.py.scripts/trains models and prepares data;reproduce/regenerates the paper's tables.- REPRODUCE.md maps every script and folder to its purpose, and DATA.md covers the datasets.
- Resolution lock. The network's message graph is a kNN graph on vertex positions, and
the models are locked to the graph density they were trained at (about 3,000 vertices on
DT4D, 6,890 on AMASS). Running far from it, or raising k, makes results measurably worse.
complete()warns outside 1,000–7,000 vertices. - DT4D's stored graph. DT4D meshes carry explicit edges, not faces. The preprocessing builds them once per identity, from the largest clip's first frame and faces, and copies that edge set into every clip of the identity. The harmonic extension and the amplitude features use these stored edges; the network's messages do not. Some edges are long in poses other than the reference one. The released numbers use this graph unchanged.
- GPU nondeterminism. The flow-matching sampler is not bit-deterministic on a GPU. Re-running identical code moves sampled sums by up to about 5e-5 relative; the harmonic extension and ground-truth sums do not move. Expect agreement to about four decimals, not bit-identity. On CPU, runs are bit-identical.
- Third-party code is not shipped. The DOMA and NSDP baselines (Table 1) and the
GenBot1K rollouts need code from
$MC_EXTERNALthat this repository does not include. Configurations for backbones that need it are kept, but those models are not registered.
@article{su2026cepheid,
title = {Cepheid: Decoupling Scale from Structure for Cross-Family Motion Completion},
author = {Su, Chih-Hai and Ke, Tsung-Wei and Liu, Yu-Lun},
journal = {arXiv preprint arXiv:2610.18871},
eprint = {2610.18871},
archivePrefix = {arXiv},
year = {2026}
}The code is released under the MIT licence (LICENSE). NOTICE lists code adapted from other projects and the terms of the datasets the weights were trained on. No dataset is redistributed here. The weights on the Hugging Face Hub are released for non-commercial research use only; see the model card.
