Skip to content

Add LEAP .mat format reader#224

Merged
talmo merged 6 commits into
mainfrom
talmo/leap-format
Sep 1, 2025
Merged

Add LEAP .mat format reader#224
talmo merged 6 commits into
mainfrom
talmo/leap-format

Conversation

@talmo

@talmo talmo commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

Summary

This PR adds support for reading LEAP (LEAP Estimates Animal Pose) MATLAB .mat files. LEAP is a deep learning framework for animal pose estimation that exports tracking data in MATLAB format. This implementation provides a read-only interface to import LEAP tracking data into the SLEAP ecosystem.

Key Changes

Core Implementation

  • New I/O Module: Added sleap_io/io/leap.py with read_labels() function for parsing LEAP .mat files
  • Format Integration: Added load_leap() wrapper function and automatic .mat file format detection in load_file()
  • Package Exports: Updated sleap_io/__init__.py to expose load_leap function
  • Dependencies: Added pymatreader as an optional dependency in the [mat] extra group

Data Parsing Features

  • Skeleton Parsing: Extracts node names from nodes or joints fields, builds edge connections
  • Position Data: Handles multiple array shapes (2D/3D), supports alternative field names (positions, pose, posedata)
  • Video Path Resolution: Automatically infers video path from .mat file path when boxPath field is missing
  • Index Conversion: Properly converts MATLAB 1-based indexing to Python 0-based indexing
  • Custom Skeleton Support: Allows users to override the skeleton structure when loading

Example Usage

import sleap_io as sio

# Basic loading
labels = sio.load_leap("path/to/leap_data.mat")

# Auto-detection via load_file
labels = sio.load_file("path/to/leap_data.mat")

# Custom skeleton override
custom_skeleton = sio.Skeleton(
    nodes=["head", "tail"], 
    edges=[("head", "tail")]
)
labels = sio.load_leap("path/to/leap_data.mat", skeleton=custom_skeleton)

# Access the loaded data
print(f"Loaded {len(labels.labeled_frames)} frames")
print(f"Skeleton has {len(labels.skeleton.nodes)} nodes")
for frame in labels.labeled_frames:
    for instance in frame.instances:
        print(f"Frame {frame.frame_idx}: {len(instance.points)} points")

Installation

To use the LEAP reader, install sleap-io with the mat extra:

# Using pip
pip install sleap-io[mat]

# Using uv
uv pip install sleap-io[mat]

# From source with all extras
uv sync --all-extras

API Changes

New Public Functions

  • sleap_io.load_leap(filename: str, skeleton: Optional[Skeleton] = None) -> Labels
  • sleap_io.io.leap.read_labels(labels_path: str, skeleton: Optional[Skeleton] = None) -> Labels

Updated Functions

  • load_file() now automatically detects .mat files and routes them to the LEAP loader
  • Supported formats list updated to include "leap"

Testing

Comprehensive test coverage (94.0%) with 11 test cases covering:

  • Loading via multiple API entry points
  • Custom skeleton support
  • Missing dependency handling
  • Various position data formats (2D/3D arrays)
  • Alternative field names
  • Edge cases in skeleton parsing
  • Video path inference
# Run tests
uv run pytest tests/io/test_leap.py -v
# 11 passed

# Coverage report
uv run pytest tests/io/test_leap.py --cov=sleap_io.io.leap --cov-report=term
# Coverage: 94.0%

Design Decisions

  1. Read-only Implementation: LEAP files are typically generated by the LEAP software itself, so writing support was not implemented. Users should export from LEAP and import into SLEAP.

  2. Video Path Inference: When boxPath is missing from the .mat file, we infer the video path by replacing the .mat extension with .mp4 (most common video format).

  3. Flexible Field Names: LEAP exports may use different field names (positions, pose, posedata) depending on the version. The reader tries multiple alternatives.

  4. Array Shape Handling: LEAP may store positions as either (nodes, 2, frames) or (frames, nodes, 2). The reader automatically detects and transposes as needed.

  5. Optional Dependency: pymatreader is only required when using LEAP functionality, keeping the base installation lightweight.

File Format Notes

LEAP .mat files typically contain:

  • skeleton: Dictionary with nodes/joints (node names) and edges (connections)
  • positions/pose/posedata: Array of shape (nodes, 2, frames) or (frames, nodes, 2)
  • boxPath: Optional path to the associated video file
  • Additional metadata fields that are currently ignored

Future Considerations

  • Could add support for LEAP's confidence scores if they become available in the format
  • Might extend to handle multiple animals/tracks if LEAP adds multi-animal support
  • Could implement a writer if there's demand for round-trip conversion

🤖 Generated with Claude Code

This adds support for reading LEAP (LEAP Estimates Animal Pose) MATLAB files. LEAP is a deep learning framework for animal pose estimation that exports tracking data in .mat format.

Key changes:
- Add new `sleap_io/io/leap.py` module with `read_labels()` function
- Parse skeleton structure from LEAP data (nodes and edges)
- Convert LEAP position data to SLEAP instances
- Handle MATLAB 1-based to Python 0-based indexing conversion
- Add `load_leap()` wrapper in main.py with .mat file detection
- Export load_leap from package __init__.py
- Add pymatreader as optional dependency in [mat] group
- Add comprehensive tests and fixture for LEAP format
- Support custom skeleton override when loading

The implementation is read-only (no writer) as LEAP files are typically generated by the LEAP software itself.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 1, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.29630% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.19%. Comparing base (1513e82) to head (f5af8b4).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
sleap_io/io/leap.py 95.89% 0 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #224      +/-   ##
==========================================
+ Coverage   93.14%   93.19%   +0.04%     
==========================================
  Files          24       25       +1     
  Lines        5119     5199      +80     
  Branches     1378     1403      +25     
==========================================
+ Hits         4768     4845      +77     
  Misses        156      156              
- Partials      195      198       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

talmo and others added 2 commits August 31, 2025 20:20
- Test alternative position field names (pose, posedata)
- Test single frame (2D array) position data
- Test various skeleton parsing edge cases (numpy arrays, non-string names)
- Test video path inference when boxPath is missing
- Improve coverage from 76.9% to 94.0%

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Change default video extension inference to .mp4 (more common than .avi)
- Remove redundant .mp4 fallback logic
- Add test case for 1D numpy edge arrays that need reshaping to 2D
- This covers line 103 for complete branch coverage

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
talmo and others added 3 commits August 31, 2025 20:27
Split combined import statement into separate lines per ruff style guide.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add load_leap to formats.md API reference
- Include LEAP in supported formats list in index.md
- Add .mat (LEAP) to format detection tip in examples.md
- Document mat optional dependency for LEAP support

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
JABS format uses .h5 files, not .jabs. Corrected the documentation
to accurately reflect this.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@talmo talmo merged commit 1e0ec2b into main Sep 1, 2025
9 checks passed
@talmo talmo deleted the talmo/leap-format branch September 1, 2025 03:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[LEAP](https://github.com/talmolab/sleap/blob/369b77280790cc025d8d3af8cdf00c6ba48ef6f9/sleap/io/format/leap_matlab.py)

1 participant