roboflow/supervision · error · ValueError

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

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.

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.

Appendix: 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)