roboflow/supervision · error · ValueError
masks_true and masks_detection must be 3D (N, H, W); got ndi
Error message
masks_true and masks_detection must be 3D (N, H, W); got ndim={masks_true.ndim} and ndim={masks_detection.ndim}. What it means
Raised by sv.mask_iou_batch when either masks_true or masks_detection is not a 3-D array of shape (N, H, W). The function computes pairwise mask overlap, so each input must be a stack of N binary masks; 2-D single masks or flattened representations are rejected. CompactMask inputs are materialized to dense arrays before this check, so a CompactMask encoding a non-3-D shape also lands here.
Source
Thrown at src/supervision/detection/utils/iou_and_nms.py:848
>>> masks_detection = np.zeros((1, 4, 4), dtype=bool)
>>> masks_detection[:, :3, :3] = True
>>> sv.mask_iou_batch(masks_true, masks_detection)
array([[0.44444445]])
```
"""
if isinstance(masks_true, CompactMask) and isinstance(masks_detection, CompactMask):
return compact_mask_iou_batch(masks_true, masks_detection, overlap_metric)
# Materialise any CompactMask that was passed alongside a dense array.
if isinstance(masks_true, CompactMask):
masks_true = np.asarray(masks_true)
if isinstance(masks_detection, CompactMask):
masks_detection = np.asarray(masks_detection)
if masks_true.ndim != 3 or masks_detection.ndim != 3:
raise ValueError(
"masks_true and masks_detection must be 3D (N, H, W); got "
f"ndim={masks_true.ndim} and ndim={masks_detection.ndim}."
)
if masks_true.shape[1:] != masks_detection.shape[1:]:
raise ValueError(
"masks_true and masks_detection must share the same (H, W); got "
f"{masks_true.shape[1:]} and {masks_detection.shape[1:]}."
)
# A single pass already handles empty inputs and avoids np.vstack([]) below.
if masks_true.shape[0] == 0 or masks_detection.shape[0] == 0:
return _mask_iou_batch_split(masks_true, masks_detection, overlap_metric)
# Peak memory of a single matmul pass: the flattened detection masks (shared
# across chunks) plus, per true-mask row, its flattened pixels and the three
# (N, M) matrices it touches (intersection, denominator and output). The
# previous (N, M, H, W) estimate overcounted by a factor of M and forced
# needless chunking now that the intersection is a matmul.
pixels = masks_true.shape[1] * masks_true.shape[2]View on GitHub (pinned to 7f254d9784)
Solutions
- Add the batch axis: mask = mask[np.newaxis, ...] for a single mask
- Reshape flattened data back: masks.reshape(n, h, w) before the call
- Index the batch, not a single mask: pass detections.mask (N,H,W), not detections.mask[0]
Example fix
# before iou = sv.mask_iou_batch(single_mask, other_batch) # single_mask is (H, W) # after iou = sv.mask_iou_batch(single_mask[np.newaxis, ...], other_batch) # (1, H, W)
Defensive patterns
Strategy: type-guard
Validate before calling
import numpy as np
def as_mask_batch(masks):
arr = np.asarray(masks)
if arr.ndim == 2:
arr = arr[np.newaxis, ...]
assert arr.ndim == 3, f"masks must be (N, H, W), got {arr.shape}"
return arr Type guard
def is_mask_batch(masks) -> bool:
import numpy as np
return np.asarray(masks).ndim == 3 Prevention
- Never pass detections.mask[i] (2-D) where a batch is expected — pass detections.mask
- Reshape flattened stored masks to (N, H, W) on load
- Wrap mask inputs in a as_mask_batch helper at your API boundary
When it happens
Trigger: sv.mask_iou_batch(mask_a, mask_b) where one argument is a single (H, W) mask (missing the batch axis) or a (N, H*W) flattened stack; also a CompactMask whose RLE decodes to 2-D.
Common situations: Feeding one segmentation mask from annotator/debug code where a batch is expected; flattening masks for storage/transport and forgetting to reshape to (N, H, W); mixing per-detection mask slices (detections.mask[i] is 2-D) with batch APIs.
Related errors
- `is_crowd` length ({len(is_crowd)}) must match `boxes_true`
- masks_true and masks_detection must share the same (H, W); g
- Incorrect connectivity value. Possible connectivity values:
- Either absolute_distance or relative_distance must be set.
- mode must be 'edge' or 'centroid'
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/5c0994ad122c9ae6.
Report an issue: GitHub.