Skip to content

fix(coco): robust duplicate-filename frames + scored segmentation → predicted#481

Open
talmo wants to merge 1 commit into
mainfrom
fix/coco-reader-followups
Open

fix(coco): robust duplicate-filename frames + scored segmentation → predicted#481
talmo wants to merge 1 commit into
mainfrom
fix/coco-reader-followups

Conversation

@talmo

@talmo talmo commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Two COCO-reader follow-ups from #479, in one PR:

  1. Duplicate-file_name crash fixed. Distinct images entries that share a file_name no longer raise KeyError.
  2. Scored segmentation reads as predicted. A COCO score on a detection annotation now produces PredictedSegmentationMask / PredictedROI, mirroring the existing bbox predicted/user split.

Key Changes

Fix #1 — duplicate filenames (correctness bug)

The reader built its frame-index map by reverse-matching resolved image paths to image_ids and breaking on the first match. Two images entries pointing at the same file_name resolved to the same path, so both frames mapped to the first image_id and the second raised KeyError. Each entry now gets its own frame index — its position within its shape group — assigned by image_id during grouping, and the buggy path-reverse-lookup loop is removed. Each images entry becomes its own LabeledFrame, as documented.

Fix #3 — scored segmentation → predicted

_decode_segmentation gained a score parameter. When score is not None, it builds PredictedSegmentationMask / PredictedROI carrying the score; otherwise the User* variants (unchanged default). The detection path passes score=annotation.get("score"); the keypoint path keeps segmentation as User (its score belongs to the pose instance), matching how the keypoint-path bbox is always User. ROI.to_mask is now polymorphic: a PredictedROI rasterizes to a PredictedSegmentationMask with its score; any other ROI still yields a UserSegmentationMask. BoundingBox.to_mask is unaffected (BoundingBox.to_roi returns a UserROI).

Example

import sleap_io as sio

# A COCO results file with `score` on each detection:
labels = sio.load_file("predictions.coco.json")
m = labels.labeled_frames[0].masks[0]
type(m).__name__   # "PredictedSegmentationMask"
m.score            # e.g. 0.91

Predicted masks round-trip through .slp (class + score preserved).

API Changes

  • ROI.to_mask() now returns a PredictedSegmentationMask (carrying score) when called on a PredictedROI; behavior for UserROI is unchanged.
  • coco._decode_segmentation (private) gained a score keyword. No public-signature changes; read_labels/load_coco behavior only changes for annotations that carry a score.

Testing

  • tests/io/test_coco.py: 3× duplicate-file_name entries load as distinct frames; scored polygon/RLE → predicted, scored ROI-mode → PredictedROI, unscored → User*, with .slp round-trip preserving class + score.
  • tests/model/test_roi.py: PredictedROI.to_maskPredictedSegmentationMask with score; UserROI.to_mask stays UserSegmentationMask.
  • Reviewed via an adversarial multi-agent pass (6 dimensions × 3-skeptic verification); the one confirmed finding (a to_mask docstring copy-paste typo) was fixed.
  • ruff check/format --check clean; full suite green; docs guard green.

🤖 Generated with Claude Code

…predicted

Two COCO reader follow-ups (see #479 follow-ups):

- Distinct `images` entries that share a `file_name` no longer crash. Each entry
  now gets its own frame index (its position within its shape group), assigned by
  image_id during grouping, replacing the path-reverse-lookup that collided on
  duplicate filenames and raised KeyError.
- A COCO `score` on a detection annotation now yields predicted segmentation:
  PredictedSegmentationMask / PredictedROI carrying the score (unscored stays
  User), mirroring the existing bbox predicted/user split. The keypoint path
  keeps segmentation as User. ROI.to_mask is now polymorphic: a PredictedROI
  rasterizes to a PredictedSegmentationMask with its score (UserROI unchanged;
  BoundingBox.to_mask unaffected since to_roi returns UserROI).

Tests cover the 3x duplicate-filename case, predicted RLE/polygon/ROI variants
with .slp round-trip, the user fallback, and PredictedROI.to_mask. Docs note the
predicted-vs-user behavior. Reviewed via an adversarial multi-agent pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.97%. Comparing base (acac75e) to head (9a35bdc).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #481   +/-   ##
=======================================
  Coverage   92.97%   92.97%           
=======================================
  Files          54       54           
  Lines       19439    19441    +2     
  Branches     4394     4391    -3     
=======================================
+ Hits        18073    18076    +3     
  Misses        651      651           
+ Partials      715      714    -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

github-actions Bot pushed a commit that referenced this pull request Jun 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Docs Preview

Preview URL https://io.sleap.ai/pr/481/
Commit 9a35bdc0a8017cd2aaaba4533cac37cb1346feac

Changed pages:

API changes detected - View API Reference

This preview will be removed when the PR is closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant