{"record":{"id":"5c0994ad122c9ae6","repo":"roboflow/supervision","slug":"masks-true-and-masks-detection-must-be-3d-n-h-w","errorCode":null,"errorMessage":"masks_true and masks_detection must be 3D (N, H, W); got ndim={masks_true.ndim} and ndim={masks_detection.ndim}.","messagePattern":"masks_true and masks_detection must be 3D \\(N, H, W\\); got ndim=(.+?) and ndim=(.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/detection/utils/iou_and_nms.py","lineNumber":848,"sourceCode":"        >>> masks_detection = np.zeros((1, 4, 4), dtype=bool)\n        >>> masks_detection[:, :3, :3] = True\n        >>> sv.mask_iou_batch(masks_true, masks_detection)\n        array([[0.44444445]])\n\n        ```\n    \"\"\"\n\n    if isinstance(masks_true, CompactMask) and isinstance(masks_detection, CompactMask):\n        return compact_mask_iou_batch(masks_true, masks_detection, overlap_metric)\n\n    # Materialise any CompactMask that was passed alongside a dense array.\n    if isinstance(masks_true, CompactMask):\n        masks_true = np.asarray(masks_true)\n    if isinstance(masks_detection, CompactMask):\n        masks_detection = np.asarray(masks_detection)\n\n    if masks_true.ndim != 3 or masks_detection.ndim != 3:\n        raise ValueError(\n            \"masks_true and masks_detection must be 3D (N, H, W); got \"\n            f\"ndim={masks_true.ndim} and ndim={masks_detection.ndim}.\"\n        )\n    if masks_true.shape[1:] != masks_detection.shape[1:]:\n        raise ValueError(\n            \"masks_true and masks_detection must share the same (H, W); got \"\n            f\"{masks_true.shape[1:]} and {masks_detection.shape[1:]}.\"\n        )\n    # A single pass already handles empty inputs and avoids np.vstack([]) below.\n    if masks_true.shape[0] == 0 or masks_detection.shape[0] == 0:\n        return _mask_iou_batch_split(masks_true, masks_detection, overlap_metric)\n\n    # Peak memory of a single matmul pass: the flattened detection masks (shared\n    # across chunks) plus, per true-mask row, its flattened pixels and the three\n    # (N, M) matrices it touches (intersection, denominator and output). The\n    # previous (N, M, H, W) estimate overcounted by a factor of M and forced\n    # needless chunking now that the intersection is a matmul.\n    pixels = masks_true.shape[1] * masks_true.shape[2]","sourceCodeStart":830,"sourceCodeEnd":866,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/detection/utils/iou_and_nms.py#L830-L866","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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]"],"exampleFix":"# before\niou = sv.mask_iou_batch(single_mask, other_batch)  # single_mask is (H, W)\n# after\niou = sv.mask_iou_batch(single_mask[np.newaxis, ...], other_batch)  # (1, H, W)","handlingStrategy":"type-guard","validationCode":"import numpy as np\n\ndef as_mask_batch(masks):\n    arr = np.asarray(masks)\n    if arr.ndim == 2:\n        arr = arr[np.newaxis, ...]\n    assert arr.ndim == 3, f\"masks must be (N, H, W), got {arr.shape}\"\n    return arr","typeGuard":"def is_mask_batch(masks) -> bool:\n    import numpy as np\n    return np.asarray(masks).ndim == 3","tryCatchPattern":null,"preventionTips":["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"],"tags":["masks","iou","shape-mismatch","validation"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}