| 117 | return self._has_holes |
| 118 | |
| 119 | def mask_to_polygons(self, mask): |
| 120 | # cv2.RETR_CCOMP flag retrieves all the contours and arranges them to a 2-level |
| 121 | # hierarchy. External contours (boundary) of the object are placed in hierarchy-1. |
| 122 | # Internal contours (holes) are placed in hierarchy-2. |
| 123 | # cv2.CHAIN_APPROX_NONE flag gets vertices of polygons from contours. |
| 124 | mask = np.ascontiguousarray(mask) # some versions of cv2 does not support incontiguous arr |
| 125 | res = cv2.findContours(mask.astype("uint8"), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE) |
| 126 | hierarchy = res[-1] |
| 127 | if hierarchy is None: # empty mask |
| 128 | return [], False |
| 129 | has_holes = (hierarchy.reshape(-1, 4)[:, 3] >= 0).sum() > 0 |
| 130 | res = res[-2] |
| 131 | res = [x.flatten() for x in res] |
| 132 | # These coordinates from OpenCV are integers in range [0, W-1 or H-1]. |
| 133 | # We add 0.5 to turn them into real-value coordinate space. A better solution |
| 134 | # would be to first +0.5 and then dilate the returned polygon by 0.5. |
| 135 | res = [x + 0.5 for x in res if len(x) >= 6] |
| 136 | return res, has_holes |
| 137 | |
| 138 | def polygons_to_mask(self, polygons): |
| 139 | rle = mask_util.frPyObjects(polygons, self.height, self.width) |