Skip to content

Commit

Permalink
Merge pull request #31 from Ipuch/multiframerate
Browse files Browse the repository at this point in the history
- multiple colors for xp markers
- nan avoided to compute the location of the floor
- little offset to avoid mesh flickering between force plates and floor
- fix: dependencies
- first notebook example
  • Loading branch information
Ipuch authored Jun 23, 2024
2 parents ccea7d3 + b7ef6fb commit b83da89
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 14 deletions.
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ channels:
- default
dependencies:
- ezc3d
- rerun-sdkrerun-sdk=0.16.1
- rerun-sdk=0.16.1
- numpy
- biorbd>=1.10.5
- trimesh
Expand Down
55 changes: 55 additions & 0 deletions examples/c3d/running_gait.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "aabbe278-4fdb-43ac-b880-4fea8dd0b057",
"metadata": {},
"outputs": [],
"source": [
"import pyorerun as prr\n",
"import rerun as rr\n",
"\n",
"prr.c3d(\n",
" \"Running_0002.c3d\",\n",
" show_floor=True,\n",
" show_force_plates=True,\n",
" show_forces=True,\n",
" down_sampled_forces=True,\n",
" video=(\"Running_0002_Oqus_6_15004.avi\", \"Running_0002_Oqus_9_15003.avi\"),\n",
" notebook=True,\n",
")\n",
"rr.notebook_show(width=1200, height=600)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a80e92a4-89d4-4d02-b69a-07e22651838b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
8 changes: 5 additions & 3 deletions pyorerun/multi_frame_rate_phase_rerun.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,15 @@ def cumulative_frames_in_merged_t_span(self) -> list[list[int]]:
frame_t_span_idx = self.frame_t_span_idx
return [calculate_cumulative_frames(p, frame_t_span_idx) for p in range(self.nb_phases)]

def rerun(self, name: str = "animation_phase", init: bool = True, clear_last_node: bool = False) -> None:
def rerun(
self, name: str = "animation_phase", init: bool = True, clear_last_node: bool = False, notebook: bool = False
) -> None:
if self.nb_phases == 1:
self.phase_reruns[0].rerun(name, init, clear_last_node)
self.phase_reruns[0].rerun(name, init, clear_last_node, notebook)
return

if init:
rr.init(f"{name}_{0}", spawn=True)
rr.init(f"{name}_{0}", spawn=True if not notebook else False)

for phase_rerun in self.phase_reruns:
frame = 0
Expand Down
6 changes: 4 additions & 2 deletions pyorerun/phase_rerun.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,11 @@ def add_video(self, name, video_array: np.ndarray) -> None:

self.xp_data.add_data(Video(name=f"{self.name}/{name}", video_array=video_array))

def rerun(self, name: str = "animation_phase", init: bool = True, clear_last_node: bool = False) -> None:
def rerun(
self, name: str = "animation_phase", init: bool = True, clear_last_node: bool = False, notebook: bool = False
) -> None:
if init:
rr.init(f"{name}_{self.phase}", spawn=True)
rr.init(f"{name}_{self.phase}", spawn=True if not notebook else False)

frame = 0
rr.set_time_seconds("stable_time", self.t_span[frame])
Expand Down
15 changes: 9 additions & 6 deletions pyorerun/rrc3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def rrc3d(
video: str | tuple[str, ...] = None,
video_crop_mode: str = "from_c3d",
marker_trajectories: bool = False,
notebook: bool = False,
) -> None:
"""
Display a c3d file in rerun.
Expand All @@ -44,6 +45,8 @@ def rrc3d(
The mode to crop the video. If 'from_c3d', the video will be cropped to the same time span as the c3d file.
marker_trajectories: bool
If True, show the marker trajectories.
notebook: bool
If True, display the animation in the notebook.
"""

# Load a c3d file
Expand Down Expand Up @@ -90,7 +93,7 @@ def rrc3d(

if show_floor:
square_width = max_xy_coordinate_span_by_markers(pyomarkers)
phase_rerun.add_floor(square_width, height_offset=lowest_corner)
phase_rerun.add_floor(square_width, height_offset=lowest_corner - 0.0005)

if video is not None:
for i, vid in enumerate(video if isinstance(video, tuple) else [video]):
Expand All @@ -108,7 +111,7 @@ def rrc3d(
phase_reruns[-1].add_video(vid_name, vid)

multi_phase_rerun = MultiFrameRatePhaseRerun(phase_reruns)
multi_phase_rerun.rerun(filename)
multi_phase_rerun.rerun(filename, notebook=notebook)

if marker_trajectories:
# todo: find a better way to display curves but hacky way ok for now
Expand All @@ -119,10 +122,10 @@ def rrc3d(

def max_xy_coordinate_span_by_markers(pyomarkers: PyoMarkers) -> float:
"""Return the max span of the x and y coordinates of the markers."""
min_pyomarkers = np.min(np.min(pyomarkers.to_numpy(), axis=2), axis=1)
max_pyomarkers = np.max(np.max(pyomarkers.to_numpy(), axis=2), axis=1)
x_absolute_max = np.max(np.abs([min_pyomarkers[0], max_pyomarkers[0]]))
y_absolute_max = np.max(np.abs([min_pyomarkers[1], max_pyomarkers[1]]))
min_pyomarkers = np.nanmin(np.nanmin(pyomarkers.to_numpy(), axis=2), axis=1)
max_pyomarkers = np.nanmax(np.nanmax(pyomarkers.to_numpy(), axis=2), axis=1)
x_absolute_max = np.nanmax(np.abs([min_pyomarkers[0], max_pyomarkers[0]]))
y_absolute_max = np.nanmax(np.abs([min_pyomarkers[1], max_pyomarkers[1]]))

return np.max([x_absolute_max, y_absolute_max])

Expand Down
16 changes: 15 additions & 1 deletion pyorerun/xp_components/markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,30 @@


class MarkersXp(Markers, ExperimentalData):

_counter = 0 # counter for each instance of the class to change the color of the markers
_MARKERS_COLORS = [
np.array([255, 255, 255]),
np.array([245, 66, 53]),
np.array([232, 30, 99]),
np.array([33, 149, 245]),
np.array([76, 176, 79]),
np.array([103, 56, 182]),
]

def __init__(self, name, markers: PyoMarkers):

self.name = name + "/markers"
self.markers = markers
self.markers_numpy = markers.to_numpy()
self.markers_properties = MarkerProperties(
markers_names=markers.channel.values.tolist(),
radius=0.01,
color=np.array([255, 255, 255]),
color=MarkersXp._MARKERS_COLORS[MarkersXp._counter],
)

MarkersXp._counter = (MarkersXp._counter + 1) % len(MarkersXp._MARKERS_COLORS)

@property
def nb_markers(self):
return len(self.markers_names)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ classifiers = [
dependencies = [
# "ezc3d", # Not yet available on pypi, use `conda install -c conda-forge ezc3d`
"numpy",
"rerun-sdk=0.16.1",
"rerun-sdk==0.16.1",
"trimesh",
"pyomeca",
"tk",
Expand Down

0 comments on commit b83da89

Please sign in to comment.