roboflow/supervision · error · ValueError
Cannot merge CompactMask objects with different image shapes
Error message
Cannot merge CompactMask objects with different image shapes: {image_shape} vs {cm._image_shape} What it means
Raised by CompactMask.merge when the CompactMask objects in masks_list were built with different _image_shape values. A merged result needs one coherent canvas; merging masks defined on different resolutions would silently misplace crops, so all inputs must share the same (height, width).
Source
Thrown at src/supervision/detection/compact_mask.py:1361
>>> masks1 = np.zeros((2, 50, 50), dtype=bool)
>>> masks2 = np.zeros((3, 50, 50), dtype=bool)
>>> xyxy1 = np.array([[0,0,10,10],[10,10,20,20]], dtype=np.float32)
>>> xyxy2 = np.array(
... [[0,0,5,5],[5,5,10,10],[10,10,15,15]], dtype=np.float32)
>>> cm1 = CompactMask.from_dense(masks1, xyxy1, image_shape=(50, 50))
>>> cm2 = CompactMask.from_dense(masks2, xyxy2, image_shape=(50, 50))
>>> len(CompactMask.merge([cm1, cm2]))
5
```
"""
if not masks_list:
raise ValueError("Cannot merge an empty list of CompactMask objects.")
image_shape = masks_list[0]._image_shape
for cm in masks_list[1:]:
if cm._image_shape != image_shape:
raise ValueError(
f"Cannot merge CompactMask objects with different image shapes: "
f"{image_shape} vs {cm._image_shape}"
)
# list.extend is a C-level call and avoids the per-element Python
# bytecode overhead of a flat list comprehension. This matters under
# GIL contention when multiple threads call merge concurrently.
new_rles: list[npt.NDArray[np.int32]] = []
for cm in masks_list:
new_rles.extend(cm._rles)
# np.concatenate handles (0, 2) arrays correctly.
# No .astype() needed — _crop_shapes and _offsets are already int32.
new_crop_shapes: npt.NDArray[np.int32] = np.concatenate(
[cm._crop_shapes for cm in masks_list], axis=0
)
new_offsets: npt.NDArray[np.int32] = np.concatenate(
[cm._offsets for cm in masks_list], axis=0View on GitHub (pinned to 7f254d9784)
Solutions
- Resize all CompactMask objects to a common shape before merging: [cm.resize(common_shape) for cm in masks_list].
- Ensure every producing call (from_dense/from_coco_rle/with_offset) receives the same image_shape.
- Group masks by image_shape and merge per group if heterogeneous sizes are expected.
Example fix
# before merged = CompactMask.merge([cm_a, cm_b]) # shapes (720,1280) vs (1080,1920) # after target = cm_a.shape[1:] merged = CompactMask.merge([cm_a, cm_b.resize(target)])
Defensive patterns
Strategy: validation
Validate before calling
shapes = {cm.shape[1:] for cm in masks_list}
assert len(shapes) == 1, f"mixed image shapes: {shapes}" Try / catch
try:
merged = sv.CompactMask.merge(masks_list)
except ValueError as e:
if "different image shapes" in str(e):
target = masks_list[0].shape[1:]
merged = sv.CompactMask.merge([cm.resize(target) for cm in masks_list])
else:
raise Prevention
- Pass the same image_shape to every from_dense/from_coco_rle/with_offset call feeding a merge.
- Resize all masks to a canonical shape before merging in mixed-resolution pipelines.
- Group by .shape[1:] and merge per group when sizes legitimately differ.
When it happens
Trigger: Calling CompactMask.merge([cm_720p, cm_1080p]); merging per-tile masks where one tile was resized before embedding; merging masks from from_coco_rle calls that received different image_shape values.
Common situations: Slicer pipelines that resize some tiles; mixed sources (one mask from from_dense on the original image, another after resize); heterogeneous image sizes in a folder processed with a single fixed shape.
Related errors
- RLE size {(rle_h, rle_w)} must match image_shape {(img_h, im
- image_shape must contain positive height and width.
- image_shape {(img_h, img_w)} exceeds the maximum allowed dim
- Cannot merge an empty list of CompactMask objects.
- new_image_shape must contain positive dimensions
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/8cec9c157a4b160a.
Report an issue: GitHub.