Skip to content

refactor: centralize geometry-aware dispatch for detection_area and detection_iou#2374

Open
mvanhorn wants to merge 2 commits into
roboflow:developfrom
mvanhorn:fix/2318-geometry-aware-dispatch
Open

refactor: centralize geometry-aware dispatch for detection_area and detection_iou#2374
mvanhorn wants to merge 2 commits into
roboflow:developfrom
mvanhorn:fix/2318-geometry-aware-dispatch

Conversation

@mvanhorn

Copy link
Copy Markdown
Before submitting
  • Self-reviewed the code
  • Updated documentation, follow Google-style
  • Added/updated tests

Description

Centralizes the "which geometry is the real one?" dispatch for detection area and IoU into a single internal module so the mask -> OBB -> box precedence lives in one place instead of being re-implemented (and silently forgotten) at each call site.

Type of Change

  • 🔨 Refactoring (no functional changes)

Motivation and Context

A recurring "silent AABB" bug class keeps reappearing: code reaches for detections.xyxy and forgets the real geometry lives in data["xyxyxyxy"] (OBB) or mask. Because xyxy is always present, the axis-aligned answer is the silent default. Recent fixes (#2306 area returning AABB area, #2303 with_nms/with_nmm dropping crossed OBBs via AABB IoU, #2289 as_yolo dropping OBB rotation) share this root cause: every consumer re-derives geometry precedence and forgetting it fails silently.

@kounelisagis endorsed (2026-06-17) keeping the policy internal ("exact when richer geometry is present") behind a single geometry-resolution helper rather than a public per-op mode argument, and suggested a metamorphic test.

This PR is a deliberate first slice of the multi-part issue: the read-only dispatch helpers plus the contract test land first. move_detections consolidation and the with_nms/with_nmm/as_yolo call-site migration are left as independent follow-ups per the issue's incremental rollout.

Refs #2318

Changes Made

  • Add internal src/supervision/detection/utils/geometry.py with two batched, read-only helpers: detection_area (mask sum / CompactMask.area -> shoelace OBB area -> box_area) and detection_iou (mask_iou_batch -> oriented_box_iou_batch -> box_iou_batch, by the richest geometry both operands carry).
  • Route Detections.area in core.py through detection_area so the property is a thin call into the shared helper, with no behavior change and the vectorized design preserved.
  • Add tests/detection/utils/test_geometry.py: area-parity across mask/OBB/AABB plus the metamorphic contract (identical envelopes with different OBB corners must differ; rotating an OBB off-axis leaves detection_area invariant while the envelope area changes).

Testing

Added tests/detection/utils/test_geometry.py covering area parity (mask/OBB/AABB), the metamorphic rotation-invariance contract, empty Detections, CompactMask routing, and the AABB IoU fallback. The helpers are read-only and preserve Detections.area behavior exactly.

Note: I was unable to run the full suite in my local sandbox (the test stack pulls cv2/matplotlib, which aren't installed in my environment); the new tests are written to run under the repo's CI. Happy to adjust if CI surfaces anything.

Google Colab (optional)

N/A

Screenshots/Videos (optional)

N/A (internal refactor; no user-visible output change)

Additional Notes

The new helpers are intentionally module-private (no public API surface, no per-op mode argument) to match the maintainer's stated preference. Follow-ups can migrate with_nms/with_nmm/as_yolo and consolidate move_detections onto the same dispatch.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

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 PR centralizes geometry-precedence dispatch (mask → OBB → AABB) for per-detection area and pairwise IoU into a single internal helper module, then routes Detections.area through that shared implementation to reduce recurring “silent AABB fallback” regressions.

Changes:

  • Added internal detection_area(...) and detection_iou(...) helpers under src/supervision/detection/utils/geometry.py.
  • Refactored Detections.area to delegate to detection_area (removing duplicated dispatch logic in core.py).
  • Added contract-style tests ensuring area/IoU respect richer geometry and catch envelope-vs-OBB regressions.

Review scores (1–5):

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

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/supervision/detection/utils/geometry.py Introduces centralized geometry-aware dispatch helpers for area and IoU.
src/supervision/detection/core.py Refactors Detections.area to call the shared helper.
tests/detection/utils/test_geometry.py Adds parity + metamorphic/contract tests for dispatch correctness across mask/OBB/AABB.

Comment on lines +39 to +42
if detections.mask is not None:
if isinstance(detections.mask, CompactMask):
return detections.mask.area
return np.array([np.sum(mask) for mask in detections.mask])
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.

3 participants