Skip to content

refactor: vectorize _mask_from_mask loop - #2495

Draft
shaoming11 wants to merge 10 commits into
roboflow:developfrom
shaoming11:shaoming/contribution-notes
Draft

refactor: vectorize _mask_from_mask loop#2495
shaoming11 wants to merge 10 commits into
roboflow:developfrom
shaoming11:shaoming/contribution-notes

Conversation

@shaoming11

Copy link
Copy Markdown
Contributor

Summary

  • Replace the Python for loop in ComparisonAnnotator._mask_from_mask with a single np.any(detections.mask, axis=0) call, eliminating per-mask iteration and in-place |= accumulation.

Test plan

  • Existing annotator tests pass (uv run pytest tests/test_annotators.py)
  • Visual inspection of ComparisonAnnotator output unchanged

🤖 Generated with Claude Code

shaoming11 and others added 7 commits August 1, 2026 15:45
Hoist loop-invariant branch outside the loop and replace per-detection
Python loop with single-pass list comprehensions over the backing arrays.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover the class_name, class_id, index-fallback, custom labels, and
empty detections paths to close the coverage gap from roboflow#2465.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each branch already returns, so the elif/else chain was structurally
unneeded; flat guard clauses read slightly cleaner.

[resolve group] PR roboflow#2465 — items 7

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…match

get_labels_text() previously always produced exactly len(detections)
labels. The class_name/class_id branches now raise ValueError instead of
silently returning a shorter/longer list if detections.data['class_name']
or class_id is mutated out of alignment with detections directly
(bypassing Detections' own validation).

Flagged by Copilot code review; discussed independently in /review as
low-risk/unreachable for validly-constructed Detections, but worth the
defensive check since it costs nothing on the happy path.

[resolve group] PR roboflow#2465 — items 1

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- N=1 single-detection case for class_id-fallback and index-fallback
- custom_labels precedence over class_name specifically (not just class_id)
- non-str-dtype class_name to make the str() coercion load-bearing
- pin which branch Detections.empty() exercises (class_id)
- cover the ValueError guard added for class_name/class_id length mismatch

[resolve group] PR roboflow#2465 — items 3, 4, 5, 6 (+1 additional test for the
length-guard added alongside item 1, not separately selected)

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@shaoming11
shaoming11 requested a review from SkalskiP as a code owner August 21, 2026 12:55
@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 88%. Comparing base (1a2b9b2) to head (6f8929f).
⚠️ Report is 27 commits behind head on develop.

Additional details and impacted files
@@           Coverage Diff            @@
##           develop   #2495    +/-   ##
========================================
  Coverage       87%     88%            
========================================
  Files           85      85            
  Lines        12023   12152   +129     
========================================
+ Hits         10514   10683   +169     
+ Misses        1509    1469    -40     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Borda Borda changed the title refactor: vectorize _mask_from_mask loop refactor: vectorize _mask_from_mask loop Aug 21, 2026
@Borda
Borda requested a balanced review from Copilot August 21, 2026 13:52
@Borda Borda added the enhancement New feature or request label Aug 21, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Vectorizes mask unioning in ComparisonAnnotator, but introduces a CompactMask memory regression and unrelated label-validation changes.

Changes:

  • Replaces iterative dense-mask union with np.any.
  • Adds label-alignment validation and tests.
  • Expands scope beyond the PR description.

Review scores: Code quality 2/5 · Testing 2/5 · Documentation 2/5

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/supervision/annotators/core.py Vectorizes mask unioning.
src/supervision/annotators/utils.py Adds label-alignment errors.
tests/annotators/test_utils.py Tests label generation and validation.
Suppressed comments (1)

src/supervision/annotators/core.py:3464

  • The existing ComparisonAnnotator tests cover only empty and box-based detections, so none exercises the mask-union path changed here; the newly added tests target a different helper. Add regression coverage for multiple overlapping dense masks and for CompactMask without whole-stack materialization (for example, patch to_dense to fail), so both output parity and the memory-preserving branch are protected.
        result: npt.NDArray[np.bool_] = np.any(detections.mask, axis=0)
        return result

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/supervision/annotators/core.py Outdated
Comment thread src/supervision/annotators/utils.py Outdated
Borda and others added 2 commits August 21, 2026 18:14
Changes:
- Keep dense comparison-mask union vectorized while reducing CompactMask crops incrementally without materializing the full mask stack.
- Add dense exact-pixel and sparse CompactMask regression coverage that fails if to_dense() is called.
- Revert unrelated get_labels_text validation and its tests so this PR remains scoped to mask reduction.

Impact:
- CompactMask annotation avoids the prior O(N*H*W) memory regression while dense-mask behavior remains unchanged.
- The regression tests protect both rendered overlap semantics and the compact-memory contract.
- No new label-helper exception behavior or changelog entry is introduced.

Verification:
- .venv/bin/python -m pytest -q tests/annotators/test_core.py tests/annotators/test_utils.py — 236 passed.
- ruff check, ruff format --check, and git diff --check — passed.
- Raw mypy remains failed with 90 repository/environment-wide errors; changed modules retain pre-existing missing-stub errors only.
- Canonical pytest gate skipped because external pytest-rerunfailures attempts a sandbox-forbidden localhost bind.

Residual limits:
- Production CompactMask RSS, fresh remote Codecov, supported-environment mypy/pytest, and runtime provenance evidence remain unresolved.

---

Co-authored-by: Codex <codex@openai.com>
@Borda

Borda commented Aug 21, 2026

Copy link
Copy Markdown
Member

@shaoming11, could you pls add some benchmark comparisons before and after?

@Borda
Borda self-requested a review August 21, 2026 18:41
@Borda
Borda marked this pull request as draft August 21, 2026 22:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants