DeepLabCut Format (.csv)¶
Load annotations from DeepLabCut, a popular markerless pose estimation tool. sleap-io reads DLC annotations from the project's CSV files (optionally augmented by a config.yaml); it does not read DLC .h5 outputs.
load_dlc reads a single DLC annotation CSV. When a project config.yaml is
available — either passed explicitly via config= or auto-discovered by walking
up from the CSV — the following extra metadata is imported:
- Skeleton edges from the config
skeleton:list. Edges that reference bodyparts not present in the labeled data are dropped with a warning. - Source videos: each
labeled-data/<video>/image folder is linked back to its original video file (from the configvideo_sets) viaVideo.source_video, matched by filename stem. The link is best-effort and left unset on a stem mismatch or missing file.
Pass config=False to disable config use entirely and reproduce the legacy,
config-free output.
Cropping (video_sets[...].crop)
DeepLabCut's video_sets[...].crop is a virtual read-time crop (an ROI
that DLC's video reader slices out of each full frame). The images under
labeled-data/<video>/ are the cropped region and the labels are stored in
cropped-frame coordinates, while the linked source_video is the
original, uncropped video. sleap-io now imports this crop:
- The crop rect is parsed from
video_sets(DLC stores it width-range-first asx1, x2, y1, y2; sleap-io reorders it to its(x1, y1, x2, y2)convention,x2/y2exclusive) and recorded underlabels.provenance["dlc_crops"], keyed by source-video path. This record persists through an SLP round-trip. - Labels are left verbatim in cropped-frame coordinates on the uncropped
labeled-dataImageVideo— no offset is applied (and the already-cropped images are never cropped again). To map a label into the full source frame, useVideo.to_source_coordswith the recorded rect (it adds the crop origin(x1, y1)). - When the source video file is available,
source_videois set to aVideo.from_cropview of it, sosource_video.crop_rect/to_source_coordswork in memory (this view's crop is in-memory only; the persistent record isprovenance["dlc_crops"]). When the source is absent,source_videois a closedVideoas before. - Identity crops at the origin (
0, W, 0, H— the DLC no-cropping default) record no crop and leave the link exact.
See the virtual cropping guide for the crop conventions.
import sleap_io as sio
# Single CSV; auto-discovers config.yaml for edges + source-video links.
labels = sio.load_dlc("project/labeled-data/vid1/CollectedData_Scorer.csv")
# Strict legacy output (no edges/links), even inside a project.
labels = sio.load_dlc("project/labeled-data/vid1/CollectedData_Scorer.csv", config=False)
Unannotated rows become empty frames
Every CSV row (image) is loaded as a LabeledFrame, including rows with no
labeled bodyparts (all-NaN) — these become empty LabeledFrames, so the
frame count matches the input. Drop them with
Labels.clean() if you don't want them.
sleap_io.io.main.load_dlc(filename, video_search_paths=None, config=None, **kwargs)
¶
Read DeepLabCut annotations from a CSV file and return a Labels object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
str
|
Path to DLC CSV file with annotations. |
required |
video_search_paths
|
list[str | Path] | None
|
Optional list of paths to search for video files. |
None
|
config
|
str | Path | bool | None
|
Path to a DLC project |
None
|
**kwargs
|
Additional arguments passed to DLC loader. |
required |
Returns:
| Type | Description |
|---|---|
Labels
|
Parsed labels as a |
Source code in sleap_io/io/main.py
def load_dlc(
filename: str,
video_search_paths: list[str | Path] | None = None,
config: str | Path | bool | None = None,
**kwargs,
) -> Labels:
"""Read DeepLabCut annotations from a CSV file and return a `Labels` object.
Args:
filename: Path to DLC CSV file with annotations.
video_search_paths: Optional list of paths to search for video files.
config: Path to a DLC project ``config.yaml``. When provided (or
auto-discovered), skeleton edges and source-video links are imported.
Pass `None` (the default) to auto-discover ``config.yaml`` by walking
up from the CSV, an explicit path to force a specific config, or
`False` to disable config use entirely (strict legacy output).
**kwargs: Additional arguments passed to DLC loader.
Returns:
Parsed labels as a `Labels` instance.
"""
from sleap_io.io import dlc
return dlc.load_dlc(
filename, video_search_paths=video_search_paths, config=config, **kwargs
)
Loading a whole DLC project¶
load_dlc_project loads every labeled-data/<video>/ folder in a project at
once and merges them into a single Labels that shares one Skeleton (with
edges) and one set of Tracks, recording provenance under the dlc_project,
dlc_scorer, and dlc_task keys. A project directory or its config.yaml can
also be passed to load_file, which routes it here automatically.
labels = sio.load_dlc_project("path/to/dlc_project") # dir or config.yaml
labels = sio.load_file("path/to/dlc_project") # auto-routed
sleap_io.io.main.load_dlc_project(config, video_search_paths=None, **kwargs)
¶
Read an entire DeepLabCut project from its config.yaml.
All labeled-data/<video>/ folders are loaded and merged into a single
Labels sharing one Skeleton (with edges from the config) and one set of
Tracks, with each video linked back to its original via
Video.source_video.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
str | Path
|
Path to a DLC project |
required |
video_search_paths
|
list[str | Path] | None
|
Optional list of paths to search for video files. |
None
|
**kwargs
|
Additional arguments passed to the DLC project loader. |
required |
Returns:
| Type | Description |
|---|---|
Labels
|
Parsed labels as a |
Source code in sleap_io/io/main.py
def load_dlc_project(
config: str | Path,
video_search_paths: list[str | Path] | None = None,
**kwargs,
) -> Labels:
"""Read an entire DeepLabCut project from its ``config.yaml``.
All ``labeled-data/<video>/`` folders are loaded and merged into a single
`Labels` sharing one `Skeleton` (with edges from the config) and one set of
`Track`s, with each video linked back to its original via
`Video.source_video`.
Args:
config: Path to a DLC project ``config.yaml`` (or the project directory
containing one).
video_search_paths: Optional list of paths to search for video files.
**kwargs: Additional arguments passed to the DLC project loader.
Returns:
Parsed labels as a `Labels` instance.
"""
from sleap_io.io import dlc
return dlc.load_dlc_project(config, video_search_paths=video_search_paths, **kwargs)
Importing train/test splits¶
load_dlc_splits recovers the train/test splits created by DLC's
create_training_dataset, reading the positional indices stored in the project's
Documentation_data-*.pickle and reconstructing DLC's globally, lexicographically
sorted merge of all per-video annotations. It returns a
LabelsSet with "train" and "test" keys.
Splits require the labeled images to be present on disk. For non-zero-padded
image filenames a warning is emitted, since DLC's lexicographic ordering (e.g.
img10 < img2) differs from numeric ordering and could otherwise cause silent
train/test mis-assignment. If a project has multiple training fractions or
shuffles, pass train_fraction= and/or shuffle= to disambiguate.
splits = sio.load_dlc_splits("path/to/dlc_project", shuffle=1)
train, test = splits["train"], splits["test"]
sleap_io.io.main.load_dlc_splits(config, shuffle=None, train_fraction=None, iteration=None, video_search_paths=None)
¶
Read DeepLabCut train/test splits from a project's Documentation pickle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
str | Path
|
Path to a DLC project |
required |
shuffle
|
int | None
|
The shuffle index to load. Required if more than one exists. |
None
|
train_fraction
|
float | None
|
The training fraction to load (e.g. |
None
|
iteration
|
int | None
|
The project iteration. Defaults to |
None
|
video_search_paths
|
list[str | Path] | None
|
Optional list of paths to search for video files. |
None
|
Returns:
| Type | Description |
|---|---|
LabelsSet
|
A |
Source code in sleap_io/io/main.py
def load_dlc_splits(
config: str | Path,
shuffle: int | None = None,
train_fraction: float | None = None,
iteration: int | None = None,
video_search_paths: list[str | Path] | None = None,
) -> "LabelsSet":
"""Read DeepLabCut train/test splits from a project's Documentation pickle.
Args:
config: Path to a DLC project ``config.yaml`` (or the project directory).
shuffle: The shuffle index to load. Required if more than one exists.
train_fraction: The training fraction to load (e.g. ``0.95``). Required
if more than one exists.
iteration: The project iteration. Defaults to ``cfg['iteration']``.
video_search_paths: Optional list of paths to search for video files.
Returns:
A `LabelsSet` with ``"train"`` and ``"test"`` keys.
"""
from sleap_io.io import dlc
return dlc.load_dlc_splits(
config,
shuffle=shuffle,
train_fraction=train_fraction,
iteration=iteration,
video_search_paths=video_search_paths,
)