PoseInversion#

PoseInversion is the inverse-fitting utility used by the conversion tools in this repository.

It fits SOMA-compatible skeleton rotations from posed vertices and supports:

  • analytical inverse-LBS refinement

  • optional Lie algebra Gauss-Newton refinement

  • optional autograd-based refinement through FK + LBS

This makes it the key API for conversions such as SMPL-to-SOMA and MHR-to-SOMA.

class soma.pose_inversion.PoseInversion(
soma_layer,
low_lod=True,
skeleton_transfer_rotation_method='auto',
refit_rotation_method='auto',
)#

Bases: object

Invert posed vertices to a layer’s skeleton rotations.

Accepts vertices in the wrapped layer topology. For SOMALayer, it also accepts supported identity-model native topologies (MHR, SMPL, etc.) and transfers them through the identity model’s barycentric interpolator. All translational inputs and outputs are in the wrapped layer’s output_unit.

Parameters:
  • soma_layer (Any) – A SOMALayer instance (any LOD).

  • low_lod (bool) – Use low-LOD SOMA topology (4505 verts) for the iterative refit. This is ~4x fewer vertices than mid-LOD (18056) with negligible accuracy loss (~0.006 cm). When True and the given soma_layer is mid-LOD, a second low-LOD SOMALayer is created internally for the refit. XLO layers keep their own topology because there is no direct xlo-to-low vertex transfer. Default True.

  • skeleton_transfer_rotation_method (str) – rotation extraction method used by the initial SkeletonTransfer pose estimate. "auto" uses a Newton-Schulz-first reference-gauge policy.

  • refit_rotation_method (str) – rotation extraction method used by the analytical inverse-LBS refit. "auto" applies a weak reference-rotation gauge to the local Procrustes covariance, then uses SVD only as a final validity projection if Newton-Schulz does not produce a rotation. "kabsch" and "newton-schulz" remain available for diagnostics.

Usage:

inv = PoseInversion(soma_layer)
inv.prepare_identity(identity_coeffs, scale_params)
result = inv.fit(posed_vertices)  # any supported topology, in soma_layer.output_unit
transfer_to_soma(vertices)#

Transfer vertices from identity-model topology to SOMA topology.

If vertices are already on SOMA topology, returns them unchanged.

Parameters:

vertices (Tensor) – (B, V, 3) or (V, 3) in any supported topology.

Returns:

(B, V_soma, 3) vertices on SOMA topology.

Return type:

Tensor

prepare_identity(
identity_coeffs,
scale_params=None,
repose_to_bind_pose=True,
kwargs=None,
)#

Set up rig from identity parameters.

Supports both single identity (1, C) and batched identities (B, C). Structural caches (sparse weights, groups, levels) are built once on the first call and reused on subsequent calls. Per-identity bind data (bind_local_t, W_bind_inv, bind_verts) is updated every call.

Parameters:
  • identity_coeffs (Tensor) – (1, C) or (B, C) identity coefficients.

  • scale_params (Tensor | None) – (1, S) or (B, S) optional scale parameters.

  • repose_to_bind_pose (bool) – if True (default), transform the rest shape into SOMA’s bind pose. Set to False when the target vertices are posed relative to the identity model’s native rest pose (e.g. MHR).

  • kwargs (Mapping[str, Any] | None) – optional dict passed to the identity model’s get_rest_shape.

fit(
posed_vertices,
body_iters=2,
finger_iters=0,
full_iters=1,
lie_iters=3,
lie_lambda=1e-1,
autograd_iters=0,
autograd_lr=5e-3,
autograd_translation_lr_scale=1.0,
autograd_pose_prior=0.0,
autograd_leaf_weight=None,
autograd_pose_prior_weights=None,
constrain_1dof=False,
leaf_weight=1.0,
batch_size=None,
)#

Fit SOMA skeleton rotations to posed vertices.

Supports several modes depending on the iteration arguments:

  • Analytical + Lie algebra Gauss-Newton (default): body_iters=2, full_iters=1, lie_iters=3. The analytical solve gives a fast warm start, then Lie-GN solves all active joint rotations simultaneously via a dense FK-coupled normal equation.

  • Analytical only: lie_iters=0.

  • Lie algebra Gauss-Newton only: body_iters=0, full_iters=0, lie_iters=5.

  • Autograd FK only: body_iters=0, full_iters=0, lie_iters=0, autograd_iters=10. Slow but controllable (e.g. extra weights on extremities).

  • Default + autograd FK: autograd_iters=10. The default analytical + Lie-GN solve warm-starts autograd refinement.

Parameters:
  • posed_vertices (Tensor) – (B, V, 3) vertices on any supported topology.

  • body_iters (int) – analytical iterations for body chain (default: 2).

  • finger_iters (int) – analytical iterations for finger chain (default: 0).

  • full_iters (int) – analytical iterations for all joints (default: 1).

  • lie_iters (int) – Lie algebra Gauss-Newton iterations (default: 3). When > 0, runs a dense batched Gauss-Newton solve in SO(3) after the analytical solver. Each iter solves a (3J x 3J) system for all joint twists simultaneously.

  • lie_lambda (float) – Tikhonov regularisation for the Lie-GN normal equations (default: 1e-1).

  • autograd_iters (int) – Adam optimization steps through FK + LBS (default: 0). When > 0, runs autograd refinement after the analytical solve.

  • autograd_lr (float) – learning rate for autograd Adam (default: 5e-3).

  • autograd_translation_lr_scale (float) – multiplier for the root-translation Adam learning rate. Useful because translations are optimized in layer output units while rotations use unitless 6D parameters.

  • autograd_pose_prior (float) – local-rotation prior weight for autograd FK. Penalizes deviation from the initial local rotation matrices. 0.0 disables it.

  • autograd_leaf_weight (float | Mapping[str, float] | None) – optional vertex weighting used only by the autograd FK stage. This lets the analytical/Lie warm start remain unweighted while the refinement emphasizes contact regions.

  • autograd_pose_prior_weights (Mapping[str, float] | None) – optional per-joint multipliers for the autograd pose prior. Values > 1 stiffen a joint relative to the warm start; values < 1 let it move more.

  • constrain_1dof (bool) – apply 1-DOF constraints on elbows/knees (analytical only).

  • leaf_weight (float | Mapping[str, float]) – importance multiplier for extremity vertices. Float for uniform (e.g. 3.0), or dict for per-group (e.g. {"head": 2, "hands": 2, "feet": 5, "heels": 10}). 1.0 = uniform (default).

  • batch_size (int | None) – process in chunks of this size.

Returns:

dict with rotations (B, J, 3, 3), root_translation (B, 3), and per_vertex_error (B, V) L2 error per vertex.

Return type:

PoseInversionResult

roundtrip(posed_vertices, **kwargs)#

Invert and forward for verification.

Returns:

(soma_vertices, result) where soma_vertices is (B, V_soma, 3).

Return type:

tuple[Tensor, PoseInversionResult]