Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions musicalgestures/_pose.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ def pose(
avg_acc = np.zeros((self.height, self.width, 3), dtype=np.float64) if save_average_pose else None
avg_n = 0
from collections import deque
_trail = deque(maxlen=int(marker_history)) if marker_history and marker_history > 0 else None
_trail: deque | None = deque(maxlen=int(marker_history)) if marker_history and marker_history > 0 else None

while True:
# Read frame-by-frame
Expand All @@ -509,7 +509,7 @@ def pose(

H = output.shape[2]
W = output.shape[3]
points = []
points: list[tuple[int, int] | None] = []

for i in range(nPoints):

Expand Down Expand Up @@ -824,7 +824,7 @@ def _rerender_pose_from_cache(self: "musicalgestures.MgVideo", style='both', ove
video_out = None
frame_bytes = width * height * 3
from collections import deque
_trail = deque(maxlen=int(marker_history)) if marker_history and marker_history > 0 else None
_trail: deque | None = deque(maxlen=int(marker_history)) if marker_history and marker_history > 0 else None
pb = MgProgressbar(total=len(data), prefix='Re-rendering pose (cached):')

for ii, row in enumerate(data):
Expand Down Expand Up @@ -920,7 +920,7 @@ def _ensure_pose_keypoints(self: "musicalgestures.MgVideo", **pose_kwargs):

def mg_pose_waterfall(self: "musicalgestures.MgVideo", style: str = 'trajectories', n_samples: int = 40, markers: list | None = None, color_by: str | None = None,
cmap: str = 'hsv', dpi: int = 200, elev: float = 20, azim: float = -60, lw: float = 1.0, axes: bool = True, crop: bool = False,
target_name: str | None = None, overwrite: bool = True, **pose_kwargs) -> "MgFigure":
target_name: str | None = None, overwrite: bool = True, **pose_kwargs) -> "MgFigure | None":
"""
Render a 3D spatio-temporal waterfall of the pose, cascading along the time (depth) axis —
a pose-based counterpart to ``silhouette_waterfall()``. Uses cached pose keypoints from a
Expand Down Expand Up @@ -952,7 +952,7 @@ def mg_pose_waterfall(self: "musicalgestures.MgVideo", style: str = 'trajectorie
**pose_kwargs: Forwarded to ``pose()`` if keypoints have to be computed.

Returns:
MgFigure: the 3D waterfall figure, or None if there are too few frames.
MgFigure | None: the 3D waterfall figure, or None if there are too few frames.
"""
from musicalgestures._pose_visualize import render_pose_waterfall

Expand All @@ -974,7 +974,7 @@ def mg_pose_waterfall(self: "musicalgestures.MgVideo", style: str = 'trajectorie


def mg_pose_segments(self: "musicalgestures.MgVideo", segments: list | None = None, n_bins: int = 36, cmap: str = 'viridis', dpi: int = 200, ncols: int = 6,
target_name: str | None = None, overwrite: bool = True, **pose_kwargs) -> "MgFigure":
target_name: str | None = None, overwrite: bool = True, **pose_kwargs) -> "MgFigure | None":
"""
Circular (polar) motion plots and statistics for each body segment.

Expand All @@ -998,7 +998,7 @@ def mg_pose_segments(self: "musicalgestures.MgVideo", segments: list | None = No
**pose_kwargs: Forwarded to ``pose()`` if keypoints have to be computed.

Returns:
MgFigure: the grid of circular plots (per-segment stats in ``.data['stats']``), or None.
MgFigure | None: the grid of circular plots (per-segment stats in ``.data['stats']``), or None.
"""
from musicalgestures._pose_visualize import render_segment_circular

Expand All @@ -1018,7 +1018,7 @@ def mg_pose_segments(self: "musicalgestures.MgVideo", segments: list | None = No
return mgf


def mg_pose_center(self: "musicalgestures.MgVideo", save_data: bool = True, dpi: int = 200, target_name: str | None = None, overwrite: bool = True, **pose_kwargs) -> "MgFigure":
def mg_pose_center(self: "musicalgestures.MgVideo", save_data: bool = True, dpi: int = 200, target_name: str | None = None, overwrite: bool = True, **pose_kwargs) -> "MgFigure | None":
"""
Centre the pose data on its global centroid — a 2D port of the MoCap Toolbox ``mccenter``.

Expand All @@ -1037,7 +1037,8 @@ def mg_pose_center(self: "musicalgestures.MgVideo", save_data: bool = True, dpi:
**pose_kwargs: Forwarded to ``pose()`` if keypoints have to be computed.

Returns:
MgFigure: the centred-trajectories figure; ``.data['coords']`` holds the (T, n, 2) centred
MgFigure | None: the centred-trajectories figure; ``.data['coords']`` holds the (T, n, 2) centred
None when the figure could not be built, for instance with too few frames.
coordinates and ``.data['offset']`` the removed centroid. None if there are too few frames.
"""
from musicalgestures._pose_visualize import render_pose_center
Expand Down Expand Up @@ -1068,7 +1069,7 @@ def mg_pose_center(self: "musicalgestures.MgVideo", save_data: bool = True, dpi:
return mgf


def mg_pose_distance(self: "musicalgestures.MgVideo", dpi: int = 200, target_name: str | None = None, overwrite: bool = True, **pose_kwargs) -> "MgFigure":
def mg_pose_distance(self: "musicalgestures.MgVideo", dpi: int = 200, target_name: str | None = None, overwrite: bool = True, **pose_kwargs) -> "MgFigure | None":
"""
Per-marker distance travelled and the average across markers — a 2D port of the MoCap Toolbox
``mccumdist``.
Expand All @@ -1086,7 +1087,8 @@ def mg_pose_distance(self: "musicalgestures.MgVideo", dpi: int = 200, target_nam
**pose_kwargs: Forwarded to ``pose()`` if keypoints have to be computed.

Returns:
MgFigure: ``.data['total']`` (per-marker totals), ``.data['average']`` (mean total), and
MgFigure | None: ``.data['total']`` (per-marker totals), ``.data['average']`` (mean total), and
None when the figure could not be built, for instance with too few frames.
``.data['cumulative']`` (per-marker cumulative curves). None if there are too few frames.
"""
from musicalgestures._pose_visualize import render_pose_distance
Expand Down Expand Up @@ -1184,7 +1186,7 @@ def _pose_mediapipe(
avg_acc = np.zeros((self.height, self.width, 3), dtype=np.float64) if save_average_pose else None
avg_n = 0
from collections import deque
_trail = deque(maxlen=int(marker_history)) if marker_history and marker_history > 0 else None
_trail: deque | None = deque(maxlen=int(marker_history)) if marker_history and marker_history > 0 else None

estimator = MediaPipePoseEstimator(device=device.lower())

Expand Down
14 changes: 7 additions & 7 deletions musicalgestures/_pose_visualize.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import cv2
import matplotlib
import matplotlib.pyplot as plt
from musicalgestures._utils import MgImage, generate_outfilename
from musicalgestures._utils import MgFigure, MgImage, generate_outfilename


def _layout_labels(anchors, box_w, box_h, width, height, iterations=400, gap=None):
Expand Down Expand Up @@ -90,7 +90,7 @@ def _per_marker_stats(coords, fps, fmin=0.2, fmax=8.0):


def render_average_pose(data, names, connections, width, height, fps, avg_frame,
target_name, overwrite=True, fmin=0.2, fmax=8.0, style='both'):
target_name, overwrite=True, fmin=0.2, fmax=8.0, style='both') -> "MgImage | None":
"""
Render the average pose of the whole video, with per-marker quantity of motion
(colour + label) and dominant frequency (label) annotated.
Expand Down Expand Up @@ -191,7 +191,7 @@ def render_average_pose(data, names, connections, width, height, fps, avg_frame,


def render_trajectories(data, names, width, height, fps, target_name, overwrite=True,
background='black', labels=False):
background='black', labels=False) -> "MgImage | None":
"""
Render every marker's spatial trajectory across the whole video.

Expand Down Expand Up @@ -258,7 +258,7 @@ def render_trajectories(data, names, width, height, fps, target_name, overwrite=
def render_pose_waterfall(data, names, width, height, fps, target_name, overwrite=True,
style='trajectories', connections=None, n_samples=40,
markers=None, color_by=None, cmap='hsv', dpi=200,
elev=20, azim=-60, lw=1.0, axes=True, crop=False):
elev=20, azim=-60, lw=1.0, axes=True, crop=False) -> "MgFigure | None":
"""
Render a 3D spatio-temporal waterfall of the pose, cascading along the time (depth) axis —
a pose-based counterpart to ``silhouette_waterfall()``.
Expand Down Expand Up @@ -450,7 +450,7 @@ def _segment_angles(coords, a, b):

def render_segment_circular(data, names, connections, width, height, fps, target_name,
overwrite=True, segments=None, n_bins=36, cmap='viridis',
dpi=200, ncols=6):
dpi=200, ncols=6) -> "MgFigure | None":
"""
Circular (polar) motion plots and statistics for every body segment.

Expand Down Expand Up @@ -591,7 +591,7 @@ def pose_center(data, names):


def render_pose_center(data, names, width, height, target_name, overwrite=True,
cmap='hsv', dpi=200):
cmap='hsv', dpi=200) -> "MgFigure | None":
"""
Centre the pose data (see :func:`pose_center`) and plot the centred marker trajectories.

Expand Down Expand Up @@ -667,7 +667,7 @@ def pose_distance(data, names, width, height):


def render_pose_distance(data, names, width, height, fps, target_name, overwrite=True,
cmap='hsv', dpi=200):
cmap='hsv', dpi=200) -> "MgFigure | None":
"""
Plot per-marker cumulative distance travelled over time plus a ranked total per marker.

Expand Down
Loading