Single camera biomechanics library
Project description
monomech
monomech is a notebook-first Python library for single-camera biomechanics. It helps you move from video or marker data into inspectable pose results, OpenSim-ready TRC files, inverse kinematics, inverse dynamics, and analysis tables without hiding the intermediate steps.
Full documentation: chags1313.github.io/monomech
Why Use It
- Start from a normal video or an existing TRC file.
- Export readable CSV and OpenSim-compatible TRC files.
- Run pose estimation, marker cleanup, scaling, inverse kinematics, and inverse dynamics as separate inspectable steps.
- Create OpenSim external loads from measured force data, arrays, carried loads, or estimated ground reaction forces.
- Export IK-driven OpenSim animations to a single portable GLB file.
- Review GLB meshes, markers, external-force arrows, IK traces, and ID traces in a Three.js HTML viewer.
- Keep OpenSim preflight checks on by default so NaNs and isolated gaps are fixed before IK and ID runs.
- Import the base package without installing heavy optional video or OpenSim dependencies.
Install
python -m pip install monomech
Choose extras only when you need them:
| Workflow | Install command |
|---|---|
| Video pose estimation | python -m pip install "monomech[pose]" |
| OpenSim Python bindings | python -m pip install "monomech[opensim]" |
| OpenSim animation export | python -m pip install "monomech[animation]" |
| Notebooks and plots | python -m pip install "monomech[notebook]" |
| Everything optional | python -m pip install "monomech[all]" |
monomech supports Python 3.10 through 3.12.
Quick Start: Video To TRC
from pathlib import Path
import monomech as mm
video_path = Path("data/subject01.mp4")
output_dir = Path("outputs/subject01")
output_dir.mkdir(parents=True, exist_ok=True)
pose3d_global = mm.estimate_pose(video_path, root_centered=False, floored=True)
pose3d_global = mm.gap_fill(mm.smooth(pose3d_global))
pose3d_global.to_csv(output_dir / "subject01_global.csv")
pose3d_global.to_trc(output_dir / "subject01_global.trc")
Or run the common video export path in one call:
run = mm.video_to_trc(video_path, output_dir=output_dir)
print(run.csv_paths)
print(run.trc_path)
Notebook-First Workflow
The high-level API is designed to read like the analysis steps in a notebook:
import monomech as mm
pose = mm.estimate_pose(
"data/curl.mp4",
root_centered=False,
floored=True,
)
pose = mm.smooth(pose, cutoff_hz=6.0)
pose = mm.gap_fill(pose, max_gap_frames=12)
pose.vis_2d(frame=100)
pose.vis_3d(frame=100)
scaled_model = mm.run_scaling(pose, model="pose", output_dir="outputs/curl/scale")
ik = mm.run_ik(scaled_model, output_dir="outputs/curl/ik")
dumbbell = mm.load(type="carried", body="hand_r", mass_kg=10.0)
grf = mm.estimate_grf(pose, body_mass_kg=82.0)
forces = mm.external_forces(loads=[dumbbell, *grf])
id_result = mm.run_id(
ik=ik,
external_forces=forces,
output_dir="outputs/curl/id",
)
ik.plot()
id_result.plot()
animation = mm.animate(
ik=ik,
id=id_result,
external_loads_path=id_result.metadata["external_loads_mot_path"],
output_dir="outputs/curl/visualizer",
mode="balanced",
)
animation.show()
Carried and constant loads created without start_time and end_time are applied over the full IK trial. Use explicit times only for loads that turn on and off during the movement.
For batch scripts, use the one-call aliases:
result = mm.video_pipeline(
"data/curl.mp4",
model_path=mm.get_builtin_osim_model("pose"),
output_dir="outputs/curl",
)
result.display()
Full Pipeline: Video To Inverse Dynamics
import monomech as mm
result = mm.video_to_inverse_dynamics(
"data/subject01.mp4",
model_path="models/subject01_scaled.osim",
output_dir="outputs/subject01",
body_mass_kg=75.0,
)
print(result.trc_path)
print(result.ik.path)
print(result.id.path)
print(result.visualizer.html_path)
result.display()
If you already have marker data in a TRC file, start at OpenSim:
result = mm.trc_to_inverse_dynamics(
"outputs/subject01/subject01.trc",
model_path="models/subject01_scaled.osim",
output_dir="outputs/subject01/opensim",
external_forces=None,
)
print(result.ik.path)
print(result.id.path)
Export the IK and ID run to one portable animation file:
animation = mm.save_opensim_animation(
osim_path="models/subject01_scaled.osim",
mot_path=result.ik.path,
id_path=result.id.path,
out_glb_path="outputs/subject01/animation/subject01_ik_id.glb",
stride=2,
decimate_target_reduction=0.35,
)
print(animation.glb_path)
Create a notebook-friendly Three.js dashboard with the animated model, marker fallback, force arrows, IK plots, and ID plots:
viewer = mm.animate(
ik=result.ik,
id=result.id,
model="models/subject01_scaled.osim",
output_dir="outputs/subject01/visualizer",
external_loads_path=result.id.metadata["external_loads_mot_path"],
)
viewer.show()
The dashboard is a standalone HTML file, so it works in notebooks, local browsers, and GitHub Pages. mm.animate() writes the GLB next to the HTML and references that file by default, which keeps notebooks fast. Pass embed_glb=True only when you need one self-contained HTML file. The online visualizer starts empty and includes an Upload GLB control, which lets a reader drag in their own exported model without sending the file anywhere.
For a carried object plus estimated ground-reaction forces:
dumbbell = mm.external.carried_load(
mass_kg=12.5,
applied_to_body="radius_r",
point=(0.0, -0.2, 0.0),
name="right_dumbbell",
)
result = mm.video_to_inverse_dynamics(
"data/curl.mp4",
model_path=mm.get_builtin_osim_model("pose"),
output_dir="outputs/curl",
body_mass_kg=82.0,
external_forces=mm.external.with_estimated_grf(dumbbell),
)
The GitHub Pages site includes an online GLB visualizer for quick review: chags1313.github.io/monomech/visualizer/.
Geometry note: the default full-body geometry is packaged with monomech, so most users do not need to pass geom_dir. Pass geom_dir only when using a different OpenSim model or custom mesh folder. If your model came from a macOS-created zip, avoid the __MACOSX folder because it usually contains only tiny ._*.vtp metadata files, not usable meshes.
For measured force plates, build an external-load spec from your force table:
right_grf = mm.external.from_csv(
"data/right_force_plate.csv",
applied_to_body="calcn_r",
force_columns=("Fx", "Fy", "Fz"),
point_columns=("Px", "Py", "Pz"),
torque_columns=("Mx", "My", "Mz"),
time_column="time",
name="right_grf",
)
OpenSim Reliability Defaults
OpenSim is strict about missing or non-finite values. The OpenSim helpers preflight inputs by default:
- TRC marker gaps are interpolated before scale and IK.
- IK coordinate NaNs are interpolated before inverse dynamics.
- External-load data is resampled to IK time and non-finite force values are filled with zero.
- Preflight reports and generated paths are stored in result metadata.
print(ik.metadata["preflight"])
print(id_result.metadata["coordinate_preflight"])
Example Notebooks
The examples/ folder includes ready-to-edit notebooks:
video_to_trc_quickstart.ipynbfor video to CSV/TRC.marker_trc_cleanup.ipynbfor TRC inspection, gap filling, and smoothing.opensim_scale_ik_template.ipynbfor OpenSim scale and IK setup.video_to_inverse_dynamics_pipeline.ipynbfor video to IK/ID with external loads.run_video_smoke.pyfor repeatable command-line checks on a real video.
Documentation
- Getting started
- Example notebooks
- API reference
- Online GLB visualizer
- External loads and forces
- OpenSim scale, IK, and ID
- OpenSim animation export
- Full video-to-ID pipeline
- Outputs and files
Development
python -m pip install -e ".[dev]"
python -m pytest tests
mkdocs build --strict
python -m build
Run a real video smoke test:
python examples/run_video_smoke.py "path/to/video.mp4" --output-dir outputs/smoke
Add --opensim when OpenSim-compatible bindings are installed.
Publishing
GitHub Actions builds distributions on every push to main. PyPI publishing goes directly to PyPI through trusted publishing and is triggered by version tags such as:
git tag v0.15.16
git push origin v0.15.16
The publish workflow can also be run manually from GitHub Actions with the publish input set to true.
License
See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file monomech-0.15.16.tar.gz.
File metadata
- Download URL: monomech-0.15.16.tar.gz
- Upload date:
- Size: 29.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
05b4d2d7def26dbd80134b0396b78884b5c12f6617af9520277dfa1b5833cbbd
|
|
| MD5 |
f77939cb2ec30bfacf3f8149abf39b72
|
|
| BLAKE2b-256 |
59991902d826ac2b94ab5a6f8b7f0d16d196d8177d11d96db713bb45c714ff61
|
Provenance
The following attestation bundles were made for monomech-0.15.16.tar.gz:
Publisher:
publish.yml on chags1313/monomech
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
monomech-0.15.16.tar.gz -
Subject digest:
05b4d2d7def26dbd80134b0396b78884b5c12f6617af9520277dfa1b5833cbbd - Sigstore transparency entry: 1395067654
- Sigstore integration time:
-
Permalink:
chags1313/monomech@8b229b5b795d5b61607fdec0956541d85a030b26 -
Branch / Tag:
refs/tags/v0.15.16 - Owner: https://github.com/chags1313
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8b229b5b795d5b61607fdec0956541d85a030b26 -
Trigger Event:
push
-
Statement type:
File details
Details for the file monomech-0.15.16-py3-none-any.whl.
File metadata
- Download URL: monomech-0.15.16-py3-none-any.whl
- Upload date:
- Size: 30.2 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61e6603d0d375c0ba7023aa18f95f54ea7eb21d2f9f97a31bed418757946e514
|
|
| MD5 |
2bc0ccb0aa76f612c4a78182ec3f8f2f
|
|
| BLAKE2b-256 |
d643a8657ba2de366b18a25403761cec97a435a8daffd10f86b34d2228d3c3ad
|
Provenance
The following attestation bundles were made for monomech-0.15.16-py3-none-any.whl:
Publisher:
publish.yml on chags1313/monomech
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
monomech-0.15.16-py3-none-any.whl -
Subject digest:
61e6603d0d375c0ba7023aa18f95f54ea7eb21d2f9f97a31bed418757946e514 - Sigstore transparency entry: 1395067658
- Sigstore integration time:
-
Permalink:
chags1313/monomech@8b229b5b795d5b61607fdec0956541d85a030b26 -
Branch / Tag:
refs/tags/v0.15.16 - Owner: https://github.com/chags1313
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8b229b5b795d5b61607fdec0956541d85a030b26 -
Trigger Event:
push
-
Statement type: