def test_produces_valid_crop(self): """ <b>Description:</b> Checks that shape_produces_valid_crop returns the correct values and does not raise errors <b>Input data:</b> A valid Rectangle at [0, 0.4, 1, 0.5] A Polygon that has an invalid bounding box <b>Expected results:</b> The test passes if the call with the Rectangle returns True and the one with the polygon returns False <b>Steps</b> 1. Check Valid Rectangle 2. Check invalid Polygon """ rectangle = Rectangle(x1=0, y1=0.4, x2=1, y2=0.5) assert ShapeFactory.shape_produces_valid_crop(rectangle, 100, 100) point1 = Point(x=0.01, y=0.1) point2 = Point(x=0.35, y=0.1) point3 = Point(x=0.35, y=0.1) polygon = Polygon(points=[point1, point2, point3]) assert not ShapeFactory.shape_produces_valid_crop(polygon, 100, 250)
def test_annotation_magic_methods(self): """ <b>Description:</b> Check Annotation __repr__, __eq__ methods <b>Input data:</b> Initialized instance of Annotation <b>Expected results:</b> Test passes if Annotation magic methods returns correct values <b>Steps</b> 1. Create Annotation instances 2. Check returning value of magic methods """ annotation = self.annotation other_annotation = self.annotation point1 = Point(0.3, 0.1) point2 = Point(0.8, 0.3) point3 = Point(0.6, 0.2) points = [point1, point2, point3] third_annotation = Annotation(shape=Polygon(points=points), labels=self.labels) assert ( repr(annotation) == "Annotation(shape=Ellipse(x1=0.5, y1=0.1, x2=0.8, y2=0.3), labels=[], id=123456789)" ) assert annotation == other_annotation assert annotation != third_annotation assert annotation != str
def test_dataset_item_append_annotations(self): """ <b>Description:</b> Check DatasetItemEntity class "append_annotations" method <b>Input data:</b> DatasetItemEntity class object with specified "media", "annotation_scene", "roi", "metadata" and "subset" parameters <b>Expected results:</b> Test passes if annotations list returned after "append_annotations" method is equal to expected <b>Steps</b> 1. Check annotations list returned after "append_annotations" method with specified non-included annotations 2. Check annotations list returned after "append_annotations" method with incorrect shape annotation """ # Checking annotations list returned after "append_annotations" method with specified non-included annotations dataset_item = DatasetItemParameters().default_values_dataset_item() full_box_annotations = list(dataset_item.annotation_scene.annotations) annotations_to_add = self.annotations_to_add() normalized_annotations = [] for annotation in annotations_to_add: normalized_annotations.append( Annotation( shape=annotation.shape.normalize_wrt_roi_shape( dataset_item.roi.shape), labels=annotation.get_labels(), )) dataset_item.append_annotations(annotations_to_add) # Random id is generated for normalized annotations normalized_annotations[ 0].id = dataset_item.annotation_scene.annotations[2].id normalized_annotations[ 1].id = dataset_item.annotation_scene.annotations[3].id assert ( dataset_item.annotation_scene.annotations == full_box_annotations + normalized_annotations) # Checking annotations list returned after "append_annotations" method with incorrect shape annotation incorrect_shape_label = LabelEntity( name="Label for incorrect shape", domain=Domain.CLASSIFICATION, color=Color(red=80, green=70, blue=155), id=ID("incorrect_shape_label"), ) incorrect_polygon = Polygon( [Point(x=0.01, y=0.1), Point(x=0.35, y=0.1), Point(x=0.35, y=0.1)]) incorrect_shape_annotation = Annotation( shape=incorrect_polygon, labels=[ScoredLabel(incorrect_shape_label)], id=ID("incorrect_shape_annotation"), ) dataset_item.append_annotations([incorrect_shape_annotation]) assert ( dataset_item.annotation_scene.annotations == full_box_annotations + normalized_annotations)
def test_shape_entity_not_implemented_methods(self): """ <b>Description:</b> Check not implemented methods of ShapeEntity class <b>Expected results:</b> Test passes if NotImplementedError exception raises when using not implemented methods on ShapeEntity instance """ rectangle_entity = Rectangle(x1=0.2, y1=0.2, x2=0.6, y2=0.7) ellipse_entity = Ellipse(x1=0.4, y1=0.1, x2=0.9, y2=0.8) polygon_entity = Polygon([ Point(0.3, 0.4), Point(0.3, 0.7), Point(0.5, 0.75), Point(0.8, 0.7), Point(0.8, 0.4), ]) for shape in [rectangle_entity, ellipse_entity, polygon_entity]: with pytest.raises(NotImplementedError): ShapeEntity.get_area(shape) with pytest.raises(NotImplementedError): ShapeEntity.intersects(shape, shape) with pytest.raises(NotImplementedError): ShapeEntity.intersect_percentage(shape, shape) with pytest.raises(NotImplementedError): ShapeEntity.get_labels(shape) with pytest.raises(NotImplementedError): ShapeEntity.append_label( shape, ScoredLabel( LabelEntity(name="classification", domain=Domain.CLASSIFICATION)), ) with pytest.raises(NotImplementedError): ShapeEntity.set_labels( shape, [ ScoredLabel( LabelEntity(name="detection", domain=Domain.DETECTION)) ], ) with pytest.raises(NotImplementedError): ShapeEntity.normalize_wrt_roi_shape(shape, rectangle_entity) with pytest.raises(NotImplementedError): ShapeEntity.denormalize_wrt_roi_shape(shape, rectangle_entity) with pytest.raises(NotImplementedError): ShapeEntity._as_shapely_polygon(shape)
def test_polygon_shape_conversion(self): """ <b>Description:</b> Checks that conversions from Polygon to other shapes works correctly <b>Input data:</b> A Polygon at [[0.01, 0.2], [0.35, 0.2], [0.35, 0.4]] <b>Expected results:</b> The test passes if the Polygon can be converted to Rectangle and Ellipse <b>Steps</b> 1. Create rectangle and get coordinates 2. Convert to Ellipse 3. Convert to Polygon 4. Convert to Rectangle """ point1 = Point(x=0.01, y=0.2) point2 = Point(x=0.35, y=0.2) point3 = Point(x=0.35, y=0.4) polygon = Polygon(points=[point1, point2, point3]) polygon_coords = (polygon.min_x, polygon.min_y, polygon.max_x, polygon.max_y) ellipse = ShapeFactory.shape_as_ellipse(polygon) assert isinstance(ellipse, Ellipse) assert (ellipse.x1, ellipse.y1, ellipse.x2, ellipse.y2) == polygon_coords polygon2 = ShapeFactory.shape_as_polygon(polygon) assert isinstance(polygon2, Polygon) assert polygon == polygon2 rectangle = ShapeFactory.shape_as_rectangle(polygon) assert isinstance(rectangle, Rectangle) assert ( rectangle.x1, rectangle.y1, rectangle.x2, rectangle.y2, ) == polygon_coords
def shape_as_polygon(shape: ShapeEntity) -> Polygon: """ Return a shape converted as polygon. For a rectangle, a polygon will be constructed with a point in each corner. For a ellipse, 360 points will be made. Otherwise, the original shape will be returned. The width/height for the parent need to be specified to make sure the aspect ratio is maintained. :param shape: Shape to convert to polygon :return: Polygon """ if isinstance(shape, Polygon): new_shape = shape elif isinstance(shape, Rectangle): points = [ Point(x=shape.x1, y=shape.y1), Point(x=shape.x2, y=shape.y1), Point(x=shape.x2, y=shape.y2), Point(x=shape.x1, y=shape.y2), Point(x=shape.x1, y=shape.y1), ] new_shape = Polygon(points=points) elif isinstance(shape, Ellipse): coordinates = shape.get_evenly_distributed_ellipse_coordinates() points = [Point(x=point[0], y=point[1]) for point in coordinates] new_shape = Polygon(points=points) else: raise NotImplementedError( f"Conversion of a {type(shape)} to a polygon is not implemented yet: " f"{shape}") return new_shape
def fully_covering_polygon() -> Polygon: return Polygon([ Point(0.0, 0.1), Point(0.0, 0.9), Point(0.5, 1.0), Point(1.0, 0.9), Point(1.0, 0.0), Point(0.0, 0.1), ])
def base_self_intersect_polygon() -> Polygon: return Polygon([ Point(0.3, 0.3), Point(0.4, 0.3), Point(0.3, 0.3), Point(0.3, 0.2), Point(0.3, 1), Point(0.2, 0.2), ])
def other_self_intersect_polygon() -> Polygon: return Polygon([ Point(0.3, 0.2), Point(0.2, 0.3), Point(0.3, 0.1), Point(0.3, 0.2), Point(0, 0.2), Point(0, 4), ])
def not_inscribed_polygon() -> Polygon: return Polygon([ Point(0.0, 0.0), Point(0.0, 0.01), Point(0.01, 0.02), Point(0.02, 0.01), Point(0.02, 0.0), Point(0.0, 0.0), ])
def polygon(self) -> Polygon: return Polygon( [ Point(0.3, 0.4), Point(0.3, 0.7), Point(0.5, 0.75), Point(0.8, 0.7), Point(0.8, 0.4), Point(0.3, 0.4), ], labels=self.generate_labels_list(), )
def upper_side_intersect_shapes() -> list: return [ Rectangle(x1=0.2, y1=0.4, x2=0.5, y2=0.7), Polygon([ Point(0.35, 0.7), Point(0.2, 0.6), Point(0.2, 0.4), Point(0.5, 0.4), Point(0.5, 0.6), Point(0.35, 0.7), ]), ]
def lower_side_intersect_shapes() -> list: return [ Rectangle(x1=0.2, y1=0.1, x2=0.5, y2=0.4), Polygon([ Point(0.35, 0.1), Point(0.2, 0.2), Point(0.2, 0.4), Point(0.5, 0.4), Point(0.5, 0.2), Point(0.35, 0.1), ]), ]
def point(self): return Point(*self.coordinates())
def other_point(self): return Point(*self.other_coordinates())
def other_points(self): point1 = Point(0.3, 0.1) point2 = Point(0.8, 0.3) point3 = Point(0.6, 0.2) return [point1, point2, point3]
class TestAnnotationSceneEntity: creation_date = now() labels: List[ScoredLabel] = [] rectangle = Rectangle(x1=0.5, x2=1.0, y1=0.0, y2=0.5) annotation = Annotation(shape=rectangle, labels=labels) point1 = Point(0.3, 0.1) point2 = Point(0.8, 0.3) point3 = Point(0.6, 0.2) points = [point1, point2, point3] polygon = Polygon(points=points) annotation2 = Annotation(shape=polygon, labels=labels) annotations = [annotation, annotation2] annotation_scene_entity = AnnotationSceneEntity( annotations=annotations, kind=AnnotationSceneKind.ANNOTATION) @pytest.mark.priority_medium @pytest.mark.component @pytest.mark.reqids(Requirements.REQ_1) def test_annotation_scene_entity_default_value(self): """ <b>Description:</b> Check that AnnotationSceneEntity default values <b>Input data:</b> AnnotationSceneEntity class <b>Expected results:</b> Test passes if the AnnotationSceneEntity return correct values <b>Steps</b> 1. Create AnnotationSceneEntity instances 2. Check default values """ annotation_scene_entity = self.annotation_scene_entity assert annotation_scene_entity.id == ID() assert annotation_scene_entity.kind == AnnotationSceneKind.ANNOTATION assert annotation_scene_entity.editor_name == "" assert type(annotation_scene_entity.creation_date) == datetime.datetime assert "Annotation(shape=Rectangle" in str( annotation_scene_entity.annotations) assert "Annotation(shape=Polygon" in str( annotation_scene_entity.annotations) assert annotation_scene_entity.shapes == [self.rectangle, self.polygon] @pytest.mark.priority_medium @pytest.mark.component @pytest.mark.reqids(Requirements.REQ_1) def test_annotation_scene_entity_setters(self): """ <b>Description:</b> Check that AnnotationSceneEntity can correctly return modified property value <b>Input data:</b> Annotation class <b>Expected results:</b> Test passes if the AnnotationSceneEntity return correct values <b>Steps</b> 1. Create AnnotationSceneEntity instances 2. Set another values 3. Check changed values """ annotation_scene_entity = self.annotation_scene_entity creation_date = self.creation_date annotation_scene_entity.id = ID(123456789) annotation_scene_entity.kind = AnnotationSceneKind.PREDICTION annotation_scene_entity.editor_name = "editor" annotation_scene_entity.creation_date = creation_date annotation_scene_entity.annotations = self.annotation assert annotation_scene_entity.id == ID(123456789) assert annotation_scene_entity.kind == AnnotationSceneKind.PREDICTION assert annotation_scene_entity.editor_name == "editor" assert annotation_scene_entity.creation_date == creation_date assert annotation_scene_entity.annotations == self.annotation @pytest.mark.priority_medium @pytest.mark.component @pytest.mark.reqids(Requirements.REQ_1) def test_annotation_scene_entity_magic_methods(self): """ <b>Description:</b> Check Annotation __repr__ method <b>Input data:</b> Initialized instance of AnnotationSceneEntity <b>Expected results:</b> Test passes if AnnotationSceneEntity magic method returns correct values <b>Steps</b> 1. Create AnnotationSceneEntity instances 2. Check returning value of magic method """ annotation_scene_entity = self.annotation_scene_entity annotation_scene_entity_repr = [ f"{annotation_scene_entity.__class__.__name__}(" f"annotations={annotation_scene_entity.annotations}, " f"kind={annotation_scene_entity.kind}, " f"editor={annotation_scene_entity.editor_name}, " f"creation_date={annotation_scene_entity.creation_date}, " f"id={annotation_scene_entity.id})" ] for i in annotation_scene_entity_repr: assert i in repr(annotation_scene_entity) @pytest.mark.priority_medium @pytest.mark.component @pytest.mark.reqids(Requirements.REQ_1) def test_annotation_scene_entity_contains_any(self): """ <b>Description:</b> Check Annotation contains_any method <b>Input data:</b> Initialized instance of AnnotationSceneEntity <b>Expected results:</b> Test passes if AnnotationSceneEntity contains_any method returns correct values <b>Steps</b> 1. Create AnnotationSceneEntity instances 2. Check returning value of contains_any method """ annotation_scene_entity = self.annotation_scene_entity annotation_scene_entity.annotations = self.annotations car = LabelEntity(name="car", domain=Domain.DETECTION, is_empty=True) person = LabelEntity(name="person", domain=Domain.DETECTION) tree = LabelEntity(name="tree", domain=Domain.DETECTION) car_label = ScoredLabel(car) person_label = ScoredLabel(person) tree_label = ScoredLabel(tree) labels = [car_label] labels2 = [car_label, person_label] annotation = Annotation(shape=self.rectangle, labels=labels2) annotations = [annotation] annotation_scene_entity2 = AnnotationSceneEntity( annotations=annotations, kind=AnnotationSceneKind.ANNOTATION) assert annotation_scene_entity.contains_any(labels=labels) is False assert annotation_scene_entity2.contains_any(labels=labels2) is True assert annotation_scene_entity2.contains_any( labels=[tree_label]) is False @pytest.mark.priority_medium @pytest.mark.component @pytest.mark.reqids(Requirements.REQ_1) def test_annotation_scene_entity_append_annotation(self): """ <b>Description:</b> Check Annotation append_annotation method <b>Input data:</b> Initialized instance of AnnotationSceneEntity <b>Expected results:</b> Test passes if AnnotationSceneEntity append_annotation method returns correct values <b>Steps</b> 1. Create AnnotationSceneEntity instances 2. Check returning value of append_annotation method """ annotation_scene_entity = self.annotation_scene_entity tree = LabelEntity(name="tree", domain=Domain.DETECTION) tree_label = ScoredLabel(tree) labels = [tree_label] annotation = Annotation(shape=self.rectangle, labels=labels) assert len(annotation_scene_entity.annotations) == 2 annotation_scene_entity.append_annotation(annotation) assert len(annotation_scene_entity.annotations) == 3 @pytest.mark.priority_medium @pytest.mark.component @pytest.mark.reqids(Requirements.REQ_1) def test_annotation_scene_entity_append_annotations(self): """ <b>Description:</b> Check Annotation append_annotations method <b>Input data:</b> Initialized instance of AnnotationSceneEntity <b>Expected results:</b> Test passes if AnnotationSceneEntity append_annotations method returns correct values <b>Steps</b> 1. Create AnnotationSceneEntity instances 2. Check returning value of append_annotations method """ annotation_scene_entity = self.annotation_scene_entity annotation_scene_entity.append_annotations(self.annotations) assert len(annotation_scene_entity.annotations) == 6 @pytest.mark.priority_medium @pytest.mark.component @pytest.mark.reqids(Requirements.REQ_1) def test_annotation_scene_entity_get_labels(self): """ <b>Description:</b> Check Annotation get_labels method <b>Input data:</b> Initialized instance of AnnotationSceneEntity <b>Expected results:</b> Test passes if AnnotationSceneEntity get_labels method returns correct values <b>Steps</b> 1. Create AnnotationSceneEntity instances 2. Check returning value of get_labels method """ annotation_scene_entity = self.annotation_scene_entity assert len(annotation_scene_entity.get_labels()) == 1 assert "name=tree" in str(annotation_scene_entity.get_labels()) @pytest.mark.priority_medium @pytest.mark.component @pytest.mark.reqids(Requirements.REQ_1) def test_annotation_scene_entity_get_label_ids(self): """ <b>Description:</b> Check Annotation get_label_ids method <b>Input data:</b> Initialized instance of AnnotationSceneEntity <b>Expected results:</b> Test passes if AnnotationSceneEntity get_label_ids method returns correct values <b>Steps</b> 1. Create AnnotationSceneEntity instances 2. Check returning value of get_label_ids method """ annotation_scene_entity = self.annotation_scene_entity assert annotation_scene_entity.get_label_ids() == {ID()} bus = LabelEntity(id=ID(123456789), name="bus", domain=Domain.DETECTION) bus_label = ScoredLabel(bus) labels = [bus_label] annotation = Annotation(shape=self.rectangle, labels=labels) annotation_scene_entity.append_annotation(annotation) assert annotation_scene_entity.get_label_ids() == {ID(), ID(123456789)}
def create_annotation_from_segmentation_map( hard_prediction: np.ndarray, soft_prediction: np.ndarray, label_map: dict) -> List[Annotation]: """ Creates polygons from the soft predictions. Background label will be ignored and not be converted to polygons. :param hard_prediction: hard prediction containing the final label index per pixel. See function `create_hard_prediction_from_soft_prediction`. :param soft_prediction: soft prediction with shape H x W x N_labels, where soft_prediction[:, :, 0] is the soft prediction for background. If soft_prediction is of H x W shape, it is assumed that this soft prediction will be applied for all labels. :param label_map: dictionary mapping labels to an index. It is assumed that the first item in the dictionary corresponds to the background label and will therefore be ignored. :return: List of shapes """ # pylint: disable=too-many-locals height, width = hard_prediction.shape[:2] img_class = hard_prediction.swapaxes(0, 1) # pylint: disable=too-many-nested-blocks annotations: List[Annotation] = [] for label_index, label in label_map.items(): # Skip background if label_index == 0: continue # obtain current label soft prediction if len(soft_prediction.shape) == 3: current_label_soft_prediction = soft_prediction[:, :, label_index] else: current_label_soft_prediction = soft_prediction obj_group = img_class == label_index label_index_map = (obj_group.T.astype(int) * 255).astype(np.uint8) # Contour retrieval mode CCOMP (Connected components) creates a two-level # hierarchy of contours contours, hierarchies = cv2.findContours(label_index_map, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE) if hierarchies is not None: for contour, hierarchy in zip(contours, hierarchies[0]): if hierarchy[3] == -1: # In this case a contour does not represent a hole contour = list( (point[0][0], point[0][1]) for point in contour) # Split contour into subcontours that do not have self intersections. subcontours = get_subcontours(contour) for subcontour in subcontours: # compute probability of the shape mask = np.zeros(hard_prediction.shape, dtype=np.uint8) cv2.drawContours( mask, np.asarray([[[x, y]] for x, y in subcontour]), contourIdx=-1, color=1, thickness=-1, ) probability = cv2.mean(current_label_soft_prediction, mask)[0] # convert the list of points to a closed polygon points = [ Point(x=x / width, y=y / height) for x, y in subcontour ] polygon = Polygon(points=points) if polygon.get_area() > 0: # Contour is a closed polygon with area > 0 annotations.append( Annotation( shape=polygon, labels=[ScoredLabel(label, probability)], id=ID(ObjectId()), )) else: # Contour is a closed polygon with area == 0 warnings.warn( "The geometry of the segmentation map you are converting " "is not fully supported. Polygons with a area of zero " "will be removed.", UserWarning, ) else: # If contour hierarchy[3] != -1 then contour has a parent and # therefore is a hole # Do not allow holes in segmentation masks to be filled silently, # but trigger warning instead warnings.warn( "The geometry of the segmentation map you are converting is " "not fully supported. A hole was found and will be filled.", UserWarning, ) return annotations
def test_dataset_item_roi_numpy(self): """ <b>Description:</b> Check DatasetItemEntity class "roi_numpy" method <b>Input data:</b> DatasetItemEntity class object with specified "media", "annotation_scene", "roi", "metadata" and "subset" parameters <b>Expected results:</b> Test passes if array returned by "roi_numpy" method is equal to expected <b>Steps</b> 1. Check array returned by roi_numpy method with not specified "roi" parameter for DatasetItemEntity with "roi" attribute is "None" 2. Check array returned by roi_numpy method with Rectangle-shape "roi" parameter 3. Check array returned by roi_numpy method with Ellipse-shape "roi" parameter 4. Check array returned by roi_numpy method with Polygon-shape "roi" parameter 5. Check array returned by roi_numpy method with non-specified "roi" parameter for DatasetItemEntity with "roi" attribute """ media = DatasetItemParameters.generate_random_image() annotation_scene = DatasetItemParameters().annotations_entity() roi_label = LabelEntity("ROI label", Domain.DETECTION, id=ID("roi_label")) dataset_item = DatasetItemEntity(media, annotation_scene) # Checking array returned by "roi_numpy" method with non-specified "roi" parameter for DatasetItemEntity # "roi" attribute is "None" assert np.array_equal(dataset_item.roi_numpy(), media.numpy) # Checking array returned by "roi_numpy" method with specified Rectangle-shape "roi" parameter rectangle_roi = Annotation( Rectangle(x1=0.2, y1=0.1, x2=0.8, y2=0.9), [ScoredLabel(roi_label)], ID("rectangle_roi"), ) assert np.array_equal(dataset_item.roi_numpy(rectangle_roi), media.numpy[1:9, 3:13]) # Checking array returned by "roi_numpy" method with specified Ellipse-shape "roi" parameter ellipse_roi = Annotation( Ellipse(x1=0.1, y1=0.0, x2=0.9, y2=0.8), [ScoredLabel(roi_label)], ID("ellipse_roi"), ) assert np.array_equal(dataset_item.roi_numpy(ellipse_roi), media.numpy[0:8, 2:14]) # Checking array returned by "roi_numpy" method with specified Polygon-shape "roi" parameter polygon_roi = Annotation( shape=Polygon([ Point(0.3, 0.4), Point(0.3, 0.7), Point(0.5, 0.75), Point(0.8, 0.7), Point(0.8, 0.4), ]), labels=[], id=ID("polygon_roi"), ) assert np.array_equal(dataset_item.roi_numpy(polygon_roi), media.numpy[4:8, 5:13]) # Checking array returned by "roi_numpy" method with not specified "roi" parameter for DatasetItemEntity with # "roi" attribute roi_specified_dataset_item = DatasetItemEntity( media, annotation_scene, DatasetItemParameters().roi()) roi_specified_dataset_item.roi_numpy() assert np.array_equal(roi_specified_dataset_item.roi_numpy(), media.numpy[1:9, 2:14])
def generate_random_annotated_image( image_width: int, image_height: int, labels: Sequence[LabelEntity], min_size=50, max_size=250, shape: Optional[str] = None, max_shapes: int = 10, intensity_range: List[Tuple[int, int]] = None, random_seed: Optional[int] = None, ) -> Tuple[np.ndarray, List[Annotation]]: """ Generate a random image with the corresponding annotation entities. :param intensity_range: Intensity range for RGB channels ((r_min, r_max), (g_min, g_max), (b_min, b_max)) :param max_shapes: Maximum amount of shapes in the image :param shape: {"rectangle", "ellipse", "triangle"} :param image_height: Height of the image :param image_width: Width of the image :param labels: Task Labels that should be applied to the respective shape :param min_size: Minimum size of the shape(s) :param max_size: Maximum size of the shape(s) :param random_seed: Seed to initialize the random number generator :return: uint8 array, list of shapes """ from skimage.draw import random_shapes, rectangle if intensity_range is None: intensity_range = [(100, 200)] image1: Optional[np.ndarray] = None sc_labels = [] # Sporadically, it might happen there is no shape in the image, especially on low-res images. # It'll retry max 5 times until we see a shape, and otherwise raise a runtime error if ( shape == "ellipse" ): # ellipse shape is not available in random_shapes function. use circle instead shape = "circle" for _ in range(5): rand_image, sc_labels = random_shapes( (image_height, image_width), min_shapes=1, max_shapes=max_shapes, intensity_range=intensity_range, min_size=min_size, max_size=max_size, shape=shape, random_seed=random_seed, ) num_shapes = len(sc_labels) if num_shapes > 0: image1 = rand_image break if image1 is None: raise RuntimeError( "Was not able to generate a random image that contains any shapes") annotations: List[Annotation] = [] for sc_label in sc_labels: sc_label_name = sc_label[0] sc_label_shape_r = sc_label[1][0] sc_label_shape_c = sc_label[1][1] y_min, y_max = max(0.0, float(sc_label_shape_r[0] / image_height)), min( 1.0, float(sc_label_shape_r[1] / image_height)) x_min, x_max = max(0.0, float(sc_label_shape_c[0] / image_width)), min( 1.0, float(sc_label_shape_c[1] / image_width)) if sc_label_name == "ellipse": # Fix issue with newer scikit-image libraries that generate ellipses. # For now we render a rectangle on top of it sc_label_name = "rectangle" rr, cc = rectangle( start=(sc_label_shape_r[0], sc_label_shape_c[0]), end=(sc_label_shape_r[1] - 1, sc_label_shape_c[1] - 1), shape=image1.shape, ) image1[rr, cc] = ( random.randint(0, 200), # nosec random.randint(0, 200), # nosec random.randint(0, 200), # nosec ) if sc_label_name == "circle": sc_label_name = "ellipse" label_matches = [ label for label in labels if sc_label_name == label.name ] if len(label_matches) > 0: label = label_matches[0] box_annotation = Annotation( Rectangle(x1=x_min, y1=y_min, x2=x_max, y2=y_max), labels=[ScoredLabel(label, probability=1.0)], ) annotation: Annotation if label.name == "ellipse": annotation = Annotation( Ellipse( x1=box_annotation.shape.x1, y1=box_annotation.shape.y1, x2=box_annotation.shape.x2, y2=box_annotation.shape.y2, ), labels=box_annotation.get_labels(include_empty=True), ) elif label.name == "triangle": points = [ Point( x=(box_annotation.shape.x1 + box_annotation.shape.x2) / 2, y=box_annotation.shape.y1, ), Point(x=box_annotation.shape.x1, y=box_annotation.shape.y2), Point(x=box_annotation.shape.x2, y=box_annotation.shape.y2), ] annotation = Annotation( Polygon(points=points), labels=box_annotation.get_labels(include_empty=True), ) else: annotation = box_annotation annotations.append(annotation) else: logger.warning( "Generated a random image, but was not able to associate a label with a shape. " f"The name of the shape was `{sc_label_name}`. ") return image1, annotations
def points(self): point1 = Point(0.5, 0.0) point2 = Point(0.75, 0.2) point3 = Point(0.6, 0.1) return [point1, point2, point3]