Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/jabs-core/src/jabs/core/abstract/pose_est.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ class PoseEstimation(ABC):
hash (str): Hash of the pose file.
static_objects (dict): Static objects in the pose file.
num_lixit_keypoints (int): Number of lixit keypoints (default 0).
external_identities (list[int] | None): Mapping to external identities.
external_identities (list[str] | None): External identity names, indexed by
JABS identity index. None when the pose file has no external identities.
"""

class KeypointIndex(enum.IntEnum):
Expand Down
13 changes: 13 additions & 0 deletions src/jabs/project/timeline_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,19 @@ def load(
def serialize(self) -> list[dict]:
"""Convert the internal IntervalTree to a JSON-serializable list of dictionaries.

Each annotation is serialized with the following fields, which are also the
fields :meth:`load` reads back:

- ``start``: first frame of the annotation (inclusive).
- ``end``: last frame of the annotation (inclusive).
- ``tag``: annotation tag.
- ``color``: color used to render the annotation.
- ``description``: optional description, omitted when unset.
- ``identity``: optional internal JABS identity index the annotation
applies to, omitted when the annotation is not tied to an identity.
The derived ``display_identity`` is not serialized: :meth:`load`
recomputes it from this index.

Returns:
list[dict]: A list containing a dictionary representation for each timeline annotation,
suitable for JSON serialization.
Expand Down
115 changes: 62 additions & 53 deletions src/jabs/project/video_labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,62 +160,71 @@ def as_dict(
project_metadata: dict | None = None,
video_metadata: dict | None = None,
) -> dict:
"""return dict representation of video labels
"""Return a dict representation of the video labels.

useful for JSON serialization and saving to disk
This is the on-disk format of a project's ``jabs/annotations/<video>.json``
file, so it is also what :meth:`load` consumes.

example return value:
{
"file": "filename.avi",
"num_frames": 100,
"external_identities: {
"jabs identity", 1234,
},
"metadata": {
"project": {},
"video": {},
}
"labels": {
"jabs identity": {
"behavior": [
{
"start": 25,
"end": 50,
"present": True
}
]
}
},
"unfragmented_labels": {
"jabs identity": {
"behavior": [
{
"start": 25,
"end": 50,
"present": True
}
]
}
},
annotations: [
{
"start": 10,
"end": 20,
"tag": "annotationTag",
"color": "#FF0000",
"description": "Description for the annotation"
},
{
"start": 30,
"end": 40,
"tag": "anotherTag",
"color": "#00FF00",
"description": "Another optional description",
"animal_id": 0 # optional, if the annotation is associated with an identity (internal JABS ID)
}
]
}
Args:
pose: PoseEstimation for this video. Its identity mask fragments the
blocks written to ``labels``, and its external identities are
included in the output when the pose file has them.
project_metadata: Optional project-level metadata to embed.
video_metadata: Optional video-level metadata to embed.

Returns:
Dict representation of the labels, with these keys:

- ``version``: serialization format version (``SERIALIZED_VERSION``).
- ``file``: name of the video these labels belong to.
- ``num_frames``: number of frames in the video.
- ``labels``: label blocks masked by ``pose.identity_mask()``, so a
block is split wherever the identity is missing from the pose file.
These are the blocks that correspond to usable training data.
- ``unfragmented_labels``: the same blocks without that mask, which is
what the user actually labeled. :meth:`load` prefers this key and
falls back to ``labels`` for files written before it existed.
- ``metadata``: the ``project_metadata`` and ``video_metadata``
arguments, each defaulting to an empty dict.
- ``external_identities``: only present when the pose file has external
identities. Maps the JABS identity index, as a string, to the
external identity.
- ``annotations``: only present when the video has timeline
annotations. See :meth:`TimelineAnnotations.serialize` for the fields
of an entry.

Both label dicts are keyed by identity (as a string), then by behavior
name, and hold the block lists produced by
:meth:`TrackLabels.get_blocks`.

Example:
Comment thread
gbeane marked this conversation as resolved.
{
"version": 1,
"file": "filename.avi",
"num_frames": 100,
"labels": {
"0": {
"behavior": [{"start": 25, "end": 40, "present": true}]
}
},
"unfragmented_labels": {
"0": {
"behavior": [{"start": 25, "end": 50, "present": true}]
}
},
"metadata": {"project": {}, "video": {}},
"external_identities": {"0": "mouse_a"},
"annotations": [
{
"start": 10,
"end": 20,
"tag": "annotationTag",
"color": "#FF0000",
"description": "optional description",
"identity": 0
}
]
}
"""
label_dict: dict[str, Any] = {
"version": SERIALIZED_VERSION,
Expand Down
Loading