Instructions to use OzzyGT/motionbricks_soma_custom_blocks with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use OzzyGT/motionbricks_soma_custom_blocks with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("OzzyGT/motionbricks_soma_custom_blocks", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
MotionBricks SOMA Modular Diffusers
The start of a Kimodo run, then the 30 frames generated with the code below, then the standing end of another Kimodo take, placed 1.4 m ahead. The generated frames are drawn on SOMA77 with a relaxed finger pose. At normal playback speed; the cameras follow the root.
Modular Diffusers blocks for NVIDIA MotionBricks on the SOMA skeleton: generate the transition from the last poses of one motion to the first poses of another in a single forward pass, with no sampling loop. The model code and weights are in OzzyGT/motionbricks_soma_diffusers.
MotionBricks has no text input. It fills the gap between poses you provide, for example two motions from Kimodo SOMA Modular, and chooses the length of the transition itself.
Requires PyTorch, diffusers>=0.41.0 and numpy.
Licensing
The blocks are Apache-2.0, adapted from the MotionBricks runtime. See NOTICE. This repository contains no model weights; they use the NVIDIA Open Model Agreement, see the model repository.
Usage
The example uses trust_remote_code=True, which loads these blocks, and trust_components_code=True, which allows the model repository's custom classes.
Note: both flags run Python code downloaded from the Hub:
trust_remote_code=Trueruns the code of the blocks repository, andtrust_components_code=Trueruns the code shipped in thecomponents_repo. Only use them with repositories you trust, and review their code before passing different ones.
Bring a run to a stop, as in the video. Both clips come from Kimodo SOMA Modular: the start of a run, and a standing pose that the pipeline places 1.4 m ahead of it:
import numpy as np
import torch
from diffusers import ModularPipelineBlocks
from huggingface_hub import hf_hub_download
blocks = ModularPipelineBlocks.from_pretrained(
"OzzyGT/motionbricks_soma_custom_blocks",
trust_remote_code=True,
components_repo="OzzyGT/motionbricks_soma_diffusers",
trust_components_code=True,
)
pipe = blocks.init_pipeline()
pipe.load_components(dtype=torch.float32)
pipe.to("cuda")
first = hf_hub_download("OzzyGT/diffusers-examples", "motionbricks_soma_diffusers/first.npz", repo_type="dataset")
second = hf_hub_download("OzzyGT/diffusers-examples", "motionbricks_soma_diffusers/second.npz", repo_type="dataset")
context = dict(np.load(first))
target = dict(np.load(second))
motion = pipe(context=context, target=target, target_offset=(1.4, 0.0), num_target_poses=4, output="motion")[0]
np.savez_compressed("transition.npz", **{key: value.cpu().numpy() for key, value in motion.items()})
Here the model picks a 30-frame transition.
Inputs
context: the motion to continue from.target: the motion to arrive at.target_offset: wheretargetstarts,(forward, left)in meters from the end ofcontext.target_turn: which waytargetfaces, in degrees from the end ofcontext, positive to the left.num_target_poses: how many frames oftargetto match, 1 to 4. Default 1.num_tokens: the lengths the model may choose. Default 26 to 50 frames.correct_endpoints: start and end exactly on the given poses. DefaultTrue.
Notes
- Motions.
contextandtargetare SOMA30 or SOMA77 motions at 30 FPS, such as Kimodo output loaded from an NPZ. The result is a SOMA30 motion. - Joining. The transition starts on the last frame of
contextand ends on the first frame oftarget, so cut them where they should join. Ask foroutput=["motion", "placed_target"]to also gettargetwhere the pipeline placed it, to join the three motions. - Placement. Without
target_offsetortarget_turn,targetstays where it is. With only one of them, the other is 0. - Target poses. The model always reads the last 4 frames of
contextand the firstnum_target_posesframes oftarget, consecutive frames, to see how the body moves at each end. Use 4 whenevertargethas at least 4 frames, so the transition also matches howtargetmoves; use 1 when it is a single pose. - Length.
num_tokenscounts in steps of 4 frames, from 6 to 16;ntokens give4 * n - 6frames. Pass one value to fix the length. - Longer transitions. Feed the generated motion back as the next
context.
A call takes about 7.5 ms in float32 on an RTX 5090 and peaks at 0.8 GB of VRAM.
Precision
The weights are float32, and float32 reproduces NVIDIA's ONNX graph with PyTorch's default settings. Enabling TF32 matrix multiplication (torch.backends.cuda.matmul.allow_tf32 = True) can change a few pose codes. dtype=torch.float16 halves the memory and is a little faster, with small drift: in a test of 8 transitions, every length matched float32 and joints moved by about 4 cm in the median. Avoid torch.bfloat16, which drifts much more. NVIDIA notes the same drift in its mixed FP16 model, and it grows when the output is fed back as context.
Real-time use
These blocks bring MotionBricks to apps and environments that already run on Diffusers. For a real-time product, such as a game or a robot, prefer NVIDIA's MotionBricks runtime, which runs the ONNX models through TensorRT. With the blocks, about half of each call is Python and data preparation rather than GPU work.
To generate transitions while the motion plays in a Diffusers app, keep the pipeline loaded and capture the three models in CUDA graphs. On an RTX 5090, a call then takes about 4.5 ms instead of 7.5 ms.
Compile while loading, not in the loop: the first call compiles the models in about 15 seconds, and the second records the graphs.
for name in ("root_model", "pose_model", "pose_decoder"):
getattr(pipe, name).compile(mode="reduce-overhead", fullgraph=True)
for _ in range(2):
torch.compiler.cudagraph_mark_step_begin()
pipe(context=context, target=target, target_offset=(1.4, 0.0), num_target_poses=4, output="motion")
Then, whenever a transition is needed:
torch.compiler.cudagraph_mark_step_begin()
motion = pipe(context=context, target=target, target_offset=(1.4, 0.0), num_target_poses=4, output="motion")[0]
- Call
torch.compiler.cudagraph_mark_step_begin()before every call, so the graphs can reuse their memory; without it, the second call fails. - New clips, target placements,
num_target_poses,num_tokensandcorrect_endpointsreuse the graphs. The first new batch size compiles again, once, in about 20 seconds; later sizes don't. - Several characters, or several candidates for one character such as different stop distances, fit in one call: stack their motions on a leading batch dimension. Each gets its own transition and length, matching separate calls to within 0.01 mm. With CUDA graphs, a batch of 4 takes about 5.5 ms and a batch of 8 about 6 ms, against 4.5 ms for one.
- Downloads last month
- -
Model tree for OzzyGT/motionbricks_soma_custom_blocks
Base model
nvidia/MotionBricks