Skip to content

Shapes

siapy.entities.shapes

ShapeType module-attribute

ShapeType = Literal[
    _SHAPE_TYPE_RECTANGLE,
    _SHAPE_TYPE_POINT,
    _SHAPE_TYPE_FREEDRAW,
]

Shape dataclass

Shape(
    shape_type: ShapeType,
    pixels: Pixels,
    label: str | None = None,
)

Bases: ABC

Source code in siapy/entities/shapes.py
32
33
34
35
36
37
38
39
40
def __init__(
    self,
    shape_type: ShapeType,
    pixels: Pixels,
    label: str | None = None,
):
    self._shape_type = shape_type
    self._pixels = pixels
    self._label = label

shape_type property

shape_type: str

pixels property

pixels: Pixels

label property

label: str | None

from_shape_type classmethod

from_shape_type(
    shape_type: ShapeType,
    pixels: Pixels,
    label: str | None = None,
) -> Shape
Source code in siapy/entities/shapes.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@classmethod
def from_shape_type(
    cls,
    shape_type: ShapeType,
    pixels: Pixels,
    label: str | None = None,
) -> "Shape":
    types_map: dict[ShapeType, type[Shape]] = {
        _SHAPE_TYPE_RECTANGLE: Rectangle,
        _SHAPE_TYPE_POINT: Point,
        _SHAPE_TYPE_FREEDRAW: FreeDraw,
    }
    if shape_type in types_map:
        return types_map[shape_type](
            shape_type=shape_type,
            pixels=pixels,
            label=label,
        )
    else:
        raise InvalidInputError(
            {
                "shape_type": shape_type,
            },
            f"Unsupported shape type: {shape_type}",
        )

convex_hull abstractmethod

convex_hull()
Source code in siapy/entities/shapes.py
80
81
82
@abstractmethod
def convex_hull(self):
    raise MethodNotImplementedError(self.__class__.__name__, "convex_hull")

GeometricShapes dataclass

GeometricShapes(
    image: SpectralImage,
    geometric_shapes: list[Shape] | None = None,
)
Source code in siapy/entities/shapes.py
87
88
89
90
91
92
93
def __init__(
    self,
    image: "SpectralImage",
    geometric_shapes: list["Shape"] | None = None,
):
    self._image = image
    self._geometric_shapes = geometric_shapes if geometric_shapes is not None else []

shapes property writable

shapes: list[Shape]

append

append(shape: Shape)
Source code in siapy/entities/shapes.py
122
123
124
def append(self, shape: "Shape"):
    self._check_shape_type(shape)
    self._geometric_shapes.append(shape)

extend

extend(shapes: Iterable[Shape])
Source code in siapy/entities/shapes.py
126
127
128
def extend(self, shapes: Iterable["Shape"]):
    self._check_shape_type(shapes)
    self._geometric_shapes.extend(shapes)

insert

insert(index: int, shape: Shape)
Source code in siapy/entities/shapes.py
130
131
132
def insert(self, index: int, shape: "Shape"):
    self._check_shape_type(shape)
    self._geometric_shapes.insert(index, shape)

remove

remove(shape: Shape)
Source code in siapy/entities/shapes.py
134
135
136
def remove(self, shape: "Shape"):
    self._check_shape_type(shape)
    self._geometric_shapes.remove(shape)

pop

pop(index: int = -1) -> Shape
Source code in siapy/entities/shapes.py
138
139
def pop(self, index: int = -1) -> "Shape":
    return self._geometric_shapes.pop(index)

clear

clear()
Source code in siapy/entities/shapes.py
141
142
def clear(self):
    self._geometric_shapes.clear()

index

index(
    shape: Shape, start: int = 0, stop: int = maxsize
) -> int
Source code in siapy/entities/shapes.py
144
145
146
def index(self, shape: "Shape", start: int = 0, stop: int = sys.maxsize) -> int:
    self._check_shape_type(shape)
    return self._geometric_shapes.index(shape, start, stop)

count

count(shape: Shape) -> int
Source code in siapy/entities/shapes.py
148
149
150
def count(self, shape: "Shape") -> int:
    self._check_shape_type(shape)
    return self._geometric_shapes.count(shape)

reverse

reverse()
Source code in siapy/entities/shapes.py
152
153
def reverse(self):
    self._geometric_shapes.reverse()

sort

sort(key: Any = None, reverse: bool = False)
Source code in siapy/entities/shapes.py
155
156
def sort(self, key: Any = None, reverse: bool = False):
    self._geometric_shapes.sort(key=key, reverse=reverse)

get_by_name

get_by_name(name: str) -> Shape | None
Source code in siapy/entities/shapes.py
158
159
160
161
162
163
def get_by_name(self, name: str) -> Shape | None:
    names = [shape.label for shape in self.shapes]
    if name in names:
        index = names.index(name)
        return self.shapes[index]
    return None

Rectangle

Rectangle(
    pixels: Pixels, label: str | None = None, **kwargs: Any
)

Bases: Shape

Source code in siapy/entities/shapes.py
186
187
def __init__(self, pixels: Pixels, label: str | None = None, **kwargs: Any):
    super().__init__(_SHAPE_TYPE_RECTANGLE, pixels, label)

shape_type property

shape_type: str

pixels property

pixels: Pixels

label property

label: str | None

from_shape_type classmethod

from_shape_type(
    shape_type: ShapeType,
    pixels: Pixels,
    label: str | None = None,
) -> Shape
Source code in siapy/entities/shapes.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@classmethod
def from_shape_type(
    cls,
    shape_type: ShapeType,
    pixels: Pixels,
    label: str | None = None,
) -> "Shape":
    types_map: dict[ShapeType, type[Shape]] = {
        _SHAPE_TYPE_RECTANGLE: Rectangle,
        _SHAPE_TYPE_POINT: Point,
        _SHAPE_TYPE_FREEDRAW: FreeDraw,
    }
    if shape_type in types_map:
        return types_map[shape_type](
            shape_type=shape_type,
            pixels=pixels,
            label=label,
        )
    else:
        raise InvalidInputError(
            {
                "shape_type": shape_type,
            },
            f"Unsupported shape type: {shape_type}",
        )

convex_hull

convex_hull() -> Pixels
Source code in siapy/entities/shapes.py
189
190
191
192
193
194
195
196
197
198
def convex_hull(self) -> Pixels:
    # Rectangle is defined by two opposite corners
    u1, u2 = self.pixels.u()
    v1, v2 = self.pixels.v()

    pixels_inside = []
    for u_coord in range(min(u1, u2), max(u1, u2) + 1):
        for v_coord in range(min(v1, v2), max(v1, v2) + 1):
            pixels_inside.append((u_coord, v_coord))
    return Pixels.from_iterable(pixels_inside)

Point

Point(
    pixels: Pixels, label: str | None = None, **kwargs: Any
)

Bases: Shape

Source code in siapy/entities/shapes.py
202
203
def __init__(self, pixels: Pixels, label: str | None = None, **kwargs: Any):
    super().__init__(_SHAPE_TYPE_POINT, pixels, label)

shape_type property

shape_type: str

pixels property

pixels: Pixels

label property

label: str | None

from_shape_type classmethod

from_shape_type(
    shape_type: ShapeType,
    pixels: Pixels,
    label: str | None = None,
) -> Shape
Source code in siapy/entities/shapes.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@classmethod
def from_shape_type(
    cls,
    shape_type: ShapeType,
    pixels: Pixels,
    label: str | None = None,
) -> "Shape":
    types_map: dict[ShapeType, type[Shape]] = {
        _SHAPE_TYPE_RECTANGLE: Rectangle,
        _SHAPE_TYPE_POINT: Point,
        _SHAPE_TYPE_FREEDRAW: FreeDraw,
    }
    if shape_type in types_map:
        return types_map[shape_type](
            shape_type=shape_type,
            pixels=pixels,
            label=label,
        )
    else:
        raise InvalidInputError(
            {
                "shape_type": shape_type,
            },
            f"Unsupported shape type: {shape_type}",
        )

convex_hull

convex_hull() -> Pixels
Source code in siapy/entities/shapes.py
205
206
def convex_hull(self) -> Pixels:
    return self.pixels

FreeDraw

FreeDraw(
    pixels: Pixels, label: str | None = None, **kwargs: Any
)

Bases: Shape

Source code in siapy/entities/shapes.py
210
211
def __init__(self, pixels: Pixels, label: str | None = None, **kwargs: Any):
    super().__init__(_SHAPE_TYPE_FREEDRAW, pixels, label)

shape_type property

shape_type: str

pixels property

pixels: Pixels

label property

label: str | None

from_shape_type classmethod

from_shape_type(
    shape_type: ShapeType,
    pixels: Pixels,
    label: str | None = None,
) -> Shape
Source code in siapy/entities/shapes.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@classmethod
def from_shape_type(
    cls,
    shape_type: ShapeType,
    pixels: Pixels,
    label: str | None = None,
) -> "Shape":
    types_map: dict[ShapeType, type[Shape]] = {
        _SHAPE_TYPE_RECTANGLE: Rectangle,
        _SHAPE_TYPE_POINT: Point,
        _SHAPE_TYPE_FREEDRAW: FreeDraw,
    }
    if shape_type in types_map:
        return types_map[shape_type](
            shape_type=shape_type,
            pixels=pixels,
            label=label,
        )
    else:
        raise InvalidInputError(
            {
                "shape_type": shape_type,
            },
            f"Unsupported shape type: {shape_type}",
        )

convex_hull

convex_hull() -> Pixels
Source code in siapy/entities/shapes.py
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
def convex_hull(self) -> Pixels:
    if len(self.pixels) < 3:
        return self.pixels

    points = self.pixels.to_numpy()
    points_path = Path(points)

    # Create a grid of points that covers the convex hull area
    u_min, v_min = points.min(axis=0)
    u_max, v_max = points.max(axis=0)
    u, v = np.meshgrid(np.arange(u_min, u_max + 1), np.arange(v_min, v_max + 1))
    grid_points = np.vstack((u.flatten(), v.flatten())).T

    # Filter points that are inside the convex hull
    inside_points = grid_points[points_path.contains_points(grid_points)]

    return Pixels.from_iterable(inside_points.tolist())