facebookresearch/detectron2 · error · ValueError

Cannot create polygons: Expect a list of polygons per instan

Error message

Cannot create polygons: Expect a list of polygons per instance. Got '{}' instead.

What it means

Inside PolygonMasks construction, each entry must itself be a list of polygons for one instance. Passing e.g. a numpy array of shape (N, 2) or a flat coordinate array for one instance fails the list check in process_polygons.

Source

Thrown at detectron2/structures/masks.py:300

            raise ValueError(
                "Cannot create PolygonMasks: Expect a list of list of polygons per image. "
                "Got '{}' instead.".format(type(polygons))
            )

        def _make_array(t: Union[torch.Tensor, np.ndarray]) -> np.ndarray:
            # Use float64 for higher precision, because why not?
            # Always put polygons on CPU (self.to is a no-op) since they
            # are supposed to be small tensors.
            # May need to change this assumption if GPU placement becomes useful
            if isinstance(t, torch.Tensor):
                t = t.cpu().numpy()
            return np.asarray(t).astype("float64")

        def process_polygons(
            polygons_per_instance: List[Union[torch.Tensor, np.ndarray]],
        ) -> List[np.ndarray]:
            if not isinstance(polygons_per_instance, list):
                raise ValueError(
                    "Cannot create polygons: Expect a list of polygons per instance. "
                    "Got '{}' instead.".format(type(polygons_per_instance))
                )
            # transform each polygon to a numpy array
            polygons_per_instance = [_make_array(p) for p in polygons_per_instance]
            for polygon in polygons_per_instance:
                if len(polygon) % 2 != 0 or len(polygon) < 6:
                    raise ValueError(f"Cannot create a polygon from {len(polygon)} coordinates.")
            return polygons_per_instance

        self.polygons: List[List[np.ndarray]] = [
            process_polygons(polygons_per_instance) for polygons_per_instance in polygons
        ]

    def to(self, *args: Any, **kwargs: Any) -> "PolygonMasks":
        return self

    @property

View on GitHub (pinned to a2f4a8771a)

Solutions

  1. Wrap each instance's polygons in a list: [np.array([x0,y0,x1,y1,...])]
  2. Convert contour output: [contour.flatten() for contour in contours]
  3. Validate nesting depth (3 levels) before constructing PolygonMasks

Example fix

# before
masks = PolygonMasks([[contour_array]])  # contour_array shape (N,2)
# after
masks = PolygonMasks([[[pt[0], pt[1]] for pt in contour_array]])
Defensive patterns

Strategy: validation

Validate before calling

assert all(isinstance(inst, list) for inst in polygons), 'each instance must be a list of polygon arrays'

Type guard

def is_valid_per_instance(p) -> bool:
    return isinstance(p, list) and all(hasattr(poly, '__len__') for poly in p)

Prevention

When it happens

Trigger: PolygonMasks([[np.array([x0,y0,...])]]) is fine, but PolygonMasks([[np.array((N,2))]]) or an array-of-arrays per instance triggers it: any non-list per-instance value raises.

Common situations: Annotating with a single numpy array of polygon vertices per instance (common with cv2.findContours output) instead of a list of coordinate arrays.

Related errors


AI-assisted analysis of facebookresearch/detectron2@a2f4a8771a (2026-08-27). Data as JSON: /api/errors/4a3e46413a399e3c. Report an issue: GitHub.