Skip to main content

BVH reader/writer and kinematics utilities

Project description

pybvh

PyPI version Python License: MIT

A lightweight Python library for reading, writing, and manipulating BVH motion capture files. Built for researchers and developers working with skeletal animation and motion data.

Features

  • Read & write BVH files with full hierarchy and motion data preservation
  • Rotation conversions between Euler angles, rotation matrices, quaternions, 6D (Zhou et al.), and axis-angle — all vectorized with NumPy
  • Forward kinematics to compute 3D joint positions from angles
  • Skeleton operations: retargeting, scaling, joint extraction, Euler order changes
  • Frame operations: slicing, concatenation, resampling to different frame rates
  • Spatial transforms: mirroring, vertical rotation, speed perturbation, joint noise, root translation, frame dropout — all with seeded randomization
  • Motion analysis: joint velocities/accelerations, root trajectory, foot contact detection, normalization utilities, and a one-stop to_feature_array() export
  • Batch loading of entire directories with optional parallel I/O
  • NumPy export in any rotation representation — ready for any downstream workflow
  • Pandas ready via an export option ready to become a DataFrame
  • 3D visualization with multiple backends (matplotlib, OpenCV, k3d, vedo)

Philosophy

pybvh is framework-agnostic and outputs pure NumPy arrays. It understands motion capture data but does not assume what you'll do with it — the same library serves ML researchers, biomechanics scientists, and game developers. For ML-specific features (tensor packing, PyTorch Datasets, augmentation pipelines), see the companion library pybvh-ml.

Installation

pip install pybvh

Quick Start

import pybvh

# Load a BVH file
bvh = pybvh.read_bvh_file("walk.bvh")
print(bvh)  # "24 elements in the Hierarchy, 75 frames at 30.0 fps (frame_time=0.033333s)"

# Access motion data as NumPy arrays
bvh.root_pos          # (F, 3) root translation per frame
bvh.joint_angles      # (F, J, 3) Euler angles in radians
bvh.joint_names       # ['Hips', 'Spine', ...] (excludes end sites)

# Get 3D joint positions via forward kinematics
coords = bvh.node_positions()  # (F, N, 3)

# Convert to other rotation representations
root_pos, quats = bvh.to_quaternions()   # (F, 3), (F, J, 4)
root_pos, rot6d = bvh.to_6d()            # (F, 3), (F, J, 6)

# Write back to file
bvh.write("output.bvh")

Batch Loading

Load an entire directory of BVH files and convert to NumPy arrays in one call:

from pybvh import read_bvh_directory, batch_to_numpy

# Load all BVH files from a directory
clips = read_bvh_directory("dataset/", parallel=True)

# Convert to padded NumPy array
data = batch_to_numpy(clips, representation="6d", pad=True)
# shape: (batch, max_frames, features)

Supported representations: "euler", "quaternion", "6d", "axisangle", "rotmat".

Motion Analysis

Compute motion derivatives, foot contacts, and export everything in a single array:

# Joint velocities and accelerations (finite differences of FK positions).
# Defaults: central stencil + edge padding — output has the same leading
# dimension as the input.  Pass stencil="forward", pad="none" for the
# traditional (F-1, ...) / (F-2, ...) forward-difference shapes.
vel = bvh.joint_velocities()        # (F, N, 3) in units/second
acc = bvh.joint_accelerations()     # (F, N, 3)
ang_vel = bvh.angular_velocities()  # (F, J, 3) in radians/second

# Skeleton-centered positions and trajectory
rel_pos = bvh.node_positions(centered='skeleton')  # (F, N, 3)
traj = bvh.root_trajectory()                       # (F, 4) ground pos + heading

# Foot contact detection (auto-detects foot joints)
contacts = bvh.foot_contacts()  # (F, num_feet) binary indicators

# One-stop export — flat feature array
features = bvh.to_feature_array(
    representation="6d",
    include_velocities=True,
    include_foot_contacts=True,
)  # (F, D)

Normalize across a dataset:

from pybvh import compute_normalization_stats, normalize_array, denormalize_array

stats = compute_normalization_stats(clips, representation="6d")
normalized = normalize_array(data, stats)
# stats are plain dicts — save with np.savez("stats.npz", **stats)

Spatial Transforms

Standard motion transforms — all support seeded randomization for reproducibility:

from pybvh import transforms

# Left-right mirroring (auto-detects joint pairs and lateral axis)
bvh_mirrored = transforms.mirror(bvh)

# Vertical rotation (auto-detects up axis)
bvh_rotated = transforms.rotate_vertical(bvh, angle_deg=90)
bvh_rotated = transforms.random_rotate_vertical(bvh, rng=np.random.default_rng(42))

# Speed perturbation (factor > 1 = faster, < 1 = slower)
bvh_fast = transforms.perturb_speed(bvh, factor=1.5)

# Joint noise injection
bvh_noisy = transforms.add_noise(bvh, sigma_deg=1.0, sigma_pos=0.5, rng=rng)

# Root translation
bvh_shifted = transforms.translate_root(bvh, offset=[100, 0, 0])

# Frame dropout with SLERP interpolation (same frame count)
bvh_dropped = transforms.drop_frames(bvh, drop_rate=0.1, rng=rng)

All transforms also available as Bvh methods: bvh.mirror(), bvh.rotate_vertical(90), etc.

Skeleton Operations

