fix: is_empty() returns False for empty tracker arrays#2209
fix: is_empty() returns False for empty tracker arrays#2209Borda merged 8 commits intoroboflow:developfrom
Conversation
…ow#2195) Previously, is_empty() compared self to Detections.empty() via __eq__, which uses np.array_equal. When tracker_id is an empty numpy array (rather than None), np.array_equal(np.array([]), None) returns False, causing is_empty() to incorrectly return False on genuinely empty Detections objects. Fix: replace the equality-comparison approach with a direct len() check on self.xyxy, which is the canonical source of truth for detection count. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes sv.Detections.is_empty() incorrectly returning False for filtered-to-empty detections when tracker_id is an empty NumPy array (instead of None), aligning emptiness with the canonical detection count (xyxy length).
Changes:
- Reimplemented
Detections.is_empty()to uselen(self.xyxy) == 0rather than equality vsDetections.empty(). - Added parametrized regression tests covering empty/non-empty cases including the
tracker_idfiltered-to-empty scenario. - Expanded the
is_empty()docstring to describe semantics and provide an example.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/supervision/detection/core.py |
Updates is_empty() implementation and docstring to define emptiness by xyxy length. |
tests/detection/test_core.py |
Adds regression coverage for is_empty() including the tracker_id empty-array case. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- Case 4: [TEST_DET_NONE] now returns Detections.empty() — zero-length xyxy is empty - Case 8: [TEST_DET_ZERO_LENGTH x2] now returns Detections.empty() — same reason - Case 9: [TEST_DET_1, TEST_DET_NONE] now returns TEST_DET_1 with DoesNotRaise() — the ValueError was an artifact of the bug; empty operand is now stripped before merge's field-compatibility check, so merge succeeds cleanly [resolve roboflow#2] /review finding by sw-engineer + qa-specialist (report: .temp/output-review-fix-is-empty-tracker-id-2026-04-13.md) [resolve roboflow#4] /review finding by sw-engineer (report: .temp/output-review-fix-is-empty-tracker-id-2026-04-13.md) --- Co-authored-by: Claude Code <noreply@anthropic.com>
- Add filtered_empty_with_mask parametrize case (same bug applies to mask field) - Add explicit ids= for readable pytest output - Add one-line docstring to test function [resolve roboflow#5] /review finding by qa-specialist (report: .temp/output-review-fix-is-empty-tracker-id-2026-04-13.md) [resolve roboflow#6] /review finding by qa-specialist (report: .temp/output-review-fix-is-empty-tracker-id-2026-04-13.md) --- Co-authored-by: Claude Code <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #2209 +/- ##
=======================================
- Coverage 78% 78% -0%
=======================================
Files 63 63
Lines 7977 7972 -5
=======================================
- Hits 6253 6248 -5
Misses 1724 1724 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Fixes sv.Detections.is_empty() incorrectly returning False for “filtered-to-empty” detections when optional fields (e.g., tracker_id, mask) are present as empty arrays instead of None, by making emptiness depend solely on detection count (xyxy length).
Changes:
- Simplified
Detections.is_empty()toreturn len(self.xyxy) == 0. - Updated merge-related expectations in
tests/detection/test_core.pynow that truly empty detections are correctly recognized and ignored byDetections.merge. - Added parametrized regression tests for
is_empty()covering thetracker_idempty-array case (and a similarmaskcase).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/supervision/detection/core.py |
Replaces equality-based emptiness detection with an xyxy length check and expands the docstring. |
tests/detection/test_core.py |
Adds regression tests for is_empty() and updates merge test expectations to align with corrected emptiness handling. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
| Detection type | xyxy always populated? |
is_empty() correct? |
|---|---|---|
| Bounding box | Yes — required field | ✓ |
| OBB | Yes — axis-aligned wrapper always set | ✓ |
| Segmentation (dense mask) | Yes — computed via mask_to_xyxy() |
✓ |
| Segmentation (CompactMask) | Yes — same, never materialized | ✓ + O(1) |
| VLM outputs (polygons, etc.) | Yes — connectors compute xyxy first | ✓ |
Empty (cls.empty()) |
Yes — (0, 4) shape |
✓ |
KeyPoints |
N/A — separate class | ⚠ same old bug, unfixed |
2a76c30 to
8244808
Compare
Summary
Fixes #2195.
is_empty()incorrectly returnedFalseon genuinely emptyDetectionsobjects whentracker_idwas an empty numpy array rather thanNone.Root cause: The previous implementation compared
selfagainstDetections.empty()via__eq__, which usesnp.array_equal. When aDetectionsobject withtracker_idset is filtered to zero results,tracker_idbecomesnp.array([])(notNone).np.array_equal(np.array([]), None)returnsFalse, so the equality check failed andis_empty()returnedFalse.Fix: Replace the comparison-based approach with a direct length check on
self.xyxy, which is the canonical source of truth for detection count:Test plan
tests/detection/test_core.py::test_is_emptyDetections.empty()→Truetracker_id→Falsetracker_id(the regression case) →TrueFalseuv run python -m pytest tests/detection/test_core.py::test_is_empty -v🤖 Generated with Claude Code