To update movement to the latest version, see the update guide.
What's Changed
⚡️ Highlight 1: new example for scaling pose tracks to real-world coordinates
- Add example for scaling pose tracks to real-world coordinates by @HollyMorley in #827
This new gallery example by @HollyMorley walks through a complete workflow for converting pixel-based pose data to real-world units using a a known reference distance. It demonstrates pre-processing (confidence filtering, interpolation, smoothing), measuring a reference distance in napari, applying movement.transforms.scale(), and computing real-world body measurements and inter-limb distances.
Holly is working on a few more examples for our gallery, so keep an eye out for those in the coming releases!
⚡️ Highlight 2: Save and load RoIs via GeoJSON
Regions of interest (RoIs) can now be saved to and loaded from GeoJSON files using movement.roi.save_rois() and movement.roi.load_rois(). Each RoI is stored as a GeoJSON Feature containing its geometry and properties (name and RoI type), within a FeatureCollection. This makes it easy to persist ROI definitions across sessions and share them with collaborators.
from movement.roi import LineOfInterest, PolygonOfInterest, save_rois, load_rois
# Create 2 RoIs: a polygon and a line
square = PolygonOfInterest([(0, 0), (1, 0), (1, 1), (0, 1)], name="square")
diagonal = LineOfInterest([(0, 0), (1, 1)], name="diagonal")
# Save them to a GeoJSON file
save_rois([square, diagonal], "rois.geojson")
# Load RoIs from a GeoJSON file
rois = load_rois("rois.geojson")
[roi.name for roi in rois] # returns ["square", "diagonal"]⚡️ Highlight 3: derive bounding boxes from pose tracks
Thanks to @Edu92337, you can now compute bounding boxes from sets of keypoints (poses). The new movement.transforms.poses_to_bboxes() function finds the minimum and maximum coordinates across all keypoints for each individual at each time point. The resulting bounding boxes are represented by their centroid (center point position) and shape (width and height).
Suppose poses_ds is a movement poses dataset with a "position" variable containing keypoint coordinates.
from movement.transforms import poses_to_bboxes
# Compute bounding boxes with zero padding
bbox_centroid, bbox_shape = poses_to_bboxes(poses_ds["position"])
# Compute bounding boxes with 10 pixels of padding
bbox_centroid, bbox_shape = poses_to_bboxes(poses_ds["position"], padding=10)🐛 Bug fixes
- Fix MultiIndex error in compute_forward_displacement (#794) by @Tushar7012 in #799
📚 Documentation
- Updated conda installation instructions with pyqt6 by @niksirbi in #825
- Add "See Also" to
load_multiview_datasetdocstring by @niksirbi in #824 - Remove docstring types and use annotations for docs by @ParthChatupale in #727
- Draft roadmap for 2026 by @niksirbi in #809
- fix: correct loader param name, path assignment, and spelling in docs by @dhruv1955 in #857
🧹 Housekeeping
- Remove OSSS announcement banner by @adamltyson in #828
- Make hide_pooch_hash_logs internal by renaming to _hide_pooch_hash_logs by @Shreecharana24 in #842
- Use idiomatic xarray coordinate comparison in
test_load_posesby @Kayd-06 in #876 - [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci[bot] in #861
- Contributors-Readme-Action: Update contributors list by @github-actions[bot] in #856
- Bump actions/download-artifact from 7 to 8 by @dependabot[bot] in #859
- Contributors-Readme-Action: Update contributors list by @github-actions[bot] in #880
New Contributors
- @HollyMorley made their first contribution in #827
- @Shreecharana24 made their first contribution in #842
- @ParthChatupale made their first contribution in #727
- @dhruv1955 made their first contribution in #857
- @Kayd-06 made their first contribution in #876
Full Changelog: v0.14.0...v0.15.0