Visualization¶
Public API¶
bvhplot
¶
Visualization module for pybvh.
Provides five main functions:
- :func:
rest_pose— T-pose / bind pose visualization (matplotlib). - :func:
frame— static 3D skeleton snapshot (matplotlib). - :func:
play— interactive playback with camera controls. - :func:
render— fast export to video/GIF/HTML. - :func:
trajectory— 2D top-down root trajectory plot.
Backends
render supports "opencv" (fast, optional dep) and "matplotlib"
(default fallback). When backend is "auto" (the default), OpenCV is
used if available.
play supports "k3d" (Jupyter notebooks, optional dep), "vedo"
(desktop window, optional dep), and "matplotlib" (fallback). When
backend is "auto", the best available backend for the current
environment is selected automatically.
Install optional backends::
pip install pybvh[opencv] # fast video rendering
pip install pybvh[interactive] # k3d for Jupyter
pip install pybvh[viewer] # vedo for desktop
pip install pybvh[all-viz] # all of the above
rest_pose(bvh: Bvh | list[Bvh], *, labels: list[str] | None = None, figsize: tuple[float, float] | None = None, show: bool = False, camera: str | tuple[float, float] = 'front', ax: matplotlib.axes.Axes | None = None) -> tuple[matplotlib.figure.Figure, matplotlib.axes.Axes | list[matplotlib.axes.Axes]]
¶
Plot the rest pose (T-pose / bind pose) of one or more skeletons.
All joint angles are zero and root is at the origin.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh or list[Bvh]
|
One or more BVH objects. Pass a list for side-by-side comparison. |
required |
labels
|
list[str]
|
Subplot titles for side-by-side comparison. |
None
|
figsize
|
(float, float)
|
Figure size in inches. |
None
|
show
|
bool
|
If |
False
|
camera
|
str or (float, float)
|
Camera preset ( |
'front'
|
ax
|
Axes
|
Existing 3D axes to draw on. If provided, no new figure is
created. Only supported for a single skeleton (raises
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
fig |
Figure
|
|
ax |
Axes or list[Axes]
|
Single axes when one skeleton, list when multiple. |
frame(bvh: Bvh | list[Bvh], frame: int | npt.NDArray[np.floating] = 0, *, centered: str = 'world', labels: list[str] | None = None, figsize: tuple[float, float] | None = None, show: bool = False, camera: str | tuple[float, float] = 'front', ax: matplotlib.axes.Axes | None = None) -> tuple[matplotlib.figure.Figure, matplotlib.axes.Axes | list[matplotlib.axes.Axes]]
¶
Plot a static 3D skeleton snapshot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh or list[Bvh]
|
One or more BVH objects. Pass a list for side-by-side comparison. |
required |
frame
|
int or ndarray
|
Frame index (default 0) or pre-computed spatial coordinates. |
0
|
centered
|
str
|
Centering mode: |
'world'
|
labels
|
list[str]
|
Subplot titles for side-by-side comparison. |
None
|
figsize
|
(float, float)
|
Figure size in inches. |
None
|
show
|
bool
|
If |
False
|
camera
|
str or (float, float)
|
Camera preset ( |
'front'
|
ax
|
Axes
|
Existing 3D axes to draw on. If provided, no new figure is
created. Only supported for a single skeleton (raises
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
fig |
Figure
|
|
ax |
Axes or list[Axes]
|
Single axes when one skeleton, list when multiple. |
render(bvh: Bvh | list[Bvh], filepath: str | Path = Path('./anim.mp4'), *, centered: str = 'world', labels: list[str] | None = None, fps: int = -1, backend: str = 'auto', camera: str | tuple[float, float] = 'front', resolution: tuple[int, int] = (1920, 1080), show_axis: bool = False, sync: str = 'truncate', follow: bool = False, match_fps: str | None = None) -> Path
¶
Render animation to a video, GIF, or HTML file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh or list[Bvh]
|
One or more BVH objects. Pass a list for side-by-side comparison. |
required |
filepath
|
str or Path
|
Output file path (default |
Path('./anim.mp4')
|
centered
|
str
|
Centering mode: |
'world'
|
labels
|
list[str]
|
Labels for each skeleton when comparing. |
None
|
fps
|
int
|
Frames per second. |
-1
|
backend
|
str
|
|
'auto'
|
camera
|
str or (float, float)
|
Camera preset ( |
'front'
|
resolution
|
(int, int)
|
Output resolution |
(1920, 1080)
|
show_axis
|
bool
|
Show 3D axes (default |
False
|
sync
|
str
|
How to handle different frame counts in side-by-side comparison:
|
'truncate'
|
follow
|
bool
|
If |
False
|
match_fps
|
str or None
|
How to handle clips with different frame rates in side-by-side
rendering. |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
The path to the written file. |
play(bvh: Bvh | list[Bvh], *, centered: str = 'world', labels: list[str] | None = None, fps: int = -1, backend: str = 'auto', camera: str | tuple[float, float] = 'front', sync: str = 'truncate', resolution: tuple[int, int] = (960, 540), quality: str = 'high', match_fps: str | None = None, spacing: float | str = 'auto') -> object
¶
Play back motion data.
Auto-detects the best backend for the current environment:
- Tier 1 (interactive): k3d in Jupyter notebooks, vedo on desktop.
- Tier 2 (fast fallback): OpenCV renders to an inline video (notebook) or matplotlib animated window (script).
- Tier 3 (slow fallback): matplotlib jshtml inline (notebook) or animated window (script).
When falling back, warnings indicate which packages to install for a better experience.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh or list[Bvh]
|
One or more BVH objects. Pass a list for side-by-side comparison. |
required |
centered
|
str
|
Centering mode: |
'world'
|
labels
|
list[str]
|
Labels for each skeleton when comparing. |
None
|
fps
|
int
|
Frames per second. |
-1
|
backend
|
str
|
|
'auto'
|
camera
|
str or (float, float)
|
Camera preset ( |
'front'
|
sync
|
str
|
How to handle different frame counts in side-by-side comparison:
|
'truncate'
|
resolution
|
(int, int)
|
Output resolution |
(960, 540)
|
quality
|
str
|
Visual quality for the vedo desktop backend:
|
'high'
|
match_fps
|
str or None
|
How to handle clips with different frame rates. |
None
|
spacing
|
float or 'auto'
|
Lateral separation between skeletons in single-scene backends (k3d,
vedo). |
'auto'
|
Returns:
| Type | Description |
|---|---|
object
|
Backend-specific return value (widget, plotter, or None). |
trajectory(bvh: Bvh | list[Bvh], *, centered: str = 'world', labels: list[str] | None = None, figsize: tuple[float, float] | None = None, show: bool = False, ax: matplotlib.axes.Axes | None = None, facing_arrows: bool = False, tight: bool = False) -> tuple[matplotlib.figure.Figure, matplotlib.axes.Axes]
¶
Plot 2D top-down trajectory of the root joint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh or list[Bvh]
|
One or more BVH objects. Pass a list for overlaid comparison. |
required |
centered
|
str
|
Centering mode: |
'world'
|
labels
|
list[str]
|
Legend labels. |
None
|
figsize
|
(float, float)
|
Figure size in inches. |
None
|
show
|
bool
|
If |
False
|
ax
|
Axes
|
Existing 2D axes to draw on. If provided, no new figure is created. Works with single or multiple skeletons (overlaid). |
None
|
facing_arrows
|
bool
|
If True, overlay small arrowheads along each skeleton's path showing the character's facing direction at ~10 evenly-spaced frames. Arrows use the same color as the trajectory line and are sized at ~8 % of the path's span. Default False. |
False
|
tight
|
bool
|
If False (default), the axis range matches the full horizontal
extent of the skeleton across all joints and frames — the same
bounding box |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
fig |
Figure
|
|
ax |
Axes
|
|
Shared Helpers¶
_common
¶
Shared helpers for all visualization backends.
Pure-data operations: skeleton topology, bounding boxes, camera math, and orthographic projection. No plotting library imports.
get_skeleton_lines(bvh: Bvh) -> list[tuple[int, int]]
¶
Precompute (parent_node_idx, child_node_idx) pairs for bone drawing.
Computed once per skeleton, reused every frame by all backends.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
The BVH object containing the skeleton hierarchy. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
lines |
list of (int, int)
|
Each tuple is |
normalize_input(bvh: Bvh | list[Bvh], frames: int | npt.NDArray[np.floating] | None, centered: str) -> tuple[list[Bvh], list[npt.NDArray[np.float64]]]
¶
Normalize single/list Bvh + frame spec into parallel lists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh or list[Bvh]
|
One or more BVH objects to visualize. |
required |
frames
|
int, ndarray, or None
|
|
required |
centered
|
str
|
Centering mode passed to |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bvh_list |
list[Bvh]
|
Always a list (length >= 1). |
coords_list |
list[ndarray]
|
Parallel list of spatial coordinates, each |
compute_unified_limits(coords_list: list[npt.NDArray[np.float64]]) -> tuple[npt.NDArray[np.float64], float]
¶
Compute a cubic bounding box encompassing all skeletons and frames.
The half-span is the larger of the per-frame body size and the trajectory extent from center. This ensures stationary skeletons fill the frame while walking skeletons never clip.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coords_list
|
list of ndarray
|
Each element has shape |
required |
Returns:
| Name | Type | Description |
|---|---|---|
center |
ndarray of shape (3,)
|
Center of the bounding box in world coordinates. |
half_span |
float
|
Half the side length of the cubic bounding box. |
get_camera_angles(bvh: Bvh, ref_frame: npt.NDArray[np.float64], camera: str | tuple[float, float] = 'front') -> tuple[float, float, str]
¶
Resolve a camera specification to (azimuth, elevation, up_axis).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bvh
|
Bvh
|
The BVH object (used for axis detection). |
required |
ref_frame
|
ndarray of shape (N, 3)
|
A reference frame of spatial coordinates for axis heuristics. |
required |
camera
|
str or (float, float)
|
|
'front'
|
Returns:
| Name | Type | Description |
|---|---|---|
azimuth |
float
|
Azimuth angle in degrees. |
elevation |
float
|
Elevation angle in degrees. |
up_axis |
str
|
Single character: |
build_view_matrix(azimuth_deg: float, elevation_deg: float, up_axis: str) -> npt.NDArray[np.float64]
¶
Build a 3x3 rotation that maps world coordinates to view coordinates.
Uses the same look-at camera math as matplotlib's view_init
so that both backends produce identical views for the same
(azimuth, elevation, up_axis) parameters.
View coordinate convention: x = right on screen, y = up on screen, z = out of screen (toward viewer).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
azimuth_deg
|
float
|
Azimuth rotation in degrees. |
required |
elevation_deg
|
float
|
Elevation rotation in degrees. |
required |
up_axis
|
str
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
view_matrix |
ndarray of shape (3, 3)
|
|
ortho_project(coords_3d: npt.NDArray[np.float64], view_matrix: npt.NDArray[np.float64], center: npt.NDArray[np.float64], half_span: float, resolution: tuple[int, int], fixed_view_half: tuple[float, float] | None = None) -> npt.NDArray[np.int32]
¶
Orthographic projection from 3D world to 2D pixel coordinates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coords_3d
|
ndarray of shape (N, 3)
|
World-space joint positions for one frame. |
required |
view_matrix
|
ndarray of shape (3, 3)
|
From :func: |
required |
center
|
ndarray of shape (3,)
|
World-space center of the bounding box. |
required |
half_span
|
float
|
Half the side length of the cubic bounding box. |
required |
resolution
|
(width, height)
|
Output image dimensions in pixels. |
required |
fixed_view_half
|
(float, float)
|
Pre-computed |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
pixels |
ndarray of shape (N, 2)
|
Integer pixel coordinates |
align_frame_counts(coords_list: list[npt.NDArray[np.float64]], pad: bool = False) -> list[npt.NDArray[np.float64]]
¶
Align all coordinate arrays to the same frame count.
When comparing multiple skeletons with different frame counts, arrays are either truncated to the minimum or padded to the maximum (by repeating the last frame).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coords_list
|
list of ndarray
|
Each element has shape |
required |
pad
|
bool
|
If |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
coords_list |
list of ndarray
|
Arrays all with the same frame count. |