Features¶
features
¶
ML pipeline feature extraction for BVH motion data.
All functions are standalone and take a :class:~pybvh.bvh.Bvh object as
their first argument. Thin wrapper methods on the Bvh class delegate
to these functions.
node_velocities(bvh: Bvh, centered: str = 'world', in_frames: bool = False, coords: npt.NDArray[np.float64] | None = None, stencil: str = 'central', pad: str = 'edge') -> npt.NDArray[np.float64]
¶
Compute per-node position velocities (joints + end sites).
Two orthogonal choices:
stencilpicks the finite-difference method — central (second-order accurate, symmetric) or forward (first-order, causal).padpicks the boundary-handling convention —"edge"fills the boundary so the output has the same shape as the input;"none"drops the boundary frames that the stencil can't define.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
centered
|
str
|
Coordinate centering mode (default |
'world'
|
in_frames
|
bool
|
If True, return velocity in units/frame. If False (default), return velocity in units/second. |
False
|
coords
|
(ndarray, shape(F, N, 3))
|
Pre-computed spatial coordinates. If None, computed
internally via :meth: |
None
|
stencil
|
('central', 'forward')
|
|
"central"
|
pad
|
('edge', 'none')
|
|
"edge"
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Shape depends on ========= ====== ================
stencil pad shape
========= ====== ================
central edge |
See Also
joint_velocities : Same data restricted to non-end-site joints
((F, J, 3)). Use that when the output should index-align
with :attr:Bvh.joint_angles / :func:angular_velocities.
Raises:
| Type | Description |
|---|---|
ValueError
|
If the clip is too short for the chosen combination,
|
joint_velocities(bvh: Bvh, centered: str = 'world', in_frames: bool = False, coords: npt.NDArray[np.float64] | None = None, stencil: str = 'central', pad: str = 'edge') -> npt.NDArray[np.float64]
¶
Compute per-joint position velocities (end sites excluded).
Returns the joint-axis subset of :func:node_velocities — same
finite-difference math, but restricted to non-end-site joints so
the output indexes match :attr:Bvh.joint_angles and
:func:angular_velocities. Output shape is (F, J, 3) (or the
appropriate trimmed variant per stencil × pad).
See :func:node_velocities for the full parameter / shape docs.
node_accelerations(bvh: Bvh, centered: str = 'world', in_frames: bool = False, coords: npt.NDArray[np.float64] | None = None, stencil: str = 'central', pad: str = 'edge') -> npt.NDArray[np.float64]
¶
Compute per-node position accelerations (joints + end sites).
Applies the chosen stencil twice to the input positions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
centered
|
str
|
Coordinate centering mode (default |
'world'
|
in_frames
|
bool
|
If True, return acceleration in units/frame^2. If False (default), return in units/second^2. |
False
|
coords
|
(ndarray, shape(F, N, 3))
|
Pre-computed spatial coordinates. If None, computed
internally via :meth: |
None
|
stencil
|
('central', 'forward')
|
Finite-difference method applied twice. Default |
"central"
|
pad
|
('edge', 'none')
|
Boundary handling. |
"edge"
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Shape depends on ========= ====== ================
stencil pad shape
========= ====== ================
central edge |
Composition identity: ``np.gradient(node_velocities(), dt)`` equals
|
|
``node_accelerations()`` exactly under the defaults
|
|
(``stencil="central"``, ``pad="edge"``). Not guaranteed for other
|
|
combinations.
|
|
See Also
joint_accelerations : Same data restricted to non-end-site joints
((F, J, 3)).
Raises:
| Type | Description |
|---|---|
ValueError
|
If the clip is too short for the chosen combination,
|
joint_accelerations(bvh: Bvh, centered: str = 'world', in_frames: bool = False, coords: npt.NDArray[np.float64] | None = None, stencil: str = 'central', pad: str = 'edge') -> npt.NDArray[np.float64]
¶
Compute per-joint position accelerations (end sites excluded).
Returns the joint-axis subset of :func:node_accelerations — same
twice-applied finite-difference math, restricted to non-end-site
joints so output indexes match :attr:Bvh.joint_angles. Output
shape is (F, J, 3) (or the appropriate trimmed variant per
stencil × pad).
See :func:node_accelerations for the full parameter / shape docs.
angular_velocities(bvh: Bvh, in_frames: bool = False, stencil: str = 'central', pad: str = 'edge', degrees: bool = False) -> npt.NDArray[np.float64]
¶
Compute per-joint angular velocities via rotation matrix log map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
in_frames
|
bool
|
If True, return angular velocity in radians/frame (or
degrees/frame if |
False
|
degrees
|
bool
|
If True, convert the final output from radians to degrees.
Default False (radians). Consistent with the |
False
|
stencil
|
('central', 'forward')
|
|
"central"
|
pad
|
('edge', 'none')
|
|
"edge"
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Shape depends on ========= ====== ================
stencil pad shape
========= ====== ================
central edge Direction is the rotation axis; magnitude is the rotation
angle (radians or radians/second). Angles are clamped to
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If fewer than 2 frames ( |
root_trajectory(bvh: Bvh, up_axis: str | None = None, include_velocities: bool = False, stencil: str = 'central', pad: str = 'edge', degrees: bool = False) -> npt.NDArray[np.float64]
¶
Extract root trajectory features commonly used in motion ML.
Returns the root's ground-plane position and heading angle (as
sin/cos pair). Optionally appends ground-plane and
heading velocities.
The heading reference is the rest-pose forward direction —
derived from the skeleton's L/R lateral geometry crossed with
world_up (see :func:pybvh.tools._compute_forward_at). This
means "heading = rest-pose forward" at any frame whose root
rotation is identity, regardless of what rotation the clip
starts with.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
up_axis
|
str or None
|
Signed axis string (e.g. |
None
|
include_velocities
|
bool
|
If True, append |
False
|
stencil
|
optional
|
Only used with |
'central'
|
pad
|
optional
|
Only used with |
'central'
|
degrees
|
bool
|
If True, convert the |
False
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Shape Columns: |
foot_contacts(bvh: Bvh, foot_joints: list[str] | None = None, method: str = 'combined', centered: str = 'world', coords: npt.NDArray[np.float64] | None = None, *, vel_threshold: float | None = None, height_threshold: float | None = None, floor: float | str = 'auto', min_contact_duration: float = 0.1, min_gap_duration: float = 0.1, return_info: bool = False) -> npt.NDArray[np.float64] | tuple[npt.NDArray[np.float64], dict]
¶
Detect binary foot contact labels per frame.
The default combines a velocity check (foot not moving) and a
height check (foot near the ground) following the HuMoR heuristic
— each signal catches a different failure mode of the other.
method="velocity" and method="height" remain available as
single-signal escape hatches.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
foot_joints
|
list of str or None
|
Explicit foot joints (recommended). If None, falls back to
:func: |
None
|
method
|
('combined', 'velocity', 'height')
|
|
"combined"
|
centered
|
str
|
Coordinate centering mode (default |
'world'
|
coords
|
(ndarray, shape(F, N, 3))
|
Pre-computed spatial coordinates. |
None
|
vel_threshold
|
(float or None, keyword - only)
|
Speed threshold in world units per frame. Defaults to
|
None
|
height_threshold
|
(float or None, keyword - only)
|
Clearance above the estimated floor, in world units. Defaults
to |
None
|
floor
|
float or ``"auto"``, keyword-only
|
Floor height along the raw |
'auto'
|
min_contact_duration
|
(float, keyword - only)
|
Morphological open: contact runs shorter than this many
seconds are set to 0. Default |
0.1
|
min_gap_duration
|
(float, keyword - only)
|
Morphological close: non-contact gaps shorter than this many
seconds are filled (set to 1). Default |
0.1
|
return_info
|
(bool, keyword - only)
|
If True, return |
False
|
Returns:
| Type | Description |
|---|---|
ndarray of shape ``(F, num_foot_joints)``, or
|
|
tuple ``(ndarray, dict)`` when ``return_info=True``.
|
Binary contact labels (1.0 = contact, 0.0 = no contact).
For |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
auto_detect_foot_joints(bvh: Bvh, _rest_coords: npt.NDArray[np.float64] | None = None) -> list[str]
¶
Auto-detect foot joint names by topology.
Algorithm:
- Substring match: candidates are joints whose names contain
"foot"or"toe"(case-insensitive). - Tip-descendant filter: keep only candidates that have an end site or a toe-named child. This drops IK helpers, which typically have no children.
- Most-distal filter: drop candidates whose subtree (any depth)
contains another candidate. On a rig with
Foot → ToeBase → EndSite, this keeps onlyToeBase— the more distal, ground-contacting joint. - Deterministic order: sort by rest-pose height along
bvh.world_up(lowest first); alphabetical name within ties so the output is stable across runs.
If step 1 produces matches but step 2 drops all of them, the
tip filter is skipped with a UserWarning (better than
returning nothing for unusual rigs).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input skeleton. |
required |
Returns:
| Type | Description |
|---|---|
list of str
|
Joint names in deterministic order. Empty if no candidates. |
Notes
This is the same detection used internally by
:func:foot_contacts when foot_joints=None. Call it
directly to preview the detection or to feed an explicit list
back in.
feature_array_layout(*, num_joints: int, num_feet: int = 0, representation: str = '6d', include_root_pos: bool = True, include_velocities: bool = False, include_foot_contacts: bool = False) -> dict[str, slice]
¶
Column layout of the array returned by :func:to_feature_array.
Returns a dict mapping block name to column slice so callers can
write feat[:, layout['rotations']] without counting columns.
Pure function — no :class:~pybvh.bvh.Bvh required; useful for
model-shape setup before any data is loaded.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_joints
|
int
|
Number of joints (excluding end sites). Used for both the rotation and velocity blocks: velocities are per-joint (not per-node), aligning with the rotation block's joint axis. |
required |
num_feet
|
int
|
Number of foot joints for contact detection. Required (>0)
when |
0
|
representation
|
str
|
Rotation representation: |
'6d'
|
include_root_pos
|
bool
|
Mirror the flags of :func: |
True
|
include_velocities
|
bool
|
Mirror the flags of :func: |
True
|
include_foot_contacts
|
bool
|
Mirror the flags of :func: |
True
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
to_feature_array(bvh: Bvh, representation: str = '6d', include_root_pos: bool = True, include_velocities: bool = False, include_foot_contacts: bool = False, centered: str = 'world', foot_joints: list[str] | None = None, stencil: str = 'central', pad: str = 'edge') -> npt.NDArray[np.float64]
¶
Export motion as a single flat feature array for ML pipelines.
Composes root position, joint rotations, velocities, and foot
contacts into a single (F, D) array ready for model input.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
Input motion. |
required |
representation
|
str
|
Rotation representation: |
'6d'
|
include_root_pos
|
bool
|
If True (default), include root position (3 columns). |
True
|
include_velocities
|
bool
|
If True, include joint velocity features. |
False
|
include_foot_contacts
|
bool
|
If True, include foot contact labels. |
False
|
centered
|
str
|
Coordinate centering mode (default |
'world'
|
foot_joints
|
list of str or None
|
Foot joints for contact detection. Only used when
|
None
|
stencil
|
optional
|
Only affect output when |
'central'
|
pad
|
optional
|
Only affect output when |
'central'
|
Returns:
| Type | Description |
|---|---|
ndarray, shape (F, D), (F-1, D), or (F-2, D)
|
See :func: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |