Perf: vectorize _get_pairwise_iou with a joint bincount#9004
Conversation
Replace the nested per-pair loop in _get_pairwise_iou, which materialized one full-volume mask per true and per predicted instance and computed full-volume sum operations for every candidate pair, with a single joint bincount over gt * (num_pred + 1) + pred. This counts all pairwise intersections and per-instance areas in one O(volume) pass and derives IoU as inter / (area_t + area_p - inter), the same integer-count formula the loop used. Instance ids are contiguous after remap_instance_id, which both the old loop (indexing mask lists by id) and this vectorized form rely on. Numerics are bit-identical to the previous implementation and peak memory is lower, since only a few bincount vectors and one (T+1)x(P+1) matrix are allocated instead of T+P full-volume masks held simultaneously. Signed-off-by: Soumya Snigdha Kundu <soumya_snigdha.kundu@kcl.ac.uk>
📝 WalkthroughWalkthrough
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
monai/metrics/panoptic_quality.py (1)
259-264: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDocument the contiguous-ID precondition.
The bincount reshape relies on IDs being contiguous from zero; sparse IDs can produce an oversized bincount and fail the reshape. Add a Google-style docstring describing inputs, output, and this precondition.
As per path instructions, “Docstrings should be present for all definition” using Google-style sections.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@monai/metrics/panoptic_quality.py` around lines 259 - 264, Document the function containing the gt_flat/pred_flat intersection computation with a Google-style docstring covering its inputs, returned output, and the requirement that remap_instance_id produces contiguous IDs starting at zero for both ground truth and predictions. Ensure the docstring follows the project’s requirement that every definition has documentation.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@monai/metrics/panoptic_quality.py`:
- Around line 259-264: Document the function containing the gt_flat/pred_flat
intersection computation with a Google-style docstring covering its inputs,
returned output, and the requirement that remap_instance_id produces contiguous
IDs starting at zero for both ground truth and predictions. Ensure the docstring
follows the project’s requirement that every definition has documentation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 65a7dd41-409c-436e-8620-91cb89109f33
📒 Files selected for processing (2)
monai/metrics/panoptic_quality.pytests/metrics/test_compute_panoptic_quality.py
Description
Replaces the nested per-pair loop in
_get_pairwise_iou(which heldT + Pfull-volume masks liveand did
O(pairs * volume)work) with a single joint bincount overgt * (num_pred + 1) + pred,computing all pairwise intersections in one
O(volume)pass that is much faster and, for realisticinstance counts, lower in peak memory.
Benchmark
Synthetic nuclei-style masks (random blobs), CPU,
OMP_NUM_THREADS=8, median of repeated runs.2D
3D
Numerics are bit-identical (
torch.equal) at every size.System: 12th Gen Intel Core i7-12800H (20 threads,
OMP_NUM_THREADS=8); Linux 6.8.0-124-genericx86_64, glibc 2.35; Python 3.10.12; PyTorch 2.12.1+cu130. Metric runs on CPU.
Types of changes
black/isort/ruff/mypy.