{"record":{"id":"fb334ca397c38ac1","repo":"roboflow/supervision","slug":"bounding-boxes-must-be-shaped-n-4","errorCode":null,"errorMessage":"Bounding boxes must be shaped (N, 4)","messagePattern":"Bounding boxes must be shaped \\(N, 4\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/metrics/utils/object_size.py","lineNumber":117,"sourceCode":"        the enum values of ObjectSizeCategory. Shaped (N,).\n\n    Example:\n        ```pycon\n        >>> import numpy as np\n        >>> from supervision.metrics.utils.object_size import get_bbox_size_category\n        >>> xyxy = np.array([\n        ...     [0, 0, 31, 31],    # 961 (Small)\n        ...     [0, 0, 32, 32],    # 1024 (Medium)\n        ...     [0, 0, 95, 95],    # 9025 (Medium)\n        ...     [0, 0, 96, 96]     # 9216 (Large)\n        ... ])\n        >>> get_bbox_size_category(xyxy)\n        array([1, 2, 2, 3])\n\n        ```\n    \"\"\"\n    if len(xyxy.shape) != 2 or xyxy.shape[1] != 4:\n        raise ValueError(\"Bounding boxes must be shaped (N, 4)\")\n\n    width = xyxy[:, 2] - xyxy[:, 0]\n    height = xyxy[:, 3] - xyxy[:, 1]\n    areas = width * height\n\n    result = np.full(areas.shape, ObjectSizeCategory.ANY.value)\n    SM, LG = SIZE_THRESHOLDS\n    result[areas < SM] = ObjectSizeCategory.SMALL.value\n    result[(areas >= SM) & (areas < LG)] = ObjectSizeCategory.MEDIUM.value\n    result[areas >= LG] = ObjectSizeCategory.LARGE.value\n    return result\n\n\ndef get_area_size_category(\n    areas: npt.NDArray[np.number],\n) -> npt.NDArray[np.int_]:\n    \"\"\"Get object size categories from per-detection pixel areas.\n","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/metrics/utils/object_size.py#L99-L135","documentation":"Raised by get_bbox_size_category() when the input bounding-box array is not 2-D with exactly 4 columns (xyxy format). The function computes width*height per row to bucket boxes into SMALL/MEDIUM/LARGE, so a malformed shape would corrupt the per-box area vector. It validates shape before any arithmetic.","triggerScenarios":"Calling get_bbox_size_category with a (N,5) array, a flat (4,) vector, a (N,4,2) OBB array, or an empty (0,) array.","commonSituations":"Passing xyxyxyxy (oriented box) coordinates by mistake; passing a single box [x1,y1,x2,y2] without wrapping in a 2-D array; slicing errors that drop a dimension; passing mask or polygon data.","solutions":["Reshape input to (N, 4): np.asarray(boxes).reshape(-1, 4) when it is a flat list of boxes","If you have oriented boxes, use get_obb_size_category instead","Add a shape assert in your pipeline: assert boxes.ndim == 2 and boxes.shape[1] == 4"],"exampleFix":"# before\nsize = get_bbox_size_category(np.array([0, 0, 31, 31]))  # 1-D\n\n# after\nsize = get_bbox_size_category(np.array([[0, 0, 31, 31]]))  # (1, 4)","handlingStrategy":"validation","validationCode":"boxes = np.asarray(boxes)\nif boxes.ndim != 2 or boxes.shape[1] != 4:\n    boxes = boxes.reshape(-1, 4)\ncats = get_bbox_size_category(boxes)","typeGuard":"import numpy as np\n\ndef is_valid_xyxy(arr: np.ndarray) -> bool:\n    \"\"\"True when arr is (N, 4) suitable for bbox size categorization.\"\"\"\n    return arr.ndim == 2 and arr.shape[1] == 4","tryCatchPattern":"try:\n    cats = get_bbox_size_category(boxes)\nexcept ValueError as e:\n    if 'shaped (N, 4)' in str(e):\n        cats = get_bbox_size_category(boxes.reshape(-1, 4))\n    else:\n        raise","preventionTips":["Always wrap single boxes in a list: np.array([[x1, y1, x2, y2]])","Keep OBB corners out of xyxy paths; convert formats explicitly"],"tags":["metrics","object-size","shape-validation","numpy"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}