From 4340896826e2766ac49c249d1cc37f649d2a800d Mon Sep 17 00:00:00 2001 From: Matt Sokoloff Date: Wed, 19 Oct 2022 13:29:16 -0400 Subject: [PATCH 1/2] fix invalid polygons --- .../data/annotation_types/geometry/mask.py | 16 ++++++----- .../serialization/coco/instance_dataset.py | 3 ++- .../serialization/coco/panoptic_dataset.py | 27 ++++++++++++++++--- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/labelbox/data/annotation_types/geometry/mask.py b/labelbox/data/annotation_types/geometry/mask.py index b184017f5..b6453af7f 100644 --- a/labelbox/data/annotation_types/geometry/mask.py +++ b/labelbox/data/annotation_types/geometry/mask.py @@ -38,9 +38,7 @@ class Mask(Geometry): @property def geometry(self) -> Dict[str, Tuple[int, int, int]]: mask = self.draw(color=1) - contours, hierarchy = cv2.findContours(image=mask, - mode=cv2.RETR_TREE, - method=cv2.CHAIN_APPROX_NONE) + contours, hierarchy = cv2.findContours(image=mask,mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE) holes = [] external_contours = [] @@ -51,9 +49,16 @@ def geometry(self) -> Dict[str, Tuple[int, int, int]]: else: external_contours.append(contours[i]) - external_polygons = self._extract_polygons_from_contours( - external_contours) + external_polygons = self._extract_polygons_from_contours(external_contours) holes = self._extract_polygons_from_contours(holes) + + + if not external_polygons.is_valid: + external_polygons = external_polygons.buffer(0) + + if not holes.is_valid: + holes = holes.buffer(0) + return external_polygons.difference(holes).__geo_interface__ def draw(self, @@ -78,7 +83,6 @@ def draw(self, np.ndarray representing only this object as opposed to the mask that this object references which might have multiple objects determined by colors """ - mask = self.mask.value mask = np.alltrue(mask == self.color, axis=2).astype(np.uint8) diff --git a/labelbox/data/serialization/coco/instance_dataset.py b/labelbox/data/serialization/coco/instance_dataset.py index f5ab45a10..2b116ff20 100644 --- a/labelbox/data/serialization/coco/instance_dataset.py +++ b/labelbox/data/serialization/coco/instance_dataset.py @@ -21,7 +21,6 @@ def mask_to_coco_object_annotation( # This is going to fill any holes into the multipolygon # If you need to support holes use the panoptic data format shapely = annotation.value.shapely.simplify(1).buffer(0) - if shapely.is_empty: return @@ -42,6 +41,8 @@ def mask_to_coco_object_annotation( iscrowd=0) + + def vector_to_coco_object_annotation(annotation: ObjectAnnotation, annot_idx: int, image_id: int, category_id: int) -> COCOObjectAnnotation: diff --git a/labelbox/data/serialization/coco/panoptic_dataset.py b/labelbox/data/serialization/coco/panoptic_dataset.py index 6226e40a2..b51981b1a 100644 --- a/labelbox/data/serialization/coco/panoptic_dataset.py +++ b/labelbox/data/serialization/coco/panoptic_dataset.py @@ -18,11 +18,17 @@ from .annotation import PanopticAnnotation, SegmentInfo, get_annotation_lookup + + def vector_to_coco_segment_info(canvas: np.ndarray, annotation: ObjectAnnotation, annotation_idx: int, image: CocoImage, category_id: int): + shapely = annotation.value.shapely + if shapely.is_empty: + return + xmin, ymin, xmax, ymax = shapely.bounds canvas = annotation.value.draw(height=image.height, width=image.width, @@ -40,6 +46,9 @@ def mask_to_coco_segment_info(canvas: np.ndarray, annotation, color = id_to_rgb(annotation_idx) mask = annotation.value.draw(color=color) shapely = annotation.value.shapely + if shapely.is_empty: + return + xmin, ymin, xmax, ymax = shapely.bounds canvas = np.where(canvas == (0, 0, 0), mask, canvas) return SegmentInfo(id=annotation_idx, @@ -70,20 +79,32 @@ def process_label(label: Label, for annotation_idx, annotation in enumerate(annotations[class_name]): categories[annotation.name] = hash_category_name(annotation.name) if isinstance(annotation.value, Mask): - segment, canvas = (mask_to_coco_segment_info( + coco_segment_info = mask_to_coco_segment_info( canvas, annotation, class_idx + 1, - categories[annotation.name])) + categories[annotation.name]) + + if coco_segment_info is None: + # Filter out empty masks + continue + + segment, canvas = coco_segment_info segments.append(segment) is_thing[annotation.name] = 0 elif isinstance(annotation.value, (Polygon, Rectangle)): - segment, canvas = vector_to_coco_segment_info( + coco_vector_info = vector_to_coco_segment_info( canvas, annotation, annotation_idx=(class_idx if all_stuff else annotation_idx) + 1, image=image, category_id=categories[annotation.name]) + + if coco_segment_info is None: + # Filter out empty annotations + continue + + segment, canvas = coco_vector_info segments.append(segment) is_thing[annotation.name] = 1 - int(all_stuff) From a032f9c6bde1893a979d861c190f69e6f8822116 Mon Sep 17 00:00:00 2001 From: Matt Sokoloff Date: Wed, 19 Oct 2022 13:30:53 -0400 Subject: [PATCH 2/2] format --- labelbox/data/annotation_types/geometry/mask.py | 8 +++++--- labelbox/data/serialization/coco/instance_dataset.py | 2 -- labelbox/data/serialization/coco/panoptic_dataset.py | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/labelbox/data/annotation_types/geometry/mask.py b/labelbox/data/annotation_types/geometry/mask.py index b6453af7f..a18613af3 100644 --- a/labelbox/data/annotation_types/geometry/mask.py +++ b/labelbox/data/annotation_types/geometry/mask.py @@ -38,7 +38,9 @@ class Mask(Geometry): @property def geometry(self) -> Dict[str, Tuple[int, int, int]]: mask = self.draw(color=1) - contours, hierarchy = cv2.findContours(image=mask,mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE) + contours, hierarchy = cv2.findContours(image=mask, + mode=cv2.RETR_TREE, + method=cv2.CHAIN_APPROX_NONE) holes = [] external_contours = [] @@ -49,10 +51,10 @@ def geometry(self) -> Dict[str, Tuple[int, int, int]]: else: external_contours.append(contours[i]) - external_polygons = self._extract_polygons_from_contours(external_contours) + external_polygons = self._extract_polygons_from_contours( + external_contours) holes = self._extract_polygons_from_contours(holes) - if not external_polygons.is_valid: external_polygons = external_polygons.buffer(0) diff --git a/labelbox/data/serialization/coco/instance_dataset.py b/labelbox/data/serialization/coco/instance_dataset.py index 2b116ff20..d5568f299 100644 --- a/labelbox/data/serialization/coco/instance_dataset.py +++ b/labelbox/data/serialization/coco/instance_dataset.py @@ -41,8 +41,6 @@ def mask_to_coco_object_annotation( iscrowd=0) - - def vector_to_coco_object_annotation(annotation: ObjectAnnotation, annot_idx: int, image_id: int, category_id: int) -> COCOObjectAnnotation: diff --git a/labelbox/data/serialization/coco/panoptic_dataset.py b/labelbox/data/serialization/coco/panoptic_dataset.py index b51981b1a..aa2cf6cbd 100644 --- a/labelbox/data/serialization/coco/panoptic_dataset.py +++ b/labelbox/data/serialization/coco/panoptic_dataset.py @@ -18,8 +18,6 @@ from .annotation import PanopticAnnotation, SegmentInfo, get_annotation_lookup - - def vector_to_coco_segment_info(canvas: np.ndarray, annotation: ObjectAnnotation, annotation_idx: int, image: CocoImage,