Files
wehub-resource-sync 9194ef5abd
Docs/Test Workflow / Test docs build (push) Failing after 0s
Check links & references / links-check (push) Failing after 1s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.10) (push) Failing after 0s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.11) (push) Failing after 0s
PR Conflict Labeler / main (push) Failing after 2s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.12) (push) Failing after 2s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.13) (push) Failing after 0s
Pytest/Test Workflow / Build this Package (push) Failing after 5s
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.10) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.11) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.12) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.13) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.10) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.11) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.12) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.13) (push) Has been cancelled
Pytest/Test Workflow / testing-guardian (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:06:10 +08:00

8.0 KiB
Raw Permalink Blame History

comments, description, authors, date_modified
comments description authors date_modified
true Use CompactMask for memory-efficient instance segmentation in supervision — ingest COCO RLE payloads, skip mask materialisation, and merge mixed dense and compact detections without allocating a full pixel stack.
name role github
Borda Open Source Engineer, Roboflow https://github.com/borda
2026-07-01

Use Compact Masks for Memory-Efficient Segmentation

[CompactMask][supervision.detection.compact_mask.CompactMask] stores each instance mask as a run-length encoding of its bounding-box crop rather than a full (H, W) boolean frame. For high-resolution images with many sparse masks this can reduce memory from tens of gigabytes to tens of megabytes, and eliminates full-frame decode work in annotators that only need the cropped region.

!!! Note

`sv.mask_to_xyxy` keeps supervision's inclusive max-coordinate convention for compatibility with `CompactMask` and current box-based adapters. Use `sv.mask_to_roi` when you need exclusive slice bounds for NumPy indexing or crop extraction.

This guide covers the four main integration points:

  1. Ingesting COCO RLE payloads directly as CompactMask
  2. Parsing Roboflow Inference results without a dense stack
  3. Skipping mask materialisation for box/label annotators
  4. Merging mixed dense and compact detections

Ingest COCO RLE Payloads

If your model or API returns masks in the COCO RLE format ({"size": [H, W], "counts": "..."}) you can convert them directly to CompactMask without allocating an (N, H, W) boolean array:

import numpy as np
import supervision as sv
from supervision.detection.compact_mask import CompactMask

# Example: two COCO RLE masks for a 720×1280 frame.
# Replace the counts strings with actual compressed RLE payloads from your
# model or API — e.g., from pycocotools mask.encode() or an Inference response.
rles = [
    {"size": [720, 1280], "counts": "YOUR_RLE_COUNTS_STRING_HERE"},
    {"size": [720, 1280], "counts": "YOUR_RLE_COUNTS_STRING_HERE"},
]
xyxy = np.array(
    [
        [100.0, 50.0, 400.0, 300.0],
        [500.0, 200.0, 900.0, 600.0],
    ]
)

compact = CompactMask.from_coco_rle(rles, xyxy, image_shape=(720, 1280))

detections = sv.Detections(
    xyxy=xyxy,
    mask=compact,
    class_id=np.array([0, 1]),
)

from_coco_rle uses run-length arithmetic scoped to each bounding box so no dense pixel array is ever created. Uncompressed integer count lists are also accepted in place of compressed strings.


Parse Inference Results

Detections.from_inference accepts a compact_masks=True flag that routes the Roboflow RLE payload through CompactMask.from_coco_rle instead of decoding to a dense stack:

import supervision as sv

# result: a Roboflow Inference v2 response dict with instance masks.
detections = sv.Detections.from_inference(result, compact_masks=True)

from supervision.detection.compact_mask import CompactMask

assert isinstance(detections.mask, CompactMask)

!!! Warning

`compact_masks=True` crops each mask to its detector bounding box. Pixels outside the box are silently dropped. For masks that extend meaningfully beyond the reported bounding box, use the default `compact_masks=False` (dense decode) to preserve all pixels.

To convert an existing dense-mask Detections to compact at any point:

detections_compact = detections.to_compact_masks()

Skip Unnecessary Materialisation

Annotators that do not draw masks (box, label, circle, ellipse, trace, keypoint) expose requires_mask = False. Integrations can branch on this flag to avoid decoding compact or RLE masks before annotation:

import supervision as sv

annotators = [
    sv.BoxAnnotator(),
    sv.LabelAnnotator(),
    sv.MaskAnnotator(),  # requires_mask = True
]

for ann in annotators:
    if ann.requires_mask:
        # Annotator reads mask pixels — CompactMask decodes lazily per crop.
        scene = ann.annotate(scene, detections)
    else:
        # Annotator ignores masks — strip mask field to eliminate any decode cost.
        det_no_mask = sv.Detections(
            xyxy=detections.xyxy,
            confidence=detections.confidence,
            class_id=detections.class_id,
        )
        scene = ann.annotate(scene, det_no_mask)

Annotators that set requires_mask = True: [MaskAnnotator][supervision.annotators.core.MaskAnnotator], [PolygonAnnotator][supervision.annotators.core.PolygonAnnotator], [HaloAnnotator][supervision.annotators.core.HaloAnnotator].

All others default to requires_mask = False.

!!! Note

`PolygonAnnotator` and `MaskAnnotator` both operate directly on `CompactMask` without materialising the full `(N, H, W)` frame — passing compact detections to them is already efficient.

Merge Mixed Detections

When merging Detections objects that mix dense ndarray masks and CompactMask instances, Detections.merge converts dense inputs to CompactMask automatically. No full (N, H, W) stack is allocated:

import numpy as np
import supervision as sv
from supervision.detection.compact_mask import CompactMask

H, W = 720, 1280

# Compact detections from an RLE-based source.
# Replace the counts string with a real compressed RLE payload from your model or API.
rles = [{"size": [H, W], "counts": "YOUR_RLE_COUNTS_STRING_HERE"}]
xyxy_a = np.array([[100.0, 50.0, 400.0, 300.0]])
cm = CompactMask.from_coco_rle(rles, xyxy_a, image_shape=(H, W))
det_a = sv.Detections(xyxy=xyxy_a, mask=cm, class_id=np.array([0]))

# Dense detections from a different source.
masks_b = np.zeros((1, H, W), dtype=bool)
masks_b[0, 200:400, 500:800] = True
xyxy_b = np.array([[500.0, 200.0, 799.0, 399.0]])
det_b = sv.Detections(xyxy=xyxy_b, mask=masks_b, class_id=np.array([1]))

# Output is CompactMask regardless of input order.
merged = sv.Detections.merge([det_a, det_b])
assert isinstance(merged.mask, CompactMask)
assert len(merged) == 2

Merge rules:

Inputs Output mask type
All CompactMask CompactMask
Mixed CompactMask + dense ndarray CompactMask
All dense ndarray ndarray (backward compatible)

All CompactMask inputs must share the same image_shape; mismatches raise ValueError.


Performance Notes

These estimates apply to the parsing and annotation stage, not end-to-end pipeline FPS. Model inference typically dominates total runtime.

Optimisation Realistic gain Applies when
from_coco_rle ingestion 2560% faster parse Full-frame COCO RLE payload; current dense decode path
MaskAnnotator ROI blending 1035% faster annotation Many small, sparse masks on high-res frames
PolygonAnnotator crop path 1545% faster polygon draw Many compact masks; full-frame materialise was the bottleneck
Mixed-mask merge 520% faster merge Mix of compact and dense sources (e.g. multi-camera stitch)

Upper-end gains assume: ≥1080p frames, tens to hundreds of instances, masks covering less than ~20% of total pixels.


API Reference

  • [CompactMask][supervision.detection.compact_mask.CompactMask]
  • [CompactMask.from_coco_rle][supervision.detection.compact_mask.CompactMask.from_coco_rle]
  • [CompactMask.from_dense][supervision.detection.compact_mask.CompactMask.from_dense]
  • [Detections.from_inference][supervision.detection.core.Detections.from_inference]
  • [Detections.to_compact_masks][supervision.detection.core.Detections.to_compact_masks]
  • [Detections.merge][supervision.detection.core.Detections.merge]
  • [BaseAnnotator.requires_mask][supervision.annotators.base.BaseAnnotator]