What's Changed
* Update backend filename when backend isn't created on replace by talmo in https://github.com/talmolab/sleap-io/pull/127
* Update labels videos list on replace by talmo in https://github.com/talmolab/sleap-io/pull/128
* Add video writing by talmo in https://github.com/talmolab/sleap-io/pull/129
- Add `sio.VideoWriter`: basic `imageio-ffmpeg` video writer with sensible H264 presets. This can be used as a context manager:
py
with sio.VideoWriter("video.mp4") as vw:
for frame in video:
vw(frame)
- Add `sio.save_video`: high-level video writing. This can be used to quickly write a set of frames or even a whole `Video` for easy (if inefficient) re-encoding:
py
bad_video = sio.load_video("unseekable.avi")
sio.save_video(bad_video, "seekable.mp4")
- Added `IndexError` in `VideoBackend` to enable sequence protocol for iteration over `Video`s:
py
for frame in video:
pass
- Refactored `sio.io.video` to `sio.io.video_reading`.
* Fixes to get JABS export to work with new data by talmo in https://github.com/talmolab/sleap-io/pull/132
* Make skeleton nodes mutable by talmo in https://github.com/talmolab/sleap-io/pull/135
* Add skeleton manipulation utilities by talmo in https://github.com/talmolab/sleap-io/pull/136
- `Skeleton`
- `__contains__(node: NodeOrIndex)`: Returns `True` if a node exists in the skeleton.
- `rebuild_cache()`: Method allowing explicit regeneration of the caching attributes from the nodes.
- Caching attributes are now named `_name_to_node_cache` and `_node_to_ind_cache`, better reflecting the mapping directionality.
- `require_node(node: NodeOrIndex, add_missing: bool = True)`: Returns a `Node` given a `Node`, `int` or `str`. If `add_missing` is `True`, the node is added or created, otherwise an `IndexError` is raised. This is helpful for flexibly converting between node representations with convenient existence handling.
- `add_nodes(list[Node | str])`: Convenience method to add a list of nodes.
- `add_edges(edges: list[Edge | tuple[NodeOrIndex, NodeOrIndex]])`: Convenience method to add a list of edges.
- `rename_nodes(name_map: dict[NodeOrIndex, str] | list[str])`: Method to rename nodes either by specifying a potentially partial mapping from node(s) to new name(s), or a list of new names. Handles updating both the `Node.name` attributes and the cache.
- `rename_node(old_name: NodeOrIndex, new_name: str)`: Shorter syntax for renaming a single node.
- `remove_nodes(nodes: list[NodeOrIndex])`: Method for removing nodes from the skeleton and updating caches. **Does NOT update corresponding instances.**
- `remove_node(node: NodeOrIndex)`: Shorter syntax for removing a single node.
- `reorder_nodes(new_order: list[NodeOrIndex])`: Method for setting the order of the nodes within the skeleton with cache updating. **Does NOT update corresponding instances.**
- `Instance`/`PredictedInstance`
- `update_skeleton()`: Updates the `points` attribute on the instance to reflect changes in the associated skeleton (removed nodes and reordering). This is called internally after updating the skeleton from the `Labels` level, but also exposed for more complex data manipulation workflows.
- `replace_skeleton(new_skeleton: Skeleton, node_map: dict[NodeOrIndex, NodeOrIndex] | None = None, rev_node_map: dict[NodeOrIndex, NodeOrIndex] | None = None)`: Method to replace the skeleton on the instance with optional capability to specify a node mapping so that data stored in the `points` attribute is retained and associated with the right nodes in the new skeleton. Mapping is specified in `node_map` from old to new nodes and defaults to mapping between node objects with the same name. `rev_node_map` maps new nodes to old nodes and is used internally when calling from the `Labels` level as it bypasses validation.
- `Labels`
- `instances`: Convenience property that returns a generator that loops over all labeled frames and returns all instances. This can be lazily iterated over without having to construct a huge list of all the instances.
- `rename_nodes(name_map: dict[NodeOrIndex, str] | list[str], skeleton: Skeleton | None = None)`: Method to rename nodes in a specified skeleton within the labels.
- `remove_nodes(nodes: list[NodeOrIndex], skeleton: Skeleton | None = None)`: Method to remove nodes in a specified skeleton within the labels. This also updates all instances associated with the skeleton, removing point data for the removed nodes.
- `reorder_nodes(new_order: list[NodeOrIndex], skeleton: Skeleton | None = None)`: Method to reorder nodes in a specified skeleton within the labels. This also updates all instances associated with the skeleton, reordering point data for the nodes.
- `replace_skeleton(new_skeleton: Skeleton, old_skeleton: Skeleton | None = None, node_map: dict[NodeOrIndex, NodeOrIndex] | None = None)`: Method to replace a skeleton entirely within the labels, updating all instances associated with the old skeleton to use the new skeleton, optionally with node remapping to retain previous point data.
* Add more checks for video seeking/reading failure by talmo in https://github.com/talmolab/sleap-io/pull/138
* Fix `HDF5Video` edge cases by talmo in https://github.com/talmolab/sleap-io/pull/137
* Docs changelog generation by talmo in https://github.com/talmolab/sleap-io/pull/130
* Add `Labels.extract`, `Labels.trim` and `Video.save` by talmo in https://github.com/talmolab/sleap-io/pull/140
- `LabeledFrame.frame_idx`: Now always converted to `int` type.
- `Video.close()`: Now caches backend metadata to `Video.backend_metadata` to persist metadata on close.
- `copy.deepcopy()` now works on `Video` objects even if backend is open.
- `Video.save(save_path: str | Path, frame_inds: list[int] | np.ndarray | None = None, video_kwargs: dict[str, Any] | None = None)`: Method to save a video file to an MP4 using `VideoWriter` with an optional subset of frames.
- `Labels.extract(inds: list[int] | list[tuple[Video, int]] | np.ndarray, copy: bool = True)`: Add method to extract a subset of frames from the labels, optionally making a copy, and return a new `Labels` object.
- `Labels.trim(save_path: str | Path, frame_inds: list[int] | np.ndarray, video: Video | int | None = None, video_kwargs: dict[str, Any] | None = None)`: Add method to extract a subset of the labels, write a video clip with the extracted friends, and adjust frame indices to match the clip.
* Docs automation by talmo in https://github.com/talmolab/sleap-io/pull/141
* Add more examples to docs by talmo in https://github.com/talmolab/sleap-io/pull/142
**Full Changelog**: https://github.com/talmolab/sleap-io/compare/v0.1.10...v0.2.0