# Change Euler rotation order for all joints (or a single joint via `joint=`)
bvh_xyz = bvh.change_euler_order("XYZ")
bvh_hips_xyz = bvh.change_euler_order("XYZ", joint="Hips")

# Scale the skeleton
bvh_scaled = bvh.scale(0.01)  # meters to centimeters

# Retarget motion to a different skeleton
bvh_retarget = bvh.retarget(reference_bvh)

# Extract a subset of joints
bvh_upper = bvh.extract_joints(["Hips", "Spine", "Neck", "Head"])

# Slice and concatenate frames
clip = bvh.slice_frames(10, 50)
combined = bvh.concat(other_bvh)

# Resample to a different frame rate
bvh_30fps = bvh.resample(30)

Rotation Utilities

All functions are batch-vectorized and work on arbitrary batch dimensions:

from pybvh import rotations

# Convert between any pair of representations
R = rotations.euler_to_rotmat(angles, order="ZYX", degrees=True)
q = rotations.rotmat_to_quat(R)
aa = rotations.quat_to_euler(q, order="ZYX", degrees=True)

# Quaternion SLERP interpolation
q_mid = rotations.quat_slerp(q1, q2, t=0.5)

Visualization

# Single-skeleton calls are methods on the Bvh object
bvh.plot_rest_pose()                             # T-pose
bvh.plot_frame(frame=0, camera="front")          # also "side", "top", (azim, elev)
bvh.plot_trajectory()                            # 2D top-down root path
bvh.render("walk.mp4")                           # video/GIF/HTML export
bvh.render("walk.mp4", follow=True)              # camera tracks character as it turns
bvh.play()                                       # interactive playback (auto-detects backend)

# Multi-skeleton comparisons go through the module functions
from pybvh import bvhplot
bvhplot.frame([bvh1, bvh2], frame=0, labels=["Original", "Generated"])
bvhplot.render([bvh1, bvh2], "compare.mp4", sync="pad")

# Orientation API — (world_up, forward_at, left_at) form an orthonormal
# right-hand-rule triple (left = world_up × forward_at)
bvh.world_up          # '+y' / '+z' — animation-derived gravity axis
bvh.forward_at(0)     # character's facing direction at a given frame
bvh.left_at(0)        # character's leftward direction at a given frame
bvh.world_up = '+y'   # manual override if auto-detect is wrong for your file

# Pose-independent companions (read from rest pose only)
bvh.rest_up           # rest-pose up axis — compare to world_up to spot mismatches
bvh.rest_forward      # rest-pose facing direction

Install optional visualization backends for best performance:

pip install pybvh[opencv]       # Fast video rendering 
pip install pybvh[interactive]  # k3d for Jupyter notebooks
pip install pybvh[viewer]       # vedo for desktop interactive viewer
pip install pybvh[all-viz]      # All of the above

Pandas Integration

import pandas as pd

# BVH to DataFrame
df = pd.DataFrame(bvh.to_df_dict(mode="euler"))

# DataFrame back to BVH
from pybvh import df_to_bvh
bvh_from_df = df_to_bvh(bvh.hierarchy_info_as_dict(), df)

Tutorials

The repository includes Jupyter notebooks with detailed walkthroughs:

  1. Introduction to pybvh — reading, writing, and basic operations
  2. Spatial coordinates — forward kinematics and 3D positions
  3. Rotations — rotation representations and conversions
  4. Visualization — static frames, video export, and interactive playback
  5. Transforms — mirroring, rotation, speed perturbation, noise
  6. Features — velocities, foot contacts, feature-array export
  7. Batch processing — directory loading, normalization, harmonization

Each tutorial is committed as a Jupytext-paired .ipynb + .py (Percent format) so the source is reviewable as plain Python. See the tutorials docs for the contributor workflow.

Requirements

  • Python >= 3.9
  • NumPy >= 1.21
  • Matplotlib >= 3.7

Pandas is optional (pip install "pybvh[pandas]") - only used in the tutorials, not part of pybvh library.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pybvh-0.7.0.tar.gz (123.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pybvh-0.7.0-py3-none-any.whl (130.8 kB view details)

Uploaded Python 3

File details

Details for the file pybvh-0.7.0.tar.gz.

File metadata

  • Download URL: pybvh-0.7.0.tar.gz
  • Upload date:
  • Size: 123.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pybvh-0.7.0.tar.gz
Algorithm Hash digest
SHA256 8b71f574192485b0019abeceda7e3e3cf7ee37c66361e369120a7e8a417c2d3a
MD5 c1c8f056228901d4792fc7b78a920c18
BLAKE2b-256 5c6c3032ed4925d888b222a47a18a4ca1af392a266f6c4dfe897fcb8151e895b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pybvh-0.7.0.tar.gz:

Publisher: publish.yml on VictorS-67/pybvh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pybvh-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: pybvh-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 130.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pybvh-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2b6076e29b0dab18929061061903034de0b49ef3315ce3b47fcb7d9b5932a32d
MD5 df21719b7727380e0eb2910c7f2af1d6
BLAKE2b-256 d75d3822143eaa30e49c69a02b23d122bb6fda2c7fd80a6e3917af12f817106e

See more details on using hashes here.

Provenance

The following attestation bundles were made for pybvh-0.7.0-py3-none-any.whl:

Publisher: publish.yml on VictorS-67/pybvh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page