Skip to content

Analysis HDF5 Format

The SLEAP Analysis HDF5 format is a portable format for exporting pose tracking predictions as dense numpy arrays. This format is designed for easy loading in MATLAB and Python analysis pipelines.

Shape change in v0.7.0

The frame dimension of every array in this file is now n_frames == len(video), not last_labeled_frame + 1 as in v0.6.x. Frames past the last labeled frame are filled with NaN for pose coordinates and False for track occupancy. Code that sized downstream arrays from the exported shape (e.g. array.shape[0] == last_labeled_frame + 1) may need to be updated to account for the new longer arrays (PR #368).

Overview

Analysis HDF5 files contain:

  • Pose coordinates as dense 4D arrays
  • Confidence scores for points, instances, and tracking
  • Track occupancy indicating which frames have valid data
  • Metadata for skeleton, video, and format information

HDF5 Layout

analysis.h5
├── tracks                    # Dataset: Pose coordinates (4D array)
│   ├── @dims                 # Attribute: Dimension names (JSON list)
│   └── ...                   # Coordinates with NaN for missing data
├── track_occupancy           # Dataset: Track presence per frame (2D bool)
│   └── @dims                 # Attribute: Dimension names
├── point_scores              # Dataset: Per-point confidence (3D array)
│   └── @dims
├── instance_scores           # Dataset: Per-instance confidence (2D array)
│   └── @dims
├── tracking_scores           # Dataset: Tracking confidence (2D array)
│   └── @dims
├── track_names               # Dataset: Track name strings
├── node_names                # Dataset: Node/keypoint name strings
├── edge_names                # Dataset: Skeleton edge name pairs
├── edge_inds                 # Dataset: Skeleton edge node-index pairs
├── video_path                # Dataset: Source video path
├── video_ind                 # Dataset: Source video index
├── labels_path               # Dataset: Original labels file path
├── provenance                # Dataset: Source file provenance (JSON)
├── @format                   # Attribute: "analysis" (format identifier)
├── @sleap_io_version         # Attribute: sleap-io package version
├── @preset                   # Attribute: Axis ordering preset
├── @skeleton_name            # Attribute: Skeleton name (save_metadata=True)
├── @skeleton_symmetries      # Attribute: Symmetry pairs, JSON (save_metadata=True)
└── @video_backend_metadata   # Attribute: Video backend metadata, JSON (save_metadata=True)

provenance, labels_path, and the skeleton edges are datasets, not attributes

Read them as f["provenance"], f["labels_path"], f["edge_names"], and f["edge_inds"]not f.attrs[...]. There is no skeleton_edges attribute.

Axis Ordering Presets

The preset parameter controls the axis ordering of arrays. This is critical for compatibility with different analysis environments.

matlab Preset (Default)

Optimized for MATLAB's column-major memory layout. Compatible with SLEAP's original analysis export.

Dataset Shape Dimensions
tracks (n_tracks, 2, n_nodes, n_frames) ["track", "xy", "node", "frame"]
track_occupancy (n_frames, n_tracks) ["frame", "track"]
point_scores (n_tracks, n_nodes, n_frames) ["track", "node", "frame"]
instance_scores (n_tracks, n_frames) ["track", "frame"]
tracking_scores (n_tracks, n_frames) ["track", "frame"]

In v0.7.0, n_frames == len(video) — the full video duration, not the number of labeled frames.

MATLAB usage:

data = h5read('analysis.h5', '/tracks');
% Access frame 10, track 1, node 3: data(1, :, 3, 10) -> [x, y]

occupancy = h5read('analysis.h5', '/track_occupancy');
% Check if track 1 is present in frame 10: occupancy(10, 1)

standard Preset

Python-native ordering with frame as the first axis for intuitive indexing.

Dataset Shape Dimensions
tracks (n_frames, n_tracks, n_nodes, 2) ["frame", "track", "node", "xy"]
track_occupancy (n_frames, n_tracks) ["frame", "track"]
point_scores (n_frames, n_tracks, n_nodes) ["frame", "track", "node"]
instance_scores (n_frames, n_tracks) ["frame", "track"]
tracking_scores (n_frames, n_tracks) ["frame", "track"]

Python usage:

import h5py

with h5py.File('analysis.h5', 'r') as f:
    tracks = f['tracks'][:]
    # Access frame 10, track 1, node 3: tracks[10, 1, 3, :] -> [x, y]

    occupancy = f['track_occupancy'][:]
    # Check if track 1 is present in frame 10: occupancy[10, 1]

track_occupancy ordering

The track_occupancy dataset always has shape (n_frames, n_tracks) regardless of preset. This matches SLEAP's original behavior where track_occupancy was stored with frames first.

Datasets

tracks

Dense array of pose coordinates. Missing data is represented as NaN.

Property Value
Dtype float64
Compression gzip
Shape Depends on preset (see above)

Coordinate system: Pixel center at (0, 0). X increases rightward, Y increases downward.

track_occupancy

Array indicating which tracks have valid data per frame (values are 0/1).

Property Value
Dtype uint8
Shape (n_frames, n_tracks)

point_scores

Per-point confidence scores from the pose estimation model.

Property Value
Dtype float64
Range 0.0 to 1.0
Missing NaN

instance_scores

Per-instance confidence scores (average of point scores).

Property Value
Dtype float64
Range 0.0 to 1.0
Missing NaN

tracking_scores

Per-instance tracking confidence (from identity tracking models).

Property Value
Dtype float64
Range 0.0 to 1.0
Missing/default NaN (including user instances without a tracking score)

track_names

Array of track name strings.

Property Value
Dtype Variable-length string
Length n_tracks

Projects without track assignments

When the source project has no Track assignments, n_tracks is sized to the largest number of instances found in any single frame, so every instance is exported (a multi-animal project is no longer collapsed to one instance per frame). Synthetic names track_0 ... track_{n-1} are used. The per-slot assignment is arbitrary across frames since no track identity exists — consistent with to_numpy(untracked=True).

node_names

Array of skeleton node/keypoint names.

Property Value
Dtype Variable-length string
Length n_nodes

video_path

Source video file path.

Property Value
Dtype String

Attributes

File-Level Attributes

Attribute Type Description
format string Always "analysis"
sleap_io_version string sleap-io package version that wrote the file
preset string Axis ordering: "matlab", "standard", or "custom"
skeleton_name string Skeleton name (written only when save_metadata=True, the default)
skeleton_symmetries JSON string Symmetry pairs as [["left", "right"], ...] (written only when save_metadata=True)
video_backend_metadata JSON string Per-video backend metadata (written only when save_metadata=True)

provenance, labels_path, and the skeleton edges (edge_names, edge_inds) are stored as datasets, not attributes — see the HDF5 Layout above.

sleap_io_version is a provenance stamp, not a format gate

This attribute records the sleap-io package version that wrote the file (the value of sleap_io.__version__ at save time). It is not a file-format version — the Analysis HDF5 layout itself is not versioned. Downstream tools should not use this value as a semver gate for structural changes; check the presence of specific datasets or @preset instead.

Dataset Attributes

Each dataset has a dims attribute containing a JSON-encoded list of dimension names:

>>> f['tracks'].attrs['dims']
b'["track", "xy", "node", "frame"]'  # matlab preset

Track Filtering

The min_occupancy parameter filters tracks with low occupancy:

import sleap_io as sio

# Keep all non-empty tracks (default)
sio.save_analysis_h5(labels, "all.h5", min_occupancy=0.0)

# Keep only tracks present in >50% of frames
sio.save_analysis_h5(labels, "filtered.h5", min_occupancy=0.5)

Occupancy is calculated as: frames_with_track / total_frames, where total_frames == len(video) in v0.7.0. If your dataset has many unlabeled frames at the end of the video, the occupancy denominator will be larger than in v0.6.x and min_occupancy thresholds may need to be lowered correspondingly.

Custom Axis Ordering

For advanced use cases, you can specify explicit dimension positions:

sio.save_analysis_h5(
    labels,
    "custom.h5",
    frame_dim=0,   # Frame is first axis
    track_dim=1,   # Track is second axis
    node_dim=2,    # Node is third axis
    xy_dim=3,      # XY is fourth axis
)

Mutually exclusive

You cannot use both preset and explicit dimension parameters.

Reading Analysis HDF5

With sleap-io

import sleap_io as sio

# Load as Labels object
labels = sio.load_analysis_h5("analysis.h5")

# Access as numpy arrays
poses = labels.numpy()

With h5py

import h5py
import json

with h5py.File('analysis.h5', 'r') as f:
    # Read data
    tracks = f['tracks'][:]
    occupancy = f['track_occupancy'][:]

    # Read metadata
    preset = f.attrs['preset']
    dims = json.loads(f['tracks'].attrs['dims'])
    node_names = [n.decode() for n in f['node_names'][:]]
    track_names = [t.decode() for t in f['track_names'][:]]

    print(f"Preset: {preset}")
    print(f"Dimensions: {dims}")
    print(f"Nodes: {node_names}")
    print(f"Tracks: {track_names}")

With MATLAB

% Read data
tracks = h5read('analysis.h5', '/tracks');
occupancy = h5read('analysis.h5', '/track_occupancy');

% Read metadata
preset = h5readatt('analysis.h5', '/', 'preset');
node_names = h5read('analysis.h5', '/node_names');
track_names = h5read('analysis.h5', '/track_names');

% Get xy coordinates for frame 100, track 1, all nodes
frame_idx = 100;
track_idx = 1;
xy = squeeze(tracks(track_idx, :, :, frame_idx));  % Shape: (2, n_nodes)

CLI Usage

# Export to Analysis HDF5 (default matlab preset)
sio export predictions.slp -o analysis.h5

# Use standard (Python-native) ordering
sio export predictions.slp -o analysis.h5 --h5-dim-order standard

# Filter tracks by occupancy
sio export predictions.slp -o filtered.h5 --min-occupancy 0.5

API Reference

sleap_io.io.main.load_analysis_h5(filename, video=None, **kwargs)

Load SLEAP Analysis HDF5 file.

Parameters:

Name Type Description Default
filename str

Path to Analysis HDF5 file.

required
video Video | str | None

Video to associate with data. If None, uses video_path stored in the file. Can be a Video object or path string.

None
**kwargs

Additional loader keyword arguments forwarded by load_file (e.g. open_videos, lazy). They are accepted but ignored; this format does not use them.

required

Returns:

Type Description
Labels

Labels object with loaded pose data.

Notes

If the file contains extended metadata (skeleton symmetries, video backend metadata, etc.), it will be used to reconstruct the full Labels context.

See Also

save_analysis_h5: Save Labels to Analysis HDF5 file.

Source code in sleap_io/io/main.py
def load_analysis_h5(
    filename: str,
    video: "Video | str | None" = None,
    **kwargs,
) -> Labels:
    """Load SLEAP Analysis HDF5 file.

    Args:
        filename: Path to Analysis HDF5 file.
        video: Video to associate with data. If None, uses video_path stored
            in the file. Can be a Video object or path string.
        **kwargs: Additional loader keyword arguments forwarded by `load_file`
            (e.g. ``open_videos``, ``lazy``). They are accepted but ignored; this
            format does not use them.

    Returns:
        Labels object with loaded pose data.

    Notes:
        If the file contains extended metadata (skeleton symmetries, video
        backend metadata, etc.), it will be used to reconstruct the full
        Labels context.

    See Also:
        save_analysis_h5: Save Labels to Analysis HDF5 file.
    """
    from sleap_io.io import analysis_h5

    return analysis_h5.read_labels(filename, video=video)

sleap_io.io.main.save_analysis_h5(labels, filename, *, video=None, labels_path=None, all_frames=True, min_occupancy=0.0, preset=None, frame_dim=None, track_dim=None, node_dim=None, xy_dim=None, save_metadata=True)

Save Labels to SLEAP Analysis HDF5 file.

Parameters:

Name Type Description Default
labels Labels

Labels to export.

required
filename str

Output file path.

required
video Video | int | None

Video to export. If None, uses first video. Can be a Video object or an integer index.

None
labels_path str | None

Source labels path (stored as metadata).

None
all_frames bool

Include all frames from 0 to the end of the video (falling back to the last labeled frame when the video length is unknown). Default True.

True
min_occupancy float

Minimum track occupancy ratio (0-1) to keep. 0 = keep all non-empty tracks (SLEAP default). 0.5 = keep tracks with >50% occupancy.

0.0
preset str | None

Axis ordering preset. Options: - "matlab" (default): SLEAP-compatible ordering for MATLAB. tracks shape: (n_tracks, 2, n_nodes, n_frames) - "standard": Intuitive Python ordering. tracks shape: (n_frames, n_tracks, n_nodes, 2) Mutually exclusive with explicit dimension parameters.

None
frame_dim int | None

Position of the frame dimension (0-3).

None
track_dim int | None

Position of the track dimension (0-3).

None
node_dim int | None

Position of the node dimension (0-3).

None
xy_dim int | None

Position of the xy dimension (0-3).

None
save_metadata bool

Store extended metadata for full round-trip. Default True.

True
See Also

load_analysis_h5: Load Labels from Analysis HDF5 file.

Source code in sleap_io/io/main.py
def save_analysis_h5(
    labels: Labels,
    filename: str,
    *,
    video: "Video | int | None" = None,
    labels_path: str | None = None,
    all_frames: bool = True,
    min_occupancy: float = 0.0,
    preset: str | None = None,
    frame_dim: int | None = None,
    track_dim: int | None = None,
    node_dim: int | None = None,
    xy_dim: int | None = None,
    save_metadata: bool = True,
) -> None:
    """Save Labels to SLEAP Analysis HDF5 file.

    Args:
        labels: Labels to export.
        filename: Output file path.
        video: Video to export. If None, uses first video. Can be a Video
            object or an integer index.
        labels_path: Source labels path (stored as metadata).
        all_frames: Include all frames from 0 to the end of the video (falling back
            to the last labeled frame when the video length is unknown).
            Default True.
        min_occupancy: Minimum track occupancy ratio (0-1) to keep.
            0 = keep all non-empty tracks (SLEAP default).
            0.5 = keep tracks with >50% occupancy.
        preset: Axis ordering preset. Options:
            - "matlab" (default): SLEAP-compatible ordering for MATLAB.
              tracks shape: (n_tracks, 2, n_nodes, n_frames)
            - "standard": Intuitive Python ordering.
              tracks shape: (n_frames, n_tracks, n_nodes, 2)
            Mutually exclusive with explicit dimension parameters.
        frame_dim: Position of the frame dimension (0-3).
        track_dim: Position of the track dimension (0-3).
        node_dim: Position of the node dimension (0-3).
        xy_dim: Position of the xy dimension (0-3).
        save_metadata: Store extended metadata for full round-trip.
            Default True.

    See Also:
        load_analysis_h5: Load Labels from Analysis HDF5 file.
    """
    from sleap_io.io import analysis_h5

    analysis_h5.write_labels(
        labels,
        filename,
        video=video,
        labels_path=labels_path,
        all_frames=all_frames,
        min_occupancy=min_occupancy,
        preset=preset,
        frame_dim=frame_dim,
        track_dim=track_dim,
        node_dim=node_dim,
        xy_dim=xy_dim,
        save_metadata=save_metadata,
    )