{"record":{"id":"8cec9c157a4b160a","repo":"roboflow/supervision","slug":"cannot-merge-compactmask-objects-with-different-im","errorCode":null,"errorMessage":"Cannot merge CompactMask objects with different image shapes: {image_shape} vs {cm._image_shape}","messagePattern":"Cannot merge CompactMask objects with different image shapes: (.+?) vs (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/detection/compact_mask.py","lineNumber":1361,"sourceCode":"            >>> masks1 = np.zeros((2, 50, 50), dtype=bool)\n            >>> masks2 = np.zeros((3, 50, 50), dtype=bool)\n            >>> xyxy1 = np.array([[0,0,10,10],[10,10,20,20]], dtype=np.float32)\n            >>> xyxy2 = np.array(\n            ...     [[0,0,5,5],[5,5,10,10],[10,10,15,15]], dtype=np.float32)\n            >>> cm1 = CompactMask.from_dense(masks1, xyxy1, image_shape=(50, 50))\n            >>> cm2 = CompactMask.from_dense(masks2, xyxy2, image_shape=(50, 50))\n            >>> len(CompactMask.merge([cm1, cm2]))\n            5\n\n            ```\n        \"\"\"\n        if not masks_list:\n            raise ValueError(\"Cannot merge an empty list of CompactMask objects.\")\n\n        image_shape = masks_list[0]._image_shape\n        for cm in masks_list[1:]:\n            if cm._image_shape != image_shape:\n                raise ValueError(\n                    f\"Cannot merge CompactMask objects with different image shapes: \"\n                    f\"{image_shape} vs {cm._image_shape}\"\n                )\n\n        # list.extend is a C-level call and avoids the per-element Python\n        # bytecode overhead of a flat list comprehension.  This matters under\n        # GIL contention when multiple threads call merge concurrently.\n        new_rles: list[npt.NDArray[np.int32]] = []\n        for cm in masks_list:\n            new_rles.extend(cm._rles)\n\n        # np.concatenate handles (0, 2) arrays correctly.\n        # No .astype() needed — _crop_shapes and _offsets are already int32.\n        new_crop_shapes: npt.NDArray[np.int32] = np.concatenate(\n            [cm._crop_shapes for cm in masks_list], axis=0\n        )\n        new_offsets: npt.NDArray[np.int32] = np.concatenate(\n            [cm._offsets for cm in masks_list], axis=0","sourceCodeStart":1343,"sourceCodeEnd":1379,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/detection/compact_mask.py#L1343-L1379","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nmerged = CompactMask.merge([cm_a, cm_b])  # shapes (720,1280) vs (1080,1920)\n\n# after\ntarget = cm_a.shape[1:]\nmerged = CompactMask.merge([cm_a, cm_b.resize(target)])","handlingStrategy":"validation","validationCode":"shapes = {cm.shape[1:] for cm in masks_list}\nassert len(shapes) == 1, f\"mixed image shapes: {shapes}\"","typeGuard":null,"tryCatchPattern":"try:\n    merged = sv.CompactMask.merge(masks_list)\nexcept ValueError as e:\n    if \"different image shapes\" in str(e):\n        target = masks_list[0].shape[1:]\n        merged = sv.CompactMask.merge([cm.resize(target) for cm in masks_list])\n    else:\n        raise","preventionTips":["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."],"tags":["compact-mask","merge","image-shape","resolution-mismatch"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}