fix(annotators): clip CropAnnotator boxes to the scene before cropping - #2391
Merged
Borda merged 1 commit intoJul 3, 2026
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #2391 +/- ##
=======================================
Coverage 84% 84%
=======================================
Files 70 70
Lines 10004 10009 +5
=======================================
+ Hits 8436 8442 +6
+ Misses 1568 1567 -1 🚀 New features to boost your workflow:
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes CropAnnotator.annotate crashing when detections contain boxes that touch/cross the scene border (negative coords, fully out-of-frame, or degenerate/zero-area boxes). It aligns CropAnnotator with sibling annotators that already clamp boxes before NumPy slicing, preventing cv2.resize from receiving empty crops.
Changes:
- Clip
detections.xyxyto the scene bounds via existingclip_boxes(...)before cropping. - Skip detections that become empty after clipping (zero width/height) to avoid
cv2.error. - Add regression tests covering border-crossing, fully-outside/degenerate, and mixed valid+degenerate cases.
Assessment (n/5):
- Code quality: 5/5
- Testing: 5/5
- Docs: 5/5 (no public API change; existing docstrings remain applicable)
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/supervision/annotators/core.py |
Clamp crop regions to the scene and skip degenerate boxes so annotation can’t crash on out-of-bounds slices. |
tests/annotators/test_core.py |
Adds coverage to ensure border/degenerate boxes don’t raise and are skipped appropriately. |
Contributor
Author
Borda
approved these changes
Jul 3, 2026
Borda
added a commit
that referenced
this pull request
Jul 3, 2026
- Keep .astype(np.int32) over .astype(int) — annotation declares npt.NDArray[np.int32] - Keep crop_x1/crop_x2 naming over x_min/x_max — cleaner degenerate-box guard - Merge all TestCropAnnotator tests from both branches — deprecation warning tests (from fix/annot) complement parametrized OOB coverage (from PR #2391) - Preserve TestIconAnnotator class introduced in fix/annot --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Borda
added a commit
that referenced
this pull request
Jul 3, 2026
Resolve duplicate-change conflicts: several commits from this branch were cherry-picked into separate PRs (#2391/#2393/#2394/#2396/#2397/#2398/#2399) and squash-merged to develop, so merging develop back produced same-change conflicts across 19 files. Resolution: - Keep this branch's later refactors: public `check_no_basename_collisions` (standardized collision-guard layer, renamed all `_check_*` call sites in dataset/core, yolo, labelme, tests), `save_pascal_voc_annotations` helper, value-based from_lmm->from_vlm mapping (drop dead lmm_to_vlm dict), CropAnnotator source-scene snapshot for overlapping boxes. - Take develop's hardening/refinements: from_vlm empty-list guard, explicit float() mAP casts, np.float32 dtypes, MJPG/avi VideoSink tests, shared make_panoptic_png test helper, expanded empty-input adapter tests. - Combine where both improve: np.int32 crop dtype + snapshot; keep both sides' added tests. Full suite: 2924 passed, 1 xfailed. pre-commit (ruff, mypy, codespell) clean. --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Draft
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Before submitting
(https://google.github.io/styleguide/pyguide.html#383-functions-and-methods)
Description
CropAnnotator.annotate passed raw detection coordinates into crop_image, which
slices with plain numpy indexing. A negative x_min or y_min wraps around and
produces an empty crop, and scale_image then feeds the zero-size image to
cv2.resize, which raises cv2.error. Boxes fully outside the frame and
zero-width or zero-area boxes crash the same way, so one degenerate detection
aborts annotation of the whole frame.
This PR clips boxes to the scene with the existing clip_boxes helper,
mirroring BlurAnnotator and PixelateAnnotator, and skips boxes that are empty
after clipping. Skipped boxes keep their detection index, so color lookup
stays aligned with the original detections.
For every input the old code handled without crashing, the output is
pixel-identical (verified byte-for-byte for in-bounds and
right/bottom-overshoot boxes). The only behavior change besides removing the
crash: a fully negative box such as [-50, -50, -10, -10] previously wrapped
around and silently pasted a crop of an unrelated image region; it is now
skipped.
Type of Change
Motivation and Context
Boxes at or beyond the frame border arise from supported workflows:
sv.pad_boxes produces negative coordinates for boxes near the frame edge, the
from_vlm connectors pass model coordinates through unclamped, and boxes land
fully outside the frame when detections computed at one resolution are
annotated onto another. The sibling annotators that slice the scene the same
way (BlurAnnotator, PixelateAnnotator) already clip; CropAnnotator was the
only one that did not.
Closes #2390
Changes Made
clip_boxes helper before cropping
empty crops to cv2.resize
longer prevents valid boxes from being drawn
four degenerate boxes are skipped leaving the scene unchanged, and a mixed
valid/degenerate case still draws the valid box
Testing
feature works
The new tests fail on current develop with cv2.error (7 failing cases) and
pass with the fix (13/13 in TestCropAnnotator). Full suite: 2459 passed, 1
skipped, including under pytest --cov=supervision.
Reproduce the original crash on develop:
Google Colab (optional)
Colab link: N/A
Screenshots/Videos (optional)
N/A
Additional Notes
No public API changes and no new dependencies. crop_image itself still returns
a silently empty array for out-of-bounds input; clamping it was left out of
scope to keep this PR minimal, since callers other than CropAnnotator already
clip.