roboflow/supervision · error · ValueError
Masks must be shaped (N, H, W)
Error message
Masks must be shaped (N, H, W)
What it means
Raised by get_mask_size_category() when the mask input is a plain ndarray that is not 3-D (N, H, W) — one binary mask per detection. The function counts True pixels per mask to derive areas; anything but a 3-D bool array (e.g. a single 2-D mask or a 4-D video tensor) breaks that per-instance counting. CompactMask inputs bypass this check because they carry their own area attribute.
Source
Thrown at src/supervision/metrics/utils/object_size.py:197
Example:
```pycon
>>> import numpy as np
>>> from supervision.metrics.utils.object_size import get_mask_size_category
>>> mask = np.zeros((3, 100, 100), dtype=bool)
>>> mask[0, 0:10, 0:10] = True # 100 (Small)
>>> mask[1, 0:50, 0:50] = True # 2500 (Medium)
>>> mask[2, 0:100, 0:100] = True # 10000 (Large)
>>> get_mask_size_category(mask)
array([1, 2, 3])
```
"""
if isinstance(mask, CompactMask):
areas = mask.area
else:
if len(mask.shape) != 3:
raise ValueError("Masks must be shaped (N, H, W)")
# count_mask_pixels uses np.count_nonzero (no axis), which dispatches
# to SIMD popcount over the bool buffer and is ~6x faster than the
# vectorized np.sum(mask, axis=(1, 2)). Do not "simplify" back to
# np.sum(axis=(1,2)); benchmark before reverting. dtype=np.int64 keeps
# areas consistent across platforms (Windows NumPy defaults to int32).
areas = count_mask_pixels(mask)
result = np.full(areas.shape, ObjectSizeCategory.ANY.value)
SM, LG = SIZE_THRESHOLDS
result[areas < SM] = ObjectSizeCategory.SMALL.value
result[(areas >= SM) & (areas < LG)] = ObjectSizeCategory.MEDIUM.value
result[areas >= LG] = ObjectSizeCategory.LARGE.value
return result
def get_obb_size_category(xyxyxyxy: npt.NDArray[np.number]) -> npt.NDArray[np.int_]:
"""
Get the size category of a oriented bounding boxes array.View on GitHub (pinned to 7f254d9784)
Solutions
- Stack per-instance masks: mask = np.stack(instance_masks) so shape is (N, H, W)
- For a single mask, wrap it: mask[None, :, :]
- Ensure boolean dtype and one channel per detection, not an RGB or label-map tensor
Example fix
# before mask = np.zeros((480, 640), dtype=bool) # single 2-D mask get_mask_size_category(mask) # after mask = np.zeros((1, 480, 640), dtype=bool) # (N=1, H, W) get_mask_size_category(mask)
Defensive patterns
Strategy: validation
Validate before calling
mask = np.asarray(mask)
if mask.ndim == 2:
mask = mask[None, :, :]
assert mask.ndim == 3, 'masks must be (N, H, W)'
cats = get_mask_size_category(mask.astype(bool)) Type guard
import numpy as np
def is_instance_masks(arr: np.ndarray) -> bool:
"""True when arr is an (N, H, W) mask stack."""
return arr.ndim == 3 Try / catch
try:
cats = get_mask_size_category(mask)
except ValueError as e:
if '(N, H, W)' in str(e):
cats = get_mask_size_category(np.asarray(mask)[None if np.asarray(mask).ndim == 2 else Ellipsis])
else:
raise Prevention
- Always np.stack() per-instance masks rather than passing one semantic mask
- Keep a shape assert in your segmentation post-processing: masks.ndim == 3
When it happens
Trigger: Calling get_mask_size_category with a single (H, W) mask, an (N, H, W, 3) array, or masks stacked along the wrong axis.
Common situations: Passing one full-image segmentation mask instead of per-detection instance masks; forgetting np.stack(masks_list) so a list or a 2-D array is passed; masks from a semantic segmentation model that outputs a single channel.
Related errors
- Bounding boxes must be shaped (N, 4)
- Areas must be shaped (N,)
- Oriented bounding boxes must be shaped (N, 4, 2)
- Detections mask is not available
- Value must be a np.ndarray or a list
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/be7328c6140fe517.
Report an issue: GitHub.