Skip to content

Optimize mask annotation ROI blending - #2368

Merged
Borda merged 12 commits into
developfrom
perf-CMask/2
Jul 1, 2026
Merged

Optimize mask annotation ROI blending#2368
Borda merged 12 commits into
developfrom
perf-CMask/2

Conversation

@Borda

@Borda Borda commented Jun 29, 2026

Copy link
Copy Markdown
Member

This pull request introduces a significant optimization to the MaskAnnotator class, ensuring that mask blending is limited to only the region of interest (ROI) touched by the mask(s), rather than always blending the full image. This change improves annotation speed, especially for large images with sparse masks, and reduces unnecessary computation. The update includes supporting utility functions, test coverage for the new behavior, and improved documentation and reporting in the benchmark script.

Mask annotation ROI optimization:

  • Updated MaskAnnotator.annotate to blend only the minimal bounding rectangle (ROI) covering all true mask pixels, both for dense and compact masks, instead of the entire image. This is achieved by computing the ROI with new helper functions and passing the ROI to the blending operation.
  • Refactored the internal _paint_masks_by_area function to support painting into a subregion of the canvas with a new canvas_origin parameter, aligning mask coordinates with the ROI. [1] [2] [3] [4]

Utility functions for ROI calculation:

  • Added _mask_to_roi, _compact_masks_to_roi, and _masks_to_roi functions to compute the minimal bounding box for true mask pixels in dense and compact masks.

Testing:

  • Added new tests in tests/annotators/test_core.py to verify that blending occurs only within the mask ROI for both dense and compact masks, and that no blending occurs when masks are all-false.

Documentation and reporting:

  • Improved docstrings and comments to clarify the new ROI-based blending logic.
  • Updated the benchmark script and table output to clarify that annotation speedup is due to ROI-only blending, and improved column naming for clarity. [1] [2] [3] [4]

Blend MaskAnnotator overlays only within the touched mask ROI while preserving dense and CompactMask rendering semantics.

Add ROI coverage tests for dense masks, CompactMask masks, and all-false masks, and clarify the compact-mask benchmark annotation speedup output.

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

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.55172% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 84%. Comparing base (098f531) to head (11bbe86).

Additional details and impacted files
@@           Coverage Diff           @@
##           develop   #2368   +/-   ##
=======================================
  Coverage       84%     84%           
=======================================
  Files           70      70           
  Lines         9834    9887   +53     
=======================================
+ Hits          8219    8270   +51     
- Misses        1615    1617    +2     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

This pull request optimizes MaskAnnotator.annotate by limiting blending work (cv2.addWeighted) to the minimal region-of-interest (ROI) that actually contains mask pixels, reducing unnecessary full-frame blending for sparse masks. It also extends the shared mask-painting utility to support painting into a sub-canvas with an absolute origin, and adds tests/benchmark wording to validate and explain the new behavior.

Changes:

  • Compute a minimal ROI for dense and CompactMask masks and blend only that ROI in MaskAnnotator.annotate.
  • Extend _paint_masks_by_area(...) with a canvas_origin parameter so masks can be painted correctly into an ROI-sized canvas.
  • Add tests asserting blending happens only on the ROI (and is skipped for all-false masks), and clarify benchmark reporting text/labels.

Review scores (n/5):

  • Code quality: 5/5
  • Testing: 5/5
  • Documentation/clarity: 5/5

Reviewed changes

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

File Description
src/supervision/annotators/core.py Adds ROI computation helpers, supports sub-canvas mask painting via canvas_origin, and blends only the ROI in MaskAnnotator.
tests/annotators/test_core.py Adds regression tests verifying ROI-only blending for dense/compact masks and skipping blending for all-false masks.
examples/compact_mask/benchmark.py Updates benchmark docstring/table labels to better attribute annotation speedups to ROI-only blending.

@Borda Borda added the enhancement New feature or request label Jun 29, 2026
@Borda Borda self-assigned this Jul 1, 2026
Borda and others added 2 commits July 1, 2026 14:37
- fix: use scene_roi.copy() in cv2.addWeighted to avoid non-contiguous
  stride rounding divergence (±1 uint8 mismatch on column-cropped views)
- fix: validate color lookup config before all-false early return via
  resolve_color pre-check; expose misconfigured annotators at call time
- perf: pass detections.xyxy to _masks_to_roi for dense path; converts
  O(N·H·W) pixel scan to O(N) box union; uses floor(x2)+1 inclusive→
  exclusive to match CompactMask.bbox_xyxy + 1 convention
- refactor: move _mask_to_roi, _compact_masks_to_roi, _masks_to_roi from
  annotators/core.py to detection/utils/masks.py (pure geometry helpers)
- perf: replace per-detection RLE decode loop in _compact_masks_to_roi
  with vectorized bbox_xyxy read (no decode required)
- docs: add Args/Returns/Notes to all three ROI helpers; document
  image_shape (height, width) convention and exclusive-bounds semantics;
  clarify _paint_masks_by_area Returns uses canvas dims not image dims

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- add TestMaskROIHelpers class: 11 parametrized unit tests covering
  _mask_to_roi (all-false, single-pixel, boundary, full-image, region),
  _compact_masks_to_roi (empty, single, union coordinates), and
  _masks_to_roi (empty array, 2D input, dense+xyxy box-union path)
- add test_annotate_pixels_outside_roi_unchanged: asserts pixels strictly
  outside ROI bounds are identical to original scene after annotate
- add test_annotate_roi_parity_with_full_frame_blend: verifies masked
  pixels match manual reference blend within ±1 uint8 rounding tolerance
- add TestPaintMasksByArea::test_canvas_origin_nonzero_paints_at_correct_position
- update test_annotate_blends_only_dense_mask_roi: switch to two-detection
  setup with tight inclusive xyxy so blended shape assertion remains valid
- update test_annotate_skips_all_false_mask_blend: remove blended_shapes
  assertion (xyxy now drives a non-None ROI; blend is a no-op since no
  pixels are painted, scene remains pixel-exact)

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@Borda
Borda requested a review from Copilot July 1, 2026 12:55
Comment thread src/supervision/annotators/core.py Outdated
Co-authored-by: Jirka Borovec <6035284+Borda@users.noreply.github.com>

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

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

Comment thread src/supervision/detection/utils/masks.py
Comment thread tests/annotators/test_core.py
Comment thread tests/annotators/test_core.py Outdated
Borda and others added 4 commits July 1, 2026 15:12
…t_masks.py

Mirrors source location of _mask_to_roi, _compact_masks_to_roi,
_masks_to_roi (detection/utils/masks.py). Removes dead try/except
import fallback from test_core.py.

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

Previously the xyxy fast-path returned a box-union ROI even when every
mask pixel was False (all-empty masks). This caused MaskAnnotator to
call cv2.addWeighted on an ROI unnecessarily. Merge the empty-array
guard with an any() check so both paths return None early.

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Fix test_annotate_skips_all_false_mask_blend to assert cv2.addWeighted
is never called (not just that pixels are unchanged) — pixel-equality
alone passes even when addWeighted is invoked on identical arrays.

Add two cases to TestMaskROIHelpers covering the xyxy fast-path:
loose-box (xyxy larger than true pixel region returns box union) and
all-false+xyxy (should return None after the fast-path guard fix).

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@Borda
Borda merged commit 3ecd5d0 into develop Jul 1, 2026
26 checks passed
@Borda
Borda deleted the perf-CMask/2 branch July 1, 2026 13:55
@Borda Borda mentioned this pull request Jul 29, 2026
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.

2 participants