Skip to content

sleap_analysis

sleap.io.format.sleap_analysis

Adaptor to read and write analysis HDF5 files.

These contain location and track data, but lack other metadata included in a full SLEAP dataset file.

Note that this adaptor will use default track names and skeleton node names if these cannot be read from the HDF5 (some files have these, some don't).

To determine whether this adaptor can read a file, we check it's an HDF5 file with a track_occupancy dataset.

Classes:

Name Description
SleapAnalysisAdaptor

SleapAnalysisAdaptor

Bases: Adaptor

Methods:

Name Description
write

Writes analysis file for :py:class:Labels source_object.

Source code in sleap/io/format/sleap_analysis.py
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
class SleapAnalysisAdaptor(Adaptor):
    @property
    def handles(self):
        return SleapObjectType.labels

    @property
    def default_ext(self):
        return "h5"

    @property
    def all_exts(self):
        return ["h5", "hdf5"]

    @property
    def name(self):
        return "SLEAP Analysis HDF5"

    def can_read_file(self, file: FileHandle):
        if not self.does_match_ext(file.filename):
            return False
        if not file.is_hdf5:
            return False
        if "track_occupancy" not in file.file:
            return False
        return True

    def can_write_filename(self, filename: str):
        return self.does_match_ext(filename)

    def does_read(self) -> bool:
        return True

    def does_write(self) -> bool:
        return True

    @classmethod
    def read(
        cls,
        file: FileHandle,
        video: Union[Video, str],
        *args,
        **kwargs,
    ) -> Labels:
        connect_adj_nodes = False

        if video is None:
            raise ValueError("Cannot read analysis hdf5 if no video specified.")

        if not isinstance(video, Video):
            video = Video.from_filename(video)

        f = file.file
        tracks_matrix = f["tracks"][:].T

        # shape: frames * nodes * 2 * tracks
        frame_count, node_count, _, track_count = tracks_matrix.shape

        if "track_names" in f and len(f["track_names"]):
            track_names_list = f["track_names"][:].T
            tracks = [
                Track(name=track_name.decode()) for track_name in track_names_list
            ]
        else:
            tracks = [Track(name=f"track_{i}") for i in range(track_count)]

        if "node_names" in f:
            node_names_dset = f["node_names"][:].T
            node_names = [name.decode() for name in node_names_dset]
        else:
            node_names = [f"node {i}" for i in range(node_count)]

        skeleton = Skeleton()
        last_node_name = None
        for node_name in node_names:
            skeleton.add_node(node_name)
            if connect_adj_nodes and last_node_name:
                skeleton.add_edge(last_node_name, node_name)
            last_node_name = node_name

        frames = []
        for frame_idx in range(frame_count):
            instances = []
            for track_idx in range(track_count):
                points = tracks_matrix[frame_idx, ..., track_idx]
                if not np.all(np.isnan(points)):
                    point_scores = np.ones(len(points))
                    # make everything a PredictedInstance since the usual use
                    # case is to export predictions for analysis
                    from sleap.sleap_io_adaptors.instance_utils import (
                        predicted_instance_from_numpy_compat,
                    )

                    instances.append(
                        predicted_instance_from_numpy_compat(
                            points=points,
                            point_confidences=point_scores,
                            skeleton=skeleton,
                            track=tracks[track_idx],
                            instance_score=1,
                        )
                    )
            if instances:
                frames.append(
                    LabeledFrame(video=video, frame_idx=frame_idx, instances=instances)
                )

        labels = Labels(labeled_frames=frames)
        labels.update()
        return labels

    @classmethod
    def write(
        cls,
        filename: str,
        source_object: Labels,
        source_path: str = None,
        video: Video = None,
    ):
        """Writes analysis file for :py:class:`Labels` `source_object`.

        Args:
            filename: The filename for the output file.
            source_object: The :py:class:`Labels` from which to get data from.
            video: The :py:class:`Video` from which toget data from. If no `video` is
                specified, then the first video in `source_object` videos list will be
                used. If there are no :py:class:`Labeled Frame`s in the `video`,
                then no analysis file will be written.
        """
        from sleap.info.write_tracking_h5 import main as write_analysis

        write_analysis(
            labels=source_object,
            output_path=filename,
            labels_path=source_path,
            all_frames=True,
            video=video,
        )

write(filename, source_object, source_path=None, video=None) classmethod

Writes analysis file for :py:class:Labels source_object.

Parameters:

Name Type Description Default
filename str

The filename for the output file.

required
source_object Labels

The :py:class:Labels from which to get data from.

required
video Video

The :py:class:Video from which toget data from. If no video is specified, then the first video in source_object videos list will be used. If there are no :py:class:Labeled Frames in the video, then no analysis file will be written.

None
Source code in sleap/io/format/sleap_analysis.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
@classmethod
def write(
    cls,
    filename: str,
    source_object: Labels,
    source_path: str = None,
    video: Video = None,
):
    """Writes analysis file for :py:class:`Labels` `source_object`.

    Args:
        filename: The filename for the output file.
        source_object: The :py:class:`Labels` from which to get data from.
        video: The :py:class:`Video` from which toget data from. If no `video` is
            specified, then the first video in `source_object` videos list will be
            used. If there are no :py:class:`Labeled Frame`s in the `video`,
            then no analysis file will be written.
    """
    from sleap.info.write_tracking_h5 import main as write_analysis

    write_analysis(
        labels=source_object,
        output_path=filename,
        labels_path=source_path,
        all_frames=True,
        video=video,
    )