Example #1
0
    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
Example #2
0
    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)
Example #3
0
    def test_polygon(self):
        """
        <b>Description:</b>
        Check Polygon parameters

        <b>Input data:</b>
        Points

        <b>Expected results:</b>
        Test passes if Polygon correctly calculates parameters and returns default values

        <b>Steps</b>
        1. Check Polygon params
        2. Check Polygon default values
        3. Check Polygon with empty points
        """

        polygon = self.polygon()
        modification_date = self.modification_date
        assert len(polygon.points) == 3
        assert polygon.modification_date == modification_date
        assert polygon.points == self.points()

        empty_points_list = []
        with pytest.raises(ValueError):
            Polygon(empty_points_list)
    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
Example #5
0
    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)
Example #6
0
 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),
     ])
Example #7
0
 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),
     ])
Example #8
0
 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),
     ])
Example #9
0
 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),
     ])
Example #10
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(),
     )
Example #11
0
 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),
         ]),
     ]
Example #12
0
 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),
         ]),
     ]
Example #13
0
    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)
Example #14
0
    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
Example #15
0
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
Example #16
0
 def other_polygon(self):
     return Polygon(self.other_points(),
                    modification_date=self.modification_date)
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)}
Example #18
0
    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
Example #20
0
 def polygon(self):
     return Polygon(self.points(), modification_date=self.modification_date)