roboflow/supervision · error · ValueError

Cannot filter keypoints with a 2D boolean mask where rows ha

Error message

Cannot filter keypoints with a 2D boolean mask where rows have different numbers of True values. All objects must select the same number of keypoints. Got counts per object: {counts.tolist()}

What it means

sv.F1Score.update() accepts a single (predictions, targets) pair or equal-length lists (one per image) and raises when the counts differ. Entries are matched index-wise, so unequal lists mean images on one side have no counterpart on the other and the metric would silently misattribute everything after the first gap.

Source

Thrown at src/supervision/key_points/core.py:876

        Raises:
            ValueError: If `mask.shape[0]` does not match the number of objects, if
                `mask.shape[1]` does not match the number of keypoints, or if
                different rows of the mask select different numbers of `True` values.
        """
        n = len(self.xy)
        if mask.shape[0] != n:
            raise ValueError(
                f"2D boolean mask row count {mask.shape[0]} does not match "
                f"object count {n}."
            )
        if mask.shape[1] != self.xy.shape[1]:
            raise ValueError(
                f"2D boolean mask column count {mask.shape[1]} does not match "
                f"keypoint count {self.xy.shape[1]}."
            )
        counts = np.sum(mask, axis=1)
        if n > 0 and not np.all(counts == counts[0]):
            raise ValueError(
                "Cannot filter keypoints with a 2D boolean mask where rows have "
                "different numbers of True values. "
                "All objects must select the same number of keypoints. "
                f"Got counts per object: {counts.tolist()}"
            )
        k = int(counts[0]) if n > 0 else 0
        xy_selected = np.zeros((n, k, self.xy.shape[2]), dtype=self.xy.dtype)
        keypoint_confidence_selected: npt.NDArray[np.float32] | None = None
        if self.keypoint_confidence is not None:
            keypoint_confidence_selected = cast(
                npt.NDArray[np.float32],
                np.zeros((n, k), dtype=self.keypoint_confidence.dtype),
            )
        visible_selected: npt.NDArray[np.bool_] | None = None
        if self.visible is not None:
            visible_selected = np.zeros((n, k), dtype=bool)
        for row in range(n):
            row_indices = np.flatnonzero(mask[row])

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Append to both lists unconditionally; use sv.Detections.empty() for frames with no detections
  2. Iterate with zip(image, ground_truth) so both sides stay paired
  3. assert len(predictions_list) == len(targets_list) before update()
  4. Call update() once per image with single Detections objects instead of accumulating lists

Example fix

# before
for img, gt in zip(images, gts):
    det = detector(img)
    if det is not None:
        preds.append(det)
    targets.append(gt)          # lists desync
f1.update(predictions=preds, targets=targets)  # -> ValueError

# after
for img, gt in zip(images, gts):
    det = detector(img) or sv.Detections.empty()
    preds.append(det)
    targets.append(gt)
f1.update(predictions=preds, targets=targets)
Defensive patterns

Strategy: validation

Validate before calling

assert len(predictions) == len(targets), (
    f'predictions ({len(predictions)}) and targets ({len(targets)}) must pair 1:1'
)
f1.update(predictions=predictions, targets=targets)

Type guard

def is_paired_batch(predictions: list, targets: list) -> bool:
    """True when both lists are equal-length lists of Detections."""
    return len(predictions) == len(targets) and all(
        isinstance(p, sv.Detections) and isinstance(t, sv.Detections)
        for p, t in zip(predictions, targets)
    )

Prevention

When it happens

Trigger: Calling f1.update(predictions=[...], targets=[...]) with mismatched list lengths — typically from loops that conditionally append to one list (e.g. skipping frames where the model found nothing) but not the other.

Common situations: Batch evaluation scripts appending predictions only for non-empty frames; dataset sweeps where some frames error out on one side; refactoring that moved one append inside an if-block.

Related errors


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