roboflow/supervision · error · ValueError

`is_crowd` length ({len(is_crowd)}) must match `boxes_true`

Error message

`is_crowd` length ({len(is_crowd)}) must match `boxes_true` length ({len(boxes_true)}).

What it means

Raised by sv.box_iou_batch_with_jaccard when the is_crowd flag array length does not equal the number of ground-truth boxes. is_crowd switches each ground-truth box between IoU and Jaccard-style overlap; supervision requires a one-to-one flag per ground-truth box, so a length mismatch is a caller error.

Source

Thrown at src/supervision/detection/utils/iou_and_nms.py:356

        ... ]
        >>> boxes_detection = [
        ...     [12, 22, 28, 38],
        ...     [16, 26, 36, 46]
        ... ]
        >>> is_crowd = [False, False]
        >>> ious = sv.box_iou_batch_with_jaccard(
        ...     boxes_true=boxes_true,
        ...     boxes_detection=boxes_detection,
        ...     is_crowd=is_crowd
        ... )
        >>> ious  # doctest: +ELLIPSIS
        array([[0.886..., 0.496...],
               [0.4  ..., 0.862...]])

        ```
    """
    if len(is_crowd) != len(boxes_true):
        raise ValueError(
            f"`is_crowd` length ({len(is_crowd)}) must match "
            f"`boxes_true` length ({len(boxes_true)})."
        )
    if len(boxes_detection) == 0 or len(boxes_true) == 0:
        return np.empty((len(boxes_detection), len(boxes_true)), dtype=np.float64)

    # Smallest number to avoid division by zero.
    eps = np.spacing(1)
    gt = np.asarray(boxes_true, dtype=np.float64)
    dt = np.asarray(boxes_detection, dtype=np.float64)
    crowd = np.asarray(is_crowd, dtype=bool)

    # Boxes are [x, y, w, h]. Build the far corners as `x2 = x + w` (rather than
    # reusing `w`) so that the area/intersection arithmetic is bit-identical to
    # the per-pair reference it replaces.
    gt_x2, gt_y2 = gt[:, 0] + gt[:, 2], gt[:, 1] + gt[:, 3]
    dt_x2, dt_y2 = dt[:, 0] + dt[:, 2], dt[:, 1] + dt[:, 3]

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Derive flags from the same filtering pass as boxes_true: is_crowd = np.array([a.get('iscrowd', 0) for a in gt_annos])
  2. Filter both together: mask = ...; boxes_true = boxes_true[mask]; is_crowd = is_crowd[mask]
  3. Default correctly when unsure: np.zeros(len(boxes_true), dtype=bool)

Example fix

# before
keep = areas >= min_area
ious = sv.box_iou_batch_with_jaccard(boxes_true[keep], boxes_detection, is_crowd=flags)  # flags unfiltered
# after
keep = areas >= min_area
ious = sv.box_iou_batch_with_jaccard(boxes_true[keep], boxes_detection, is_crowd=flags[keep])
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np

def aligned_crowd_flags(boxes_true, is_crowd):
    flags = np.asarray(is_crowd, dtype=bool).reshape(-1)
    assert len(flags) == len(boxes_true), f"is_crowd {len(flags)} != boxes_true {len(boxes_true)}"
    return flags

Prevention

When it happens

Trigger: sv.box_iou_batch_with_jaccard(boxes_true=gt, boxes_detection=dt, is_crowd=flags) with len(flags) != len(gt), e.g. flags computed from the detection array or a hard-coded np.zeros(5) reused after the GT set changed size.

Common situations: Reusing COCO-style is_crowd arrays after filtering ground truths (e.g. dropping ignore-region boxes) without filtering the flags in lockstep; building flags from the wrong list during evaluation-harness refactors; mAP/evaluation code where gt and flags come from different loaders.

Related errors


AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15). Data as JSON: /api/errors/eeccc37be752f9bd. Report an issue: GitHub.