{"record":{"id":"9b43a315cec1ec56","repo":"roboflow/supervision","slug":"masks-true-and-masks-detection-must-share-the-same","errorCode":null,"errorMessage":"masks_true and masks_detection must share the same (H, W); got {masks_true.shape[1:]} and {masks_detection.shape[1:]}.","messagePattern":"masks_true and masks_detection must share the same \\(H, W\\); got (.+?) and (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/detection/utils/iou_and_nms.py","lineNumber":853,"sourceCode":"        ```\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]\n    itemsize = 4 if pixels <= 2**24 else 8\n    limit_bytes = memory_limit * 1024 * 1024\n    detection_bytes = masks_detection.shape[0] * pixels * itemsize\n    per_true_row = pixels * itemsize + 3 * masks_detection.shape[0] * 8\n    if detection_bytes > limit_bytes > 0:","sourceCodeStart":835,"sourceCodeEnd":871,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/detection/utils/iou_and_nms.py#L835-L871","documentation":"Raised by sv.mask_iou_batch when the two mask batches have matching ndim=3 but different spatial dimensions — masks_true.shape[1:] != masks_detection.shape[1:]. Pairwise overlap is only defined for masks on the same pixel grid, so differing (H, W) is rejected.","triggerScenarios":"sv.mask_iou_batch(masks_true, masks_detection) where ground truths are e.g. (N, 720, 1280) and detections (M, 1080, 1920), or any resize applied to one side only.","commonSituations":"Comparing model outputs against ground truth when the model resizes inputs internally and returns masks at a different resolution; mixing masks from two different cameras/resolutions; a preprocessing resize added to one branch of an evaluation script during refactoring.","solutions":["Resize one side to the other's (H, W) before calling: e.g. cv2.resize per mask or model postprocess that maps masks back to original image size","Ensure the model's mask postprocess returns masks at input-image resolution","Verify shape[1:] equality with an assert before evaluation loops to catch regressions early"],"exampleFix":"# before\nious = sv.mask_iou_batch(gt_masks, det_masks)  # (N,720,1280) vs (M,1080,1920)\n# after\nimport cv2\ndet_masks = np.stack([cv2.resize(m, (gt_masks.shape[2], gt_masks.shape[1]), interpolation=cv2.INTER_NEAREST) for m in det_masks])\nious = sv.mask_iou_batch(gt_masks, det_masks)","handlingStrategy":"validation","validationCode":"import numpy as np\n\ndef assert_same_hw(masks_true, masks_detection):\n    assert masks_true.ndim == 3 and masks_detection.ndim == 3\n    assert masks_true.shape[1:] == masks_detection.shape[1:], (\n        f\"spatial mismatch: {masks_true.shape[1:]} vs {masks_detection.shape[1:]}\"\n    )","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Resize prediction masks back to source resolution in model postprocess","Assert shape[1:] equality once before evaluation loops, not per pair","Keep one canonical (H, W) in scope and resize every incoming mask batch to it"],"tags":["masks","iou","shape-mismatch","resolution"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}