Transforms¶
transforms
¶
Spatial augmentation transforms for BVH motion data.
Bvh-level API — All transforms operate on :class:~pybvh.bvh.Bvh
objects and follow the inplace=False convention: by default they
return a new object, leaving the original unchanged.
NumPy-level API — Lower-level functions (mirror_angles,
rotate_angles_vertical) accept raw arrays + minimal metadata for
users who work with pre-extracted arrays.
translate_root(bvh: Bvh, offset: npt.ArrayLike, inplace: bool = False) -> Bvh | None
¶
random_translate_root(bvh: Bvh, range_xyz: tuple[float, float] = (-100.0, 100.0), rng: np.random.Generator | None = None) -> Bvh
¶
Translate root by a random offset sampled uniformly per axis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
range_xyz
|
tuple of (low, high)
|
Uniform sampling range applied to each axis independently. |
(-100.0, 100.0)
|
rng
|
Generator or None
|
Random generator for reproducibility. |
None
|
Returns:
| Type | Description |
|---|---|
Bvh
|
|
add_noise(bvh: Bvh, sigma_deg: float, sigma_pos: float = 0.0, rng: np.random.Generator | None = None, inplace: bool = False, wrap: bool = True) -> Bvh | None
¶
Add Gaussian noise to joint rotation angles.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
sigma_deg
|
float
|
Standard deviation of noise in degrees, added to
|
required |
sigma_pos
|
float
|
Standard deviation of noise added to |
0.0
|
rng
|
Generator or None
|
Random generator for reproducibility. |
None
|
inplace
|
bool
|
If True, modify bvh and return None. |
False
|
wrap
|
bool
|
If True (default), wrap noised angles to [-π, π] (radians) so downstream Euler-to-rotmat round-trips don't see discontinuities. Set to False if the consumer handles angle ranges itself. |
True
|
Returns:
| Type | Description |
|---|---|
Bvh or None
|
|
perturb_speed(bvh: Bvh, factor: float) -> Bvh
¶
Change motion speed by resampling.
A factor of 2.0 makes the motion twice as fast (fewer frames);
0.5 makes it half as fast (more frames). Uses the existing
:meth:Bvh.resample which performs quaternion SLERP for rotations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
factor
|
float
|
Speed multiplier (must be > 0). |
required |
Returns:
| Type | Description |
|---|---|
Bvh
|
New Bvh with adjusted frame count and |
random_perturb_speed(bvh: Bvh, factor_range: tuple[float, float] = (0.8, 1.2), rng: np.random.Generator | None = None) -> Bvh
¶
Apply a random speed change sampled uniformly from factor_range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
factor_range
|
tuple of (low, high)
|
Range for the speed factor. |
(0.8, 1.2)
|
rng
|
Generator or None
|
Random generator for reproducibility. |
None
|
Returns:
| Type | Description |
|---|---|
Bvh
|
|
drop_frames(bvh: Bvh, drop_rate: float, rng: np.random.Generator | None = None, inplace: bool = False) -> Bvh | None
¶
Replace randomly selected frames with SLERP-interpolated values.
Dropped frames are filled by spherical linear interpolation (SLERP) of the nearest kept neighbours' quaternion rotations and linear interpolation of root positions. The output has the same frame count as the input.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
drop_rate
|
float
|
Fraction of frames to drop, in (0, 1). First and last frames are always kept. |
required |
rng
|
Generator or None
|
Random generator for reproducibility. |
None
|
inplace
|
bool
|
If True, modify bvh and return None. |
False
|
Returns:
| Type | Description |
|---|---|
Bvh or None
|
|
rotate_angles_vertical(joint_angles: npt.NDArray[np.float64], root_pos: npt.NDArray[np.float64], angle_deg: float, up_idx: int, root_order: str) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]
¶
Rotate motion around the vertical axis (NumPy-level).
Only modifies root_pos and the root joint's Euler angles
(index 0 in joint_angles). Non-root joints are in parent-local
coordinates and are unaffected.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
joint_angles
|
ndarray of shape (F, J, 3)
|
Euler angles in radians (pybvh's internal convention). |
required |
root_pos
|
ndarray of shape (F, 3)
|
Root translation per frame. |
required |
angle_deg
|
float
|
Rotation angle in degrees (user-facing — converted to radians internally). |
required |
up_idx
|
int
|
Index of the up axis (0=X, 1=Y, 2=Z). |
required |
root_order
|
str
|
Euler order of the root joint, e.g. |
required |
Returns:
| Type | Description |
|---|---|
(new_joint_angles, new_root_pos)
|
Copies with the rotation applied. Angles in radians. |
See Also
rotate_vertical : Bvh-level wrapper that auto-detects up_idx
and root_order from the skeleton.
Examples:
rotate_vertical(bvh: Bvh, angle_deg: float, up_axis: str | None = None, inplace: bool = False) -> Bvh | None
¶
Rotate the entire motion around the vertical (up) axis.
Only the root joint's world-space rotation and root position are modified. Child joints are in parent-local coordinates and are unaffected.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
angle_deg
|
float
|
Rotation angle in degrees (positive = counter-clockwise when viewed from above). |
required |
up_axis
|
str or None
|
Signed axis string (e.g. |
None
|
inplace
|
bool
|
If True, modify bvh and return None. |
False
|
Returns:
| Type | Description |
|---|---|
Bvh or None
|
|
random_rotate_vertical(bvh: Bvh, angle_range: tuple[float, float] = (-180.0, 180.0), up_axis: str | None = None, rng: np.random.Generator | None = None) -> Bvh
¶
Rotate motion by a random angle around the vertical axis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
angle_range
|
tuple of (low, high)
|
Angle sampling range in degrees. |
(-180.0, 180.0)
|
up_axis
|
str or None
|
Signed axis string. Auto-detected if None. |
None
|
rng
|
Generator or None
|
Random generator for reproducibility. |
None
|
Returns:
| Type | Description |
|---|---|
Bvh
|
|
mirror_angles(joint_angles: npt.NDArray[np.float64], root_pos: npt.NDArray[np.float64], lr_joint_pairs: list[tuple[int, int]], lateral_idx: int, rot_channels: list[list[str]]) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]
¶
Mirror joint angles and root position (NumPy-level).
Performs the array-level operations of mirroring: negating the
lateral component of root_pos, swapping L/R joint angle columns,
and negating Euler angle components whose rotation axis is not the
lateral axis.
This function does not modify skeleton offsets (bone geometry).
For a complete mirror that also adjusts the skeleton, use the
Bvh-level :func:mirror function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
joint_angles
|
ndarray of shape (F, J, 3)
|
Euler angles in radians (pybvh's internal convention). |
required |
root_pos
|
ndarray of shape (F, 3)
|
Root translation per frame. |
required |
lr_joint_pairs
|
list of (left_idx, right_idx)
|
Index pairs into the joint axis of |
required |
lateral_idx
|
int
|
Index of the lateral axis (0=X, 1=Y, 2=Z). |
required |
rot_channels
|
list of list of str
|
Per-joint Euler channel order, e.g. |
required |
Returns:
| Type | Description |
|---|---|
(new_joint_angles, new_root_pos)
|
Copies with the mirroring applied. |
See Also
mirror : Bvh-level wrapper that also mirrors skeleton offsets and
auto-detects lr_joint_pairs, lateral_idx, and
rot_channels from the skeleton.
Examples:
>>> angles = bvh.joint_angles
>>> pos = bvh.root_pos
>>> pairs = transforms.auto_detect_lr_pairs(bvh)
>>> lat_idx = {'x': 0, 'y': 1, 'z': 2}[bvh.left_at(frame=0)[1]]
>>> channels = [n.rot_channels for n in bvh.nodes if not n.is_end_site()]
>>> new_angles, new_pos = mirror_angles(
... angles, pos, pairs, lat_idx, channels)
auto_detect_lr_mapping(bvh: Bvh) -> dict[str, str]
¶
Return the L/R joint-name mapping for this skeleton.
Thin wrapper around :attr:Bvh.lr_mapping that returns an empty
dict (instead of None) when no pairs are available — back-compat
shape for code that expected a dict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input BVH with named joints. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
|
auto_detect_lr_pairs(bvh: Bvh) -> list[tuple[int, int]]
¶
Auto-detect left/right joint pairs as index tuples.
Wraps :func:auto_detect_lr_mapping and converts joint name
pairs to index pairs in joint_angles index space (axis 1
of bvh.joint_angles).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input BVH with named joints. |
required |
Returns:
| Type | Description |
|---|---|
list of (int, int)
|
|
mirror(bvh: Bvh, left_right_mapping: dict[str, str] | None = None, lateral_axis: str | None = None, inplace: bool = False) -> Bvh | None
¶
Mirror (reflect) the motion across the lateral plane.
Swaps left/right joint data and negates the appropriate rotation and position components so that the skeleton appears as a mirror image.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
left_right_mapping
|
dict or None
|
|
None
|
lateral_axis
|
str or None
|
Axis perpendicular to the mirror plane, e.g. |
None
|
inplace
|
bool
|
If True, modify bvh and return None. |
False
|
Returns:
| Type | Description |
|---|---|
Bvh or None
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If auto-detection finds no left/right pairs. |
reorient_world_up(bvh: Bvh, new_up: str, inplace: bool = False) -> Bvh | None
¶
Change the world coordinate system's vertical axis.
Applies a global rotation to the entire animation (root translation,
skeleton offsets, root joint rotations) so the world vertical axis
changes from the current bvh.world_up to new_up. The character
looks visually identical; only the coordinate system changes.
Restricted to axis-aligned rotations (multiples of 90 degrees) for lossless transformation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
|
required |
new_up
|
str
|
Target up axis, e.g. |
required |
inplace
|
bool
|
|
False
|
Returns:
| Type | Description |
|---|---|
Bvh or None
|
|
reorient_rest_up(bvh: Bvh, new_up: str, inplace: bool = False) -> Bvh | None
¶
Rotate the skeleton's rest-pose offsets so its topological up aligns
with new_up, compensating all joint rotations so that FK positions
are unchanged.
This fixes files where the rest pose and animation disagree on the up axis (e.g. rest pose authored in Y-up but animation plays in Z-up). After this call, the disagreement warning disappears.
The world coordinate system is unchanged: root_pos and world_up
are NOT modified.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
|
required |
new_up
|
str
|
Target rest-pose up axis, e.g. |
required |
inplace
|
bool
|
|
False
|
Returns:
| Type | Description |
|---|---|
Bvh or None
|
|
reorient_rest_forward(bvh: Bvh, new_forward: str, inplace: bool = False) -> Bvh | None
¶
Rotate the skeleton's rest-pose offsets so the character faces
new_forward, compensating all joint rotations so FK positions are
unchanged.
The rotation is around the world up axis (a rotation in the ground
plane). new_forward must not be parallel to world_up.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
|
required |
new_forward
|
str
|
Target rest-pose forward axis, e.g. |
required |
inplace
|
bool
|
|
False
|
Returns:
| Type | Description |
|---|---|
Bvh or None
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |