|
4 | 4 | import numpy as np
|
5 | 5 | import torch
|
6 | 6 | from shapely.geometry import MultiPolygon, Polygon
|
| 7 | +from shapely.geometry.collection import GeometryCollection |
7 | 8 |
|
8 | 9 | from sahi.annotation import BoundingBox, Category, Mask
|
9 | 10 | from sahi.prediction import ObjectPrediction
|
@@ -65,6 +66,71 @@ def tolist(self):
|
65 | 66 | return self.list
|
66 | 67 |
|
67 | 68 |
|
| 69 | +def repair_polygon(shapely_polygon: Polygon) -> Polygon: |
| 70 | + """ |
| 71 | + Fix polygons |
| 72 | + :param shapely_polygon: Shapely polygon object |
| 73 | + :return: |
| 74 | + """ |
| 75 | + if not shapely_polygon.is_valid: |
| 76 | + fixed_polygon = shapely_polygon.buffer(0) |
| 77 | + if fixed_polygon.is_valid: |
| 78 | + if isinstance(fixed_polygon, Polygon): |
| 79 | + return fixed_polygon |
| 80 | + elif isinstance(fixed_polygon, MultiPolygon): |
| 81 | + return max(fixed_polygon.geoms, key=lambda p: p.area) |
| 82 | + elif isinstance(fixed_polygon, GeometryCollection): |
| 83 | + polygons = [geom for geom in fixed_polygon.geoms if isinstance(geom, Polygon)] |
| 84 | + return max(polygons, key=lambda p: p.area) if polygons else shapely_polygon |
| 85 | + |
| 86 | + return shapely_polygon |
| 87 | + |
| 88 | + |
| 89 | +def repair_multipolygon(shapely_multipolygon: MultiPolygon) -> MultiPolygon: |
| 90 | + """ |
| 91 | + Fix invalid MultiPolygon objects |
| 92 | + :param shapely_multipolygon: Imported shapely MultiPolygon object |
| 93 | + :return: |
| 94 | + """ |
| 95 | + if not shapely_multipolygon.is_valid: |
| 96 | + fixed_geometry = shapely_multipolygon.buffer(0) |
| 97 | + |
| 98 | + if fixed_geometry.is_valid: |
| 99 | + if isinstance(fixed_geometry, MultiPolygon): |
| 100 | + return fixed_geometry |
| 101 | + elif isinstance(fixed_geometry, Polygon): |
| 102 | + return MultiPolygon([fixed_geometry]) |
| 103 | + elif isinstance(fixed_geometry, GeometryCollection): |
| 104 | + polygons = [geom for geom in fixed_geometry.geoms if isinstance(geom, Polygon)] |
| 105 | + return MultiPolygon(polygons) if polygons else shapely_multipolygon |
| 106 | + |
| 107 | + return shapely_multipolygon |
| 108 | + |
| 109 | + |
| 110 | +def coco_segmentation_to_shapely(segmentation: Union[List, List[List]]): |
| 111 | + """ |
| 112 | + Fix segment data in COCO format |
| 113 | + :param segmentation: segment data in COCO format |
| 114 | + :return: |
| 115 | + """ |
| 116 | + if isinstance(segmentation, List) and all([not isinstance(seg, List) for seg in segmentation]): |
| 117 | + segmentation = [segmentation] |
| 118 | + elif isinstance(segmentation, List) and all([isinstance(seg, List) for seg in segmentation]): |
| 119 | + pass |
| 120 | + else: |
| 121 | + raise ValueError("segmentation must be List or List[List]") |
| 122 | + |
| 123 | + polygon_list = [] |
| 124 | + |
| 125 | + for coco_polygon in segmentation: |
| 126 | + point_list = list(zip(coco_polygon[::2], coco_polygon[1::2])) |
| 127 | + shapely_polygon = Polygon(point_list) |
| 128 | + polygon_list.append(repair_polygon(shapely_polygon)) |
| 129 | + |
| 130 | + shapely_multipolygon = repair_multipolygon(MultiPolygon(polygon_list)) |
| 131 | + return shapely_multipolygon |
| 132 | + |
| 133 | + |
68 | 134 | def object_prediction_list_to_torch(object_prediction_list: ObjectPredictionList) -> torch.tensor:
|
69 | 135 | """
|
70 | 136 | Returns:
|
@@ -166,6 +232,12 @@ def get_merged_mask(pred1: ObjectPrediction, pred2: ObjectPrediction) -> Mask:
|
166 | 232 | # buffer(0) is a quickhack to fix invalid polygons most of the time
|
167 | 233 | poly1 = get_shapely_multipolygon(mask1.segmentation).buffer(0)
|
168 | 234 | poly2 = get_shapely_multipolygon(mask2.segmentation).buffer(0)
|
| 235 | + |
| 236 | + if poly1.is_empty: |
| 237 | + poly1 = coco_segmentation_to_shapely(mask1.segmentation) |
| 238 | + if poly2.is_empty: |
| 239 | + poly2 = coco_segmentation_to_shapely(mask2.segmentation) |
| 240 | + |
169 | 241 | union_poly = poly1.union(poly2)
|
170 | 242 | if not hasattr(union_poly, "geoms"):
|
171 | 243 | union_poly = MultiPolygon([union_poly])
|
|
0 commit comments