facebookresearch/detectron2 · error · NotImplementedError
Unknown geometric structure {}
Error message
Unknown geometric structure {} What it means
While converting Cityscapes annotation polygons, shapely's difference/union produced an object that is neither Polygon nor MultiPolygon (e.g. GeometryCollection containing lines/points, or empty geometry). The converter cannot turn it into COCO polygon lists, so it raises NotImplementedError.
Source
Thrown at detectron2/data/datasets/cityscapes.py:215
polygons_union = polygons_union.union(poly)
continue
# Take non-overlapping part of the polygon
poly_wo_overlaps = poly.difference(polygons_union)
if poly_wo_overlaps.is_empty:
continue
polygons_union = polygons_union.union(poly)
anno = {}
anno["iscrowd"] = label_name.endswith("group")
anno["category_id"] = label.id
if isinstance(poly_wo_overlaps, Polygon):
poly_list = [poly_wo_overlaps]
elif isinstance(poly_wo_overlaps, MultiPolygon):
poly_list = poly_wo_overlaps.geoms
else:
raise NotImplementedError("Unknown geometric structure {}".format(poly_wo_overlaps))
poly_coord = []
for poly_el in poly_list:
# COCO API can work only with exterior boundaries now, hence we store only them.
# TODO: store both exterior and interior boundaries once other parts of the
# codebase support holes in polygons.
poly_coord.append(list(chain(*poly_el.exterior.coords)))
anno["segmentation"] = poly_coord
(xmin, ymin, xmax, ymax) = poly_wo_overlaps.bounds
anno["bbox"] = (xmin, ymin, xmax, ymax)
anno["bbox_mode"] = BoxMode.XYXY_ABS
annos.append(anno)
else:
# See also the official annotation parsing scripts at
# https://github.com/mcordts/cityscapesScripts/blob/master/cityscapesscripts/evaluation/instances2dict.py # noqa
with PathManager.open(instance_id_file, "rb") as f:View on GitHub (pinned to a2f4a8771a)
Solutions
- Fix or regenerate the offending *_polygons.json annotation file (inspect which image id fails from the traceback)
- Upgrade/downgrade detectron2 — later versions handle GeometryCollection and filter degenerate parts
- Preprocess: filter out annotations whose polygon difference yields empty or non-polygonal geometry before conversion
Example fix
// before
poly_wo_overlaps = polys[i].difference(offsetcrowns_union)
// after (defensive)
if isinstance(poly_wo_overlaps, (Polygon, MultiPolygon)) and not poly_wo_overlaps.is_empty:
...proceed...
else:
continue # skip degenerate annotation Defensive patterns
Strategy: validation
Validate before calling
from shapely.geometry import Polygon, MultiPolygon ok = isinstance(poly_wo_overlaps, (Polygon, MultiPolygon)) and not poly_wo_overlaps.is_empty
Type guard
def is_convertible_polygon(g) -> bool:
return (isinstance(g, (Polygon, MultiPolygon))
and not g.is_empty
and g.area > 0) Try / catch
try:
dicts = _cityscapes_files_to_dict(...)
except NotImplementedError as e:
log.warning(f'skipping malformed cityscapes file: {e}') Prevention
- Validate label polygon files with shapely before dataset conversion
- Keep shapely/detectron2 versions in sync; newer versions handle GeometryCollection
- Backup and verify Cityscapes json labels after any manual edits
When it happens
Trigger: Loading Cityscapes labels via _cityscapes_files_to_dict on an image whose instance polygons, after overlap removal, degenerate into a GeometryCollection or empty geometry (tiny/degenerate masks, self-intersecting or sliver polygons).
Common situations: Corrupted or manually edited Cityscapes label files; annotations with near-zero-area or overlapping degenerate polygons; shapely version behavior differences producing GeometryCollection from difference().
Related errors
- Cannot match one checkpoint key to multiple keys in the mode
- Class with @configurable must have a 'from_config' classmeth
- {name} must take 'cfg' as the first argument!
- target of LazyCall must be a callable or defines a callable!
- total_batch_size and single_gpu_batch_size are mutually inco
AI-assisted analysis of facebookresearch/detectron2@a2f4a8771a (2026-08-27).
Data as JSON: /api/errors/a5b3d40bc2b22ac9.
Report an issue: GitHub.