{"record":{"id":"e61ea7a1f293c9d3","repo":"roboflow/supervision","slug":"mean-mask-must-match-the-image-height-and-width","errorCode":null,"errorMessage":"Mean mask must match the image height and width","messagePattern":"Mean mask must match the image height and width","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/_cv2/_image.py","lineNumber":116,"sourceCode":") -> npt.NDArray[np.uint8]:\n    \"\"\"Scale, offset, take the absolute value, and saturate to uint8.\"\"\"\n    values = np.abs(image.astype(np.float64) * alpha + beta)\n    return _cast_array_like_opencv(values, np.dtype(np.uint8))\n\n\ndef _mean(\n    image: npt.NDArray[Any], mask: npt.NDArray[Any] | None = None\n) -> tuple[float, float, float, float]:\n    \"\"\"Return per-channel means using OpenCV's four-value result contract.\"\"\"\n    if mask is None:\n        selected = (\n            image.reshape(-1, 1)\n            if image.ndim == 2\n            else image.reshape(-1, image.shape[2])\n        )\n    else:\n        if mask.shape != image.shape[:2]:\n            raise ValueError(\"Mean mask must match the image height and width\")\n        selected = image[mask != 0]\n        if image.ndim == 2:\n            selected = selected.reshape(-1, 1)\n    if selected.size == 0:\n        means = np.zeros(4, dtype=np.float64)\n    else:\n        means = np.zeros(4, dtype=np.float64)\n        means[: selected.shape[1]] = np.mean(selected, axis=0)\n    return cast(\n        tuple[float, float, float, float],\n        tuple(float(value) for value in means),\n    )\n\n\ndef _resize(\n    src: npt.NDArray[Any],\n    dsize: tuple[int, int] | None,\n    fx: float = 0,","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/_cv2/_image.py#L98-L134","documentation":"Thrown by the fallback cv2.mean when a mask is supplied. The implementation selects pixels via boolean indexing (image[mask != 0]), which requires mask to be a 2D array exactly matching the image's (height, width). OpenCV has the same contract, but the fallback checks it explicitly.","triggerScenarios":"Calling cv2.mean(image, mask=mask) where mask.shape != image.shape[:2] — e.g. a 3-channel mask, a mask from a differently-sized image, or a mask with an extra batch dimension.","commonSituations":"Reusing a mask computed on a resized/downscaled frame (common in segmentation pipelines), passing a (H, W, 1) mask where (H, W) is expected, or passing a full-shape mask for a 3-channel image.","solutions":["Resize the mask to the image dimensions: mask = cv2.resize(mask, (image.shape[1], image.shape[0])).","Squeeze extra dimensions: mask = mask.reshape(image.shape[:2]) or mask.squeeze().","Compute the mask from the same frame you are measuring."],"exampleFix":"# before\nmean = cv2.mean(frame, mask=seg_mask)  # seg_mask from half-res frame\n\n# after\nseg_mask = cv2.resize(seg_mask, (frame.shape[1], frame.shape[0]))\nmean = cv2.mean(frame, mask=seg_mask)","handlingStrategy":"validation","validationCode":"if mask is not None and mask.shape != image.shape[:2]:\n    mask = mask.reshape(image.shape[:2]) if mask.size == image.size else cv2.resize(mask, (image.shape[1], image.shape[0]))\nmean = cv2.mean(image, mask=mask)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Compute masks on the same frame being measured","Keep masks 2D (H, W)","Squeeze (H, W, 1) masks before use"],"tags":["opencv-fallback","mask","shape-mismatch","statistics"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}