sleap-io v0.8.0 Release Notes
Summary
sleap-io v0.8.0 is a feature release centered on getting data in and out from more places: load .slp and video directly from URLs and cloud buckets (including Google Drive share links), import full DeepLabCut projects (skeleton edges, source videos, train/test splits), and read COCO instance-segmentation datasets as SegmentationMask annotations with identity tracks. It rounds out the v0.7.0 segmentation architecture with mask provenance (to_user() / from_predicted), adds virtual on-read video cropping with a new apply-crops CLI, and ships a batch of rendering upgrades (mask overlays, track-identity coloring, grayscale and instance-less-frame fixes).
⚠️ Breaking change:Labels.merge()/Labels.match()now default to identity-based track matching instead of name-based. Pipelines that relied on collapsing same-named tracks across files must now passtrack="name"explicitly. See Breaking Changes below before upgrading.
Highlights:
⚠️ Merge track matching now defaults toidentity, notname(#449) —Labels.merge(other)no longer collapses tracks just because they share a name. Passtrack="name"(or--track nameon the CLI) to restore the old behavior. Audit downstream.merge()call sites before bumping.- Remote loading —
sio.load_file/sio.load_video(and the CLI read commands) read.slpand video overhttp(s),s3,gs/gcs,az/abfs, and Google Drive share links via fsspec/PyAV (#439, #442, #441, #445, #501). - DeepLabCut project import —
sio.load_dlc_project()reads a DLCconfig.yaml/project directory intoLabels(skeleton edges, source videos),sio.load_dlc_splits()returns the train/test split as aLabelsSet, andsio convertimports projects from the CLI (#424, #450, #496). - COCO instance segmentation — the COCO reader imports polygon and (compressed or uncompressed) RLE segmentation as
SegmentationMaskannotations and can map categories to identity tracks viacategory_as_track=True, also exposed insio convert(#479, #487, #496). - Segmentation mask provenance —
PredictedSegmentationMask.to_user(), afrom_predictedlink on user masks, link-first mask merge, and.slppersistence of the provenance link (preserved acrossmerge) (#472, #475, #478, #491). - Virtual on-read cropping —
Video.crop()/CropVideoBackendpresent a cropped view without re-encoding, round-trip through.slp, and bake to real files via the newsio apply-cropscommand (#460). - Rendering upgrades — auto-drawn
SegmentationMaskoverlays, track-identity coloring for masks/ROIs/bboxes, a grayscale-render crash fix, instance-less-frame rendering, and centroid scoping/scaling fixes (#461, #470, #466, #468, #494). - Smaller
.slpfiles — mask RLE and ROI WKB datasets are now gzip-compressed on write (#463, #465). - Cross-platform CLI —
siono longer crashes on the default Windows (cp1252) console (#486), andsio showsurfaces SegmentationMask/ROI counts (#500). scipy<1.18pin in the[mat]/[all]extras to keep the LEAP.matreader working (#485).
Installation / Upgrade
# One-off CLI usage (no installation needed)
uvx sleap-io@0.8.0 show labels.slp
# Install as CLI tool (new install)
uv tool install "sleap-io[all]"
# Upgrade existing CLI tool installation
uv tool upgrade sleap-io
# Add to project (new dependency)
uv add "sleap-io[all]"
# Upgrade existing project dependency
uv lock --upgrade-package sleap-io && uv syncSee installation docs for more options.
Breaking Changes
⚠️ Merge track matching now defaults to identity, not name (#449, #448)
Change: TrackMatcher's default method flipped from NAME to IDENTITY. This changes the default behavior of Labels.merge(), Labels.match(), and the sio merge CLI (--track default is now identity).
Previously, merging two label files that each contained a Track(name="track_0") would treat them as the same track (collapsing by name). Now, two tracks are only matched if they are the same Python object (identity) — so the merge produces two distinct tracks unless you opt back into name matching.
import sleap_io as sio
base = sio.load_file("session_a.slp")
other = sio.load_file("session_b.slp")
# NEW default (0.8.0): identity matching — same-named tracks stay separate
base.merge(other) # -> distinct tracks preserved
# RESTORE 0.7.x behavior: collapse tracks that share a name
base.merge(other, track="name")# CLI: restore name-based collapsing
sio merge a.slp b.slp -o merged.slp --track nameMigration for downstream (sleap, sleap-nn): Human-in-the-loop and re-tracking flows that relied on name-based collapse will now produce duplicate tracks. Audit every
.merge()/.match()call site and addtrack="name"where name-collapse is intended.sio unsplitalready pinstrack="name"internally, so its behavior is unchanged. A spatial-divergence warning (#448) now fires when name-collision merges combine tracks at incompatible locations. Documented indocs/merging.mdanddocs/cli.md.
COCO polygon segmentation now defaults to rasterized masks, not vector ROIs (#479)
Change: When the COCO reader encounters polygon segmentation, it now produces a single UserSegmentationMask per annotation by default (segmentation_format="mask"). In v0.7.x, polygons were read as vector UserROI objects (one per ring).
# NEW default (0.8.0): polygon -> rasterized SegmentationMask
labels = sio.load_coco("instances.json") # masks, no ROIs
# RESTORE 0.7.x vector-ROI behavior
labels = sio.load_coco("instances.json", segmentation_format="roi")
sio convertexposes both options too:--coco-segmentation {mask,roi}selects the representation and--coco-category-as-trackmaps categories to identity tracks (#496).
Structural skeleton deduplication in update / append / extend (#447)
Change: When you add instances whose skeletons are structurally identical (same ordered node names + edges + symmetries) but are distinct Python objects, Labels now canonicalizes them to a single Skeleton and rebinds the instances to it. Point data is preserved exactly (verified point-safe); only len(labels.skeletons) changes.
s1 = sio.Skeleton(["head", "thorax", "abdomen"])
s2 = sio.Skeleton(["head", "thorax", "abdomen"]) # structurally equal, distinct object
labels = sio.Labels(labeled_frames=[lf_using_s1, lf_using_s2])
len(labels.skeletons) # 0.8.0: 1 (0.7.x: 2)Skeletons with a different node order are still kept distinct. Code that counted len(labels.skeletons) or held references to now-canonicalized duplicate skeletons may observe a difference. Documented in docs/model/labels.md.
Re-saving .slp now trusts recorded video metadata by default (#483)
Change: save_slp gained prefer_metadata=True (the default). When a video carries recorded backend_metadata (shape / grayscale / fps), the serializer now writes those recorded values instead of re-decoding the live media. This avoids opening (and OOM-ing on) large or remote media just to re-stamp metadata on save.
Impact: Re-saving a project no longer refreshes fps/shape from the live media file. To force a refresh from the live backend, pass prefer_metadata=False:
labels.save("out.slp", prefer_metadata=False) # re-read shape/fps/grayscale from mediaPreflight-hardened: earlier 0.8.0 builds could serialize a stale/inconsistent shape after a resolution-changing relink (
replace_filename) or a post-loadgrayscaleflip. Both are fixed: a relink now invalidates the stale recorded shape/grayscale/fps (#490), and metadata serialization reconciles the channel count with thegrayscaleflag (#495). The common open → edit → save path remains byte-identical to v0.7.x.
scipy<1.18 upper bound added to [mat] and [all] extras (#485, #484)
Change: scipy 1.18.0 broke pymatreader mat-struct parsing (used by the LEAP .mat reader), so the mat and all extras now pin scipy<1.18. sleap-io itself does not import scipy; this only affects environments installing those extras.
Migration for downstream: packagers requesting
sleap-io[all]alongside a dependency that requiresscipy>=1.18will hit a resolver conflict untilpymatreaderis fixed upstream.
New Features
Remote loading: URLs, cloud buckets, and Google Drive (#439, #442, #441, #445)
Load .slp files and videos directly from remote locations — no manual download step. Supported schemes: http, https, s3, gs/gcs, az/abfs, plus Google Drive share links.
import sleap_io as sio
# .slp over http(s) / cloud
labels = sio.load_file("https://example.com/predictions.slp")
labels = sio.load_file("s3://my-bucket/predictions.slp")
# Pass auth headers for protected .slp endpoints
labels = sio.load_file(
"https://example.com/predictions.slp",
headers={"Authorization": "Bearer <token>"},
)
# Remote video (streamed via PyAV)
video = sio.load_video("https://example.com/recording.mp4")
# Google Drive share link
labels = sio.load_file("https://drive.google.com/file/d/<id>/view")
# Clear the on-disk fsspec cache
sio.clear_remote_cache()Hardening in #445 adds an auth probe, deterministic handle lifecycle/close, Google Drive capacity handling, and an fsspec cache. See docs/remote.md for the full guide.
The CLI's read commands (sio show, sio convert, sio render, sio export, …) also accept these URLs directly (#501):
sio show "https://example.com/predictions.slp"
sio convert "s3://my-bucket/predictions.slp" -o local.slpScope notes:
headers=is forwarded for.slpover HDF5. Remote media video cannot be authenticated viaheaders=(the av/imageio backend has no header plumbing); passingheaders=/stream_mode=for a remote.mp4/.avinow raises a clear error instead of silently dropping them (#498) — use a pre-signed URL for protected media.- Only
slpandvideoare loadable over a URL today; other formats (coco,dlc,csv, …) raiseNotImplementedErrorover a URL — download locally first.
DeepLabCut project import (#424, #450)
Import a full DeepLabCut project — skeleton edges, source videos, and bodyparts — from its config.yaml or project directory, plus the train/test split as a LabelsSet.
import sleap_io as sio
# Whole project (config.yaml or the project directory)
labels = sio.load_dlc_project("my_project/config.yaml")
labels = sio.load_file("my_project/config.yaml") # auto-detected
# Train/test splits as a LabelsSet
splits = sio.load_dlc_splits("my_project/config.yaml")
train, test = splits["train"], splits["test"]sio convert now recognizes a DLC project directory / config.yaml and imports it (#496):
sio convert my_project/config.yaml -o project.slp
sio convert my_project/ -o project.slp --from dlc_projectScope notes:
load_dlc_splitsrequires the labeled images to be present on disk; if they are absent it now emits a warning and returns empty splits (#492) rather than failing silently.
COCO instance-segmentation reader (#479)
The COCO reader now imports object detection/segmentation datasets: polygon and RLE segmentation become SegmentationMask annotations, and categories can be mapped to identity tracks.
import sleap_io as sio
# Polygon/RLE segmentation -> SegmentationMask (default)
labels = sio.load_coco("instances.json")
# Map COCO categories to identity tracks
labels = sio.load_coco("instances.json", category_as_track=True)
# Keep the v0.7.x vector-ROI representation instead of rasterizing
labels = sio.load_coco("instances.json", segmentation_format="roi")Both compressed (LEB128 string-counts) and uncompressed (list-of-int) RLE, as well as polygon segmentation, are decoded (#487). sio convert exposes the identity/segmentation options too (#496):
sio convert instances.json --from coco -o out.slp \
--coco-category-as-track --coco-segmentation roiSegmentation mask provenance: to_user() and from_predicted (#472, #475, #478)
Predicted masks can now be "promoted" to user masks while remembering where they came from, mirroring the long-standing Instance / PredictedInstance relationship.
import numpy as np
import sleap_io as sio
pred = sio.PredictedSegmentationMask.from_numpy(arr, score=0.95)
user = pred.to_user() # UserSegmentationMask
user.from_predicted is pred # True — provenance link preserved
# Link-first mask merge keeps user/predicted pairs together; the
# from_predicted link is persisted to and restored from .slp (#475).predict → correct → consolidate workflows retain the predicted source through save/load, including when consolidation goes through Labels.merge(..., frame="auto") — the provenance link is remapped to the surviving copy on merge so it is no longer dropped (#491).
Virtual on-read video cropping + apply-crops CLI (#460)
Video.crop() returns a virtual cropped view backed by CropVideoBackend — frames are cropped on read, with no re-encoding. The crop round-trips through .slp (in a /video_crops table) and can later be baked into real cropped video files.
import sleap_io as sio
video = sio.load_video("recording.mp4")
cropped = video.crop(crop=(x1, y1, x2, y2)) # virtual view
cropped = video.crop(center=(cx, cy), size=(256, 256))
# Bake virtual crops into physical files and rewire references
labels.apply_crops("baked.slp")# Materialize every virtual crop in an SLP into baked video files
sio apply-crops cropped.slp -o baked.slp --quality high
sio apply-crops cropped.slp -o baked.slp --dry-runNote: baked crop dimensions are rounded up to a codec-friendly multiple of 16 (bottom/right padding), so e.g. a 100×100 crop bakes to 112×112. Coordinates are unchanged.
Rendering upgrades (#461, #470, #466, #468)
- #461 —
SegmentationMaskoverlays are now auto-drawn when rendering frames that contain masks, and a grayscale-render crash is fixed. - #470 — segmentation masks, ROIs, and bounding boxes are colored by track identity (matching pose coloring).
- #466 — frames with no instances are rendered via the
Labelspath even when no skeletons exist. - #468 —
render_imagecentroids are scoped to the rendered video (no cross-video bleed).
import sleap_io as sio
labels = sio.load_file("instances.coco.json", category_as_track=True)
# Masks auto-overlaid, colored by track identity:
sio.render_video(labels, "seg.mp4", color_by="track")Overlay note:
render_image(..., overlay=...)accepts either a singleSegmentationMask/ROI/BoundingBox/LabelImage/ndarrayor a list of them (#505).
Improvements
Smaller .slp files (#463, #465)
- #465 — the
roi_wkbdataset is gzip-compressed on write. - #463 — the
mask_rledataset is gzip-compressed on write.
These are write-time changes; older readers continue to read the compressed datasets transparently. ROI/mask-heavy projects (e.g. instance-segmentation imports) shrink substantially on disk.
Metadata-preferring .slp serialization (#483)
save_slp(prefer_metadata=True) (the new default) avoids decoding large/remote media on every save by trusting recorded backend_metadata. See Breaking Changes for the behavior change and prefer_metadata=False opt-out.
Path handling and helpful errors
- #457 —
transform_labels/transform_videoacceptstrpaths (not onlyPath). - #446 — a missing SLP metadata JSON attribute now raises a clear, actionable error instead of an opaque
KeyError. - #438 —
SkeletonEncodernow preserves edge-less (isolated) nodes in standalone skeleton JSON output (previously dropped). (Minor residual: standalone skeleton-JSON does not preserve isolated-node ordering; the.slppath is unaffected — see Known Issues.)
Embedded-subset shape resolution (#476)
- #473/#476 —
_get_effective_shapewalks thesource_videochain nearest-first, so embedded subsets resolve to their source video's shape and match correctly.
Mask provenance persistence and to_user() (#472, #475, #478)
See New Features — the from_predicted provenance link is persisted in .slp (format 2.4) and mask merge is link-first.
CLI reaches the new readers (#496, #500, #501)
- #501 —
sio show/convert/render/export/split/filenamesaccept remote URLs as input (http(s),s3,gs/gcs,az/abfs), matching the Python API. - #496 —
sio convertimports DLC projects (config.yaml/ project directory,--from dlc_project) and forwards COCO options (--coco-category-as-track,--coco-segmentation {mask,roi}). - #500 —
sio shownow reports SegmentationMask and ROI counts in the header and per-frame listing, so segmentation-only files no longer look empty.
Fixes
- #467 (#466) — render instance-less frames via the
Labelspath when no skeletons exist. - #468 — scope
render_imagecentroids to the rendered video. - #470 — color segmentation masks (and ROI/bbox) by track identity.
- #461 — auto-draw
SegmentationMaskoverlays and fix grayscale render crash. - #476 (#473) — resolve effective shape through the source chain so embedded subsets match.
- #446 (#429) — raise a helpful error when an SLP metadata JSON attribute is missing.
- #447 (#427) — structural skeleton dedup in
Labels.update/append/extend(see Breaking Changes; point-safe). - #438 — preserve edge-less nodes in
SkeletonEncoderJSON output. - #485 (#484) — pin
scipy<1.18in themat/allextras to keep the LEAP.matreader working. - #507 —
sio reencodeno longer deadlocks on a 0-frame input; it now defaults to.mp4output and adds a--replaceflag. - #480 — Analysis CSV /
to_dataframe(all_frames) export now spans the full video length instead of stopping at the last labeled frame. - #481 — COCO reader: distinct images sharing a
file_nameno longer collide (each becomes its own frame), and a scored detection annotation reads as aPredictedSegmentationMask/PredictedROI.
Preflight fixes (release-hardening pass)
A pre-release adversarial audit surfaced and fixed the following before tagging:
- #489 —
Labels.merge()no longer silently misaligns per-node points when matched skeletons have a different node order (the structure matcher now reorders points by node name). Data-correctness fix. - #487 — the COCO reader decodes compressed (LEB128 string-counts) RLE masks instead of crashing with a
TypeError. - #490 — a resolution-changing
replace_filenamerelink invalidates stale recordedshape/grayscale/fpssosave_slp(prefer_metadata=True)no longer writes a wrong shape. - #495 — metadata serialization reconciles the channel count with the
grayscaleflag, so a post-load grayscale flip survives a metadata save. - #491 —
Labels.merge(..., frame="auto")remaps mask/instancefrom_predictedprovenance to the surviving copy, so it is no longer dropped on save. - #486 —
sio fixandsio render --helpno longer crash on the default Windows (cp1252) console (stdout/stderr are reconfigured to UTF-8 at import). - #488 —
sio show <dlc_project>/load_file(<dlc_project>, open_videos=…)no longer crash on an unexpectedopen_videos/lazykwarg. - #493 — a degenerate COCO polygon with a valid
bboxfalls back to the bbox instead of being dropped. - #494 — centroid markers scale linearly with the render
scale(no longer double-scaled / vanishing underpreview/draftpresets). - #492 —
load_dlc_splitswarns (instead of silently returning empty splits) when the labeled images are missing on disk. - #498 — a remote media video given unusable
headers=/stream_mode=raises a clear error instead of silently dropping them. - #503 —
save_cocowrites track identity (attributes.object_id) for mask/ROI/bbox annotations, so a COCO round-trip preserves tracks (previously keypoint-only). - #502 — the standalone overlay helpers (
draw_masks/draw_rois/draw_bboxes/draw_label_image) accept grayscale(H, W)/(H, W, 1)input instead of crashing. - #505 —
render_image(..., overlay=<single object>)accepts a singleSegmentationMask/ROI/BoundingBox(previously silently ignored unless wrapped in a list). - #504 —
tracking_scoreand_instance_idxare preserved through mask/bbox/ROI conversions andresampled(). - #506 — CLI/API polish:
draw_centroidsis exported atsio.*; theexportcommand appears insio --helpgroups;python -m sleap_io.io.cliworks; and several doc corrections (palette default, video-crop note, removal of a nonexistent DeepLabCut.h5reader claim).
Post-audit follow-ups (deferred low-severity findings)
- #509 —
LabeledFrame.is_user_labelednow counts user ROIs (a UserROI-only frame is correctly treated as user-labeled). - #510 —
sio transform --crop/--scale/--rotate/--padraise a clear error for a non-integeridx:prefix instead of a raw traceback. - #511 —
sio convert --from dlcpointed at a DLC project errors with a pointer to--from dlc_projectinstead of misrouting to the single-CSV reader. - #512 — standalone skeleton JSON preserves isolated (edge-less) node order on round-trip (the
.slppath was already correct).
Documentation
- #458 — split the formats reference into per-format leaf pages, repoint remote cross-links, add a Guides landing page.
- #454 — split spatial-annotation docs into Centroids / Boxes / ROIs / Segmentation.
- #455 — content and format-reference corrections for 0.8.0.
- #456 — add
llms.txt, surface motion trails, rendering/CLI/nav polish. - #453 — repair non-runnable examples and guard
pyconblocks. - #444 — new Remote loading guide (URLs, cloud, Google Drive, video).
- #499 — add a "Breaking change in 0.8.0" callout to the COCO format reference (polygon → mask default), remove the documented-but-nonexistent
sio render --crop auto/--crop-padding, and repair nine dead cross-link anchors. - #497 — execute runnable
cropping.mdexamples (converted topycon) so feature-doc API drift is caught by CI.
Known Issues
The pre-release audit's headline correctness, CLI, and Windows-console findings were all fixed before tagging (see Preflight fixes). The following minor items ship as-is and are tracked for follow-up:
sio showprints×(U+00D7) in video dimensions, which can render as mojibake when piped to a cp1252 consumer (the interactive console itself is now UTF-8).- Some cross-modality conversions downcast predicted → user —
to_polygon/to_roireturn user-variant outputs regardless of source (documented design choice).
A fuller list of low/info observations from the audit is tracked internally for post-release cleanup.
Changelog
- #438: fix(io): preserve edge-less nodes in SkeletonEncoder JSON output (@talmo)
- #439: feat(io): Load .slp from URLs and cloud buckets via fsspec (PR 1/3) (@talmo)
- #441: feat(io): Google Drive share-link loading (PR 3/3) (@talmo)
- #442: feat(video): Remote video loading via PyAV (supersedes #440) (@talmo)
- #444: docs: Add Remote loading guide (URLs, cloud, Google Drive, video) (@talmo)
- #445: fix(io): harden remote URL loading (auth, handle lifecycle, Drive caps, fsspec cache) (@talmo)
- #446: fix(io): raise helpful error when SLP metadata JSON attribute is missing (#429) (@talmo)
- #447: fix(model): structural skeleton dedup in Labels.update/append/extend (#427) (@talmo)
- #448: feat(model): warn on spatially-divergent track-name merge collisions (#425) (@talmo)
- #449: feat(model)!: default Labels.merge track matching to identity, not name (#425) (@talmo)
- #450: feat(io): import DLC project metadata - edges, source videos, splits (#424) (@talmo)
- #452: chore(release): Bump version to 0.8.0 (@talmo)
- #453: fix(docs): repair non-runnable examples and guard pycon blocks (@talmo)
- #454: docs: split spatial annotations into Centroids/Boxes/ROIs/Segmentation (@talmo)
- #455: docs: content & format-reference corrections for 0.8.0 (@talmo)
- #456: docs: add llms.txt, surface motion trails, and rendering/cli/nav polish (@talmo)
- #457: fix(transform): accept str paths in transform_labels / transform_video (@talmo)
- #458: docs: split formats into leaf pages, repoint remote cross-links, add Guides landing (@talmo)
- #460: feat: virtual on-read video cropping (CropVideoBackend) (@talmo)
- #461: fix(render): auto-draw SegmentationMask overlays and fix grayscale crash (@talmo)
- #463: perf(slp): gzip-compress the mask_rle dataset when writing .slp (#464) (@talmo)
- #465: perf(slp): gzip-compress the roi_wkb dataset when writing .slp (@talmo)
- #466/#467: fix(render): render instance-less frames via Labels path when no skeletons exist (@talmo)
- #468: fix(render): scope render_image centroids to the rendered video (@talmo)
- #470: fix(render): color segmentation masks (and ROI/bbox) by track identity (@talmo)
- #472: feat(model): add PredictedSegmentationMask.to_user() + from_predicted provenance (@talmo)
- #475: feat(slp): persist UserSegmentationMask.from_predicted (#474 Gap 2) (@talmo)
- #476: fix(model): resolve effective shape through source chain so embedded subsets match (#473) (@talmo)
- #478: feat(model): mask unused_predictions + link-first mask merge (#474 Gap 1) (@talmo)
- #479: feat(coco): read instance-segmentation datasets (polygon→SegmentationMask, identity tracks) (@talmo)
- #480: fix(csv): span the full video in all_frames DataFrame/CSV export (@alicup29)
- #481: fix(coco): robust duplicate-filename frames + scored segmentation → predicted (@talmo)
- #483: feat(slp): prefer recorded video metadata over decoding when serializing (@tom21100227)
- #485: fix(deps): pin scipy<1.18 to keep pymatreader (LEAP reader) working (@tom21100227)
- #486: fix(cli): make stdout/stderr UTF-8 so sio fix and render --help work on cp1252 (@talmo)
- #487: fix(coco): decode compressed (LEB128 string-counts) RLE masks (@talmo)
- #488: fix(io): tolerate loader kwargs in load_dlc_project/load_dlc_splits (sio show DLC) (@talmo)
- #489: fix(model): reorder points by node name in merge so reordered skeletons don't misalign (@talmo)
- #490: fix(slp): invalidate stale shape/grayscale/fps on a resolution-changing relink (@talmo)
- #491: fix(model): remap mask/instance from_predicted on merge so provenance survives save (@talmo)
- #492: fix(io): warn when DLC splits resolve empty (labeled images missing) (@talmo)
- #493: fix(coco): fall back to bbox when a degenerate polygon yields no geometry (@talmo)
- #494: fix(render): stop double-scaling centroid markers by scale (@talmo)
- #495: fix(slp): reconcile serialized channel count with the grayscale flag (@talmo)
- #496: feat(cli): reach DLC-project import and COCO seg/identity options from convert (@talmo)
- #497: test(docs): execute runnable cropping.md examples (@talmo)
- #498: fix(video): reject (not silently drop) headers/auth kwargs for remote media video (@talmo)
- #499: docs: COCO breaking-change callout, remove nonexistent render --crop docs, fix dead anchors (@talmo)
- #500: feat(cli): surface SegmentationMask/ROI counts in sio show (@talmo)
- #501: feat(cli): accept remote URLs as input to read commands (@talmo)
- #502: fix(render): handle grayscale input in overlay helpers (@talmo)
- #503: fix(coco): write track identity (attributes.object_id) for mask/ROI/bbox (@talmo)
- #504: fix(model): preserve tracking_score and _instance_idx through conversions (@talmo)
- #505: fix(render): accept a single object as render_image overlay= (@talmo)
- #506: chore(cli/docs): export draw_centroids, group export cmd, main guard, doc fixes (@talmo)
- #507: fix(cli): fix reencode deadlock at 0 frames; default .mp4 output + --replace (@talmo)
- #508: ci: use sysmon coverage core on py3.13 and raise test timeout to 60m (@talmo)
- #509: fix(model): count ROIs in LabeledFrame.is_user_labeled (@talmo)
- #510: fix(cli): clean error for a non-integer transform index prefix (@talmo)
- #511: fix(cli): clear error for --from dlc on a DLC project (@talmo)
- #512: fix(io): preserve isolated-node order in standalone skeleton JSON (@talmo)
Full Changelog: v0.7.1...v0.8.0