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:
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 |
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,
)