Skip to content

himena.standards.roi

Standard ROI (Region of Interest) classes for images.

CircleRoi

ROI that represents a circle.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
x int | float

X-coordinate of the center.

required
y int | float

Y-coordinate of the center.

required
radius int | float

Radius of the circle.

required
Source code in src\himena\standards\roi\core.py
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
class CircleRoi(Roi2D):
    """ROI that represents a circle."""

    x: Scalar = Field(..., description="X-coordinate of the center.")
    y: Scalar = Field(..., description="Y-coordinate of the center.")
    radius: Scalar = Field(..., description="Radius of the circle.")

    def shifted(self, dx: float, dy: float) -> CircleRoi:
        return self.model_copy(update={"x": self.x + dx, "y": self.y + dy})

    def area(self) -> float:
        return math.pi * self.radius**2

    def circumference(self) -> float:
        return 2 * math.pi * self.radius

    def bbox(self) -> Rect[float]:
        return Rect(
            self.x - self.radius,
            self.y - self.radius,
            2 * self.radius,
            2 * self.radius,
        )

    def to_mask(self, shape: tuple[int, ...]) -> NDArray[np.bool_]:
        _yy, _xx = np.indices(shape[-2:])
        comp_a = (_yy - self.y) / self.radius
        comp_b = (_xx - self.x) / self.radius
        return comp_a**2 + comp_b**2 <= 1

EllipseRoi

ROI that represents an ellipse.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
x int | float

X-coordinate of the left boundary.

required
y int | float

Y-coordinate of the top boundary.

required
width int | float

Diameter along the x-axis.

required
height int | float

Diameter along the y-axis.

required
Source code in src\himena\standards\roi\core.py
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
class EllipseRoi(Roi2D):
    """ROI that represents an ellipse."""

    x: Scalar = Field(..., description="X-coordinate of the left boundary.")
    y: Scalar = Field(..., description="Y-coordinate of the top boundary.")
    width: Scalar = Field(..., description="Diameter along the x-axis.")
    height: Scalar = Field(..., description="Diameter along the y-axis.")

    def center(self) -> tuple[float, float]:
        return self.x + self.width / 2, self.y + self.height / 2

    def shifted(self, dx: float, dy: float) -> EllipseRoi:
        return self.model_copy(update={"x": self.x + dx, "y": self.y + dy})

    def area(self) -> float:
        return math.pi * self.width * self.height / 4

    def circumference(self) -> float:
        a, b = self.width / 2, self.height / 2
        return math.pi * (3 * (a + b) - math.sqrt((3 * a + b) * (a + 3 * b)))

    def eccentricity(self) -> float:
        """Eccentricity of the ellipse."""
        a, b = sorted([self.width / 2, self.height / 2])
        if b == 0:
            return 0.0
        return math.sqrt(1 - a**2 / b**2)

    def bbox(self) -> Rect[float]:
        return Rect(self.x, self.y, self.width, self.height)

    def to_mask(self, shape: tuple[int, ...]) -> NDArray[np.bool_]:
        _yy, _xx = np.indices(shape[-2:])
        cx, cy = self.center()
        if self.height == 0 or self.width == 0:
            return np.zeros(shape, dtype=bool)
        comp_a = (_yy - cy) / self.height * 2
        comp_b = (_xx - cx) / self.width * 2
        return comp_a**2 + comp_b**2 <= 1
eccentricity()

Eccentricity of the ellipse.

Source code in src\himena\standards\roi\core.py
164
165
166
167
168
169
def eccentricity(self) -> float:
    """Eccentricity of the ellipse."""
    a, b = sorted([self.width / 2, self.height / 2])
    if b == 0:
        return 0.0
    return math.sqrt(1 - a**2 / b**2)

LineRoi

A 2D line ROI.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
start tuple[float, float]

(X, Y) coordinate of the start point.

required
end tuple[float, float]

(X, Y) coordinate of the end point.

required
Source code in src\himena\standards\roi\core.py
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
class LineRoi(Roi2D):
    """A 2D line ROI."""

    start: tuple[float, float] = Field(..., description="(X, Y) coordinate of the start point.")  # fmt: skip
    end: tuple[float, float] = Field(..., description="(X, Y) coordinate of the end point.")  # fmt: skip

    @property
    def x1(self) -> float:
        return self.start[0]

    @property
    def y1(self) -> float:
        return self.start[1]

    @property
    def x2(self) -> float:
        return self.end[0]

    @property
    def y2(self) -> float:
        return self.end[1]

    def shifted(self, dx: float, dy: float) -> LineRoi:
        """Shift the line by the given amount."""
        return LineRoi(
            start=(self.x1 + dx, self.y1 + dy),
            end=(self.x2 + dx, self.y2 + dy),
        )

    def length(self) -> float:
        """Length of the line."""
        dx = self.x2 - self.x1
        dy = self.y2 - self.y1
        return math.hypot(dx, dy)

    def angle(self) -> float:
        """Angle in degrees."""
        return math.degrees(self.angle_radian())

    def angle_radian(self) -> float:
        dx = self.x2 - self.x1
        dy = self.y1 - self.y2  # NOTE: invert y so that angle is CCW
        return math.atan2(dy, dx)

    def linspace(self, num: int) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
        """Return a tuple of x and y coordinates of np.linspace along the line."""
        return np.linspace(self.x1, self.x2, num), np.linspace(self.y1, self.y2, num)

    def arange(
        self, step: float = 1.0
    ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
        """Return a tuple of x and y coordinates of np.arange along the line."""
        radian = -self.angle_radian()
        num, rem = divmod(self.length(), step)
        xrem = rem * math.cos(radian)
        yrem = rem * math.sin(radian)
        return (
            np.linspace(self.x1, self.x2 - xrem, int(num) + 1),
            np.linspace(self.y1, self.y2 - yrem, int(num) + 1),
        )

    def bbox(self) -> Rect[float]:
        xmin, xmax = min(self.x1, self.x2), max(self.x1, self.x2)
        ymin, ymax = min(self.y1, self.y2), max(self.y1, self.y2)
        return Rect(xmin, ymin, xmax - xmin, ymax - ymin)

    def to_mask(self, shape: tuple[int, ...]) -> NDArray[np.bool_]:
        arr = np.zeros(shape, dtype=bool)
        xs, ys = self.linspace(int(self.length() + 1))
        xs = xs.round().astype(int)
        ys = ys.round().astype(int)
        arr[ys, xs] = True
        return arr
angle()

Angle in degrees.

Source code in src\himena\standards\roi\core.py
325
326
327
def angle(self) -> float:
    """Angle in degrees."""
    return math.degrees(self.angle_radian())
arange(step=1.0)

Return a tuple of x and y coordinates of np.arange along the line.

Source code in src\himena\standards\roi\core.py
338
339
340
341
342
343
344
345
346
347
348
349
def arange(
    self, step: float = 1.0
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
    """Return a tuple of x and y coordinates of np.arange along the line."""
    radian = -self.angle_radian()
    num, rem = divmod(self.length(), step)
    xrem = rem * math.cos(radian)
    yrem = rem * math.sin(radian)
    return (
        np.linspace(self.x1, self.x2 - xrem, int(num) + 1),
        np.linspace(self.y1, self.y2 - yrem, int(num) + 1),
    )
length()

Length of the line.

Source code in src\himena\standards\roi\core.py
319
320
321
322
323
def length(self) -> float:
    """Length of the line."""
    dx = self.x2 - self.x1
    dy = self.y2 - self.y1
    return math.hypot(dx, dy)
linspace(num)

Return a tuple of x and y coordinates of np.linspace along the line.

Source code in src\himena\standards\roi\core.py
334
335
336
def linspace(self, num: int) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
    """Return a tuple of x and y coordinates of np.linspace along the line."""
    return np.linspace(self.x1, self.x2, num), np.linspace(self.y1, self.y2, num)
shifted(dx, dy)

Shift the line by the given amount.

Source code in src\himena\standards\roi\core.py
312
313
314
315
316
317
def shifted(self, dx: float, dy: float) -> LineRoi:
    """Shift the line by the given amount."""
    return LineRoi(
        start=(self.x1 + dx, self.y1 + dy),
        end=(self.x2 + dx, self.y2 + dy),
    )

PointRoi1D

ROI that represents a point in 1D space.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
x float

X-coordinate of the point.

required
Source code in src\himena\standards\roi\core.py
28
29
30
31
32
33
34
class PointRoi1D(Roi1D):
    """ROI that represents a point in 1D space."""

    x: float = Field(..., description="X-coordinate of the point.")

    def shifted(self, dx: float) -> PointRoi1D:
        return PointRoi1D(x=self.x + dx)

PointRoi2D

ROI that represents a single point.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
x float

X-coordinate of the point.

required
y float

Y-coordinate of the point.

required
Source code in src\himena\standards\roi\core.py
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
class PointRoi2D(Roi2D):
    """ROI that represents a single point."""

    x: float = Field(..., description="X-coordinate of the point.")
    y: float = Field(..., description="Y-coordinate of the point.")

    def shifted(self, dx: float, dy: float) -> PointRoi2D:
        return self.model_copy(update={"x": self.x + dx, "y": self.y + dy})

    def bbox(self) -> Rect[float]:
        return Rect(self.x, self.y, 0, 0)

    def to_mask(self, shape: tuple[int, ...]) -> NDArray[np.bool_]:
        arr = np.zeros(shape, dtype=bool)
        arr[..., int(round(self.y)), int(round(self.x))] = True
        return arr

PointsRoi1D

ROI that represents a set of points in 1D space.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
xs Any

List of x-coordinates.

required
Source code in src\himena\standards\roi\core.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class PointsRoi1D(Roi1D):
    """ROI that represents a set of points in 1D space."""

    xs: Any = Field(..., description="List of x-coordinates.")

    @field_validator("xs")
    def _validate_np_array(cls, v) -> NDArray[np.number]:
        out = np.asarray(v)
        if out.dtype.kind not in "if":
            raise ValueError("Must be a numerical array.")
        return out

    def shifted(self, dx: float) -> PointsRoi1D:
        return PointsRoi1D(xs=self.xs + dx)

PointsRoi2D

ROI that represents a set of points.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
xs Any

List of x-coordinates.

required
ys Any

List of y-coordinates.

required
Source code in src\himena\standards\roi\core.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
class PointsRoi2D(Roi2D):
    """ROI that represents a set of points."""

    xs: Any = Field(..., description="List of x-coordinates.")
    ys: Any = Field(..., description="List of y-coordinates.")

    @field_validator("xs", "ys")
    def _validate_np_arrays(cls, v) -> NDArray[np.number]:
        out = np.asarray(v)
        if out.dtype.kind not in "if":
            raise ValueError("Must be a numerical array.")
        return out

    def model_dump_typed(self) -> dict[str, Any]:
        out = super().model_dump_typed()
        out["xs"] = self.xs.tolist()
        out["ys"] = self.ys.tolist()
        return out

    def shifted(self, dx: float, dy: float) -> PointsRoi2D:
        return self.model_copy(update={"xs": self.xs + dx, "ys": self.ys + dy})

    def bbox(self) -> Rect[float]:
        xmin, xmax = np.min(self.xs), np.max(self.xs)
        ymin, ymax = np.min(self.ys), np.max(self.ys)
        return Rect(xmin, ymin, xmax - xmin, ymax - ymin)

    def to_mask(self, shape: tuple[int, ...]) -> NDArray[np.bool_]:
        arr = np.zeros(shape, dtype=bool)
        xs = np.asarray(self.xs).round().astype(int)
        ys = np.asarray(self.ys).round().astype(int)
        arr[..., ys, xs] = True
        return arr

PolygonRoi

ROI that represents a closed polygon.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
xs Any

List of x-coordinates.

required
ys Any

List of y-coordinates.

required
Source code in src\himena\standards\roi\core.py
402
403
404
405
406
407
408
409
410
411
class PolygonRoi(SegmentedLineRoi):
    """ROI that represents a closed polygon."""

    def to_mask(self, shape: tuple[int, ...]) -> NDArray[np.bool_]:
        return _utils.polygon_mask(shape, np.column_stack((self.ys, self.xs)))

    def area(self) -> float:
        dot_xy = np.dot(self.xs, np.roll(self.ys, 1))
        dot_yx = np.dot(self.ys, np.roll(self.xs, 1))
        return np.abs(dot_xy - dot_yx) / 2

RectangleRoi

ROI that represents a rectangle.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
x int | float

X-coordinate of the top-left corner.

required
y int | float

Y-coordinate of the top-left corner.

required
width int | float

Width of the rectangle.

required
height int | float

Height of the rectangle.

required
Source code in src\himena\standards\roi\core.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class RectangleRoi(Roi2D):
    """ROI that represents a rectangle."""

    x: Scalar = Field(..., description="X-coordinate of the top-left corner.")
    y: Scalar = Field(..., description="Y-coordinate of the top-left corner.")
    width: Scalar = Field(..., description="Width of the rectangle.")
    height: Scalar = Field(..., description="Height of the rectangle.")

    def shifted(self, dx: float, dy: float) -> RectangleRoi:
        """Return a new rectangle shifted by the given amount."""
        return self.model_copy(update={"x": self.x + dx, "y": self.y + dy})

    def area(self) -> float:
        """Return the area of the rectangle."""
        return self.width * self.height

    def bbox(self) -> Rect[float]:
        """Return the bounding box of the rectangle."""
        return Rect(self.x, self.y, self.width, self.height)

    def to_mask(self, shape: tuple[int, ...]) -> NDArray[np.bool_]:
        bb = self.bbox().adjust_to_int("inner")
        arr = np.zeros(shape, dtype=bool)
        arr[..., bb.top : bb.bottom, bb.left : bb.right] = True
        return arr
area()

Return the area of the rectangle.

Source code in src\himena\standards\roi\core.py
65
66
67
def area(self) -> float:
    """Return the area of the rectangle."""
    return self.width * self.height
bbox()

Return the bounding box of the rectangle.

Source code in src\himena\standards\roi\core.py
69
70
71
def bbox(self) -> Rect[float]:
    """Return the bounding box of the rectangle."""
    return Rect(self.x, self.y, self.width, self.height)
shifted(dx, dy)

Return a new rectangle shifted by the given amount.

Source code in src\himena\standards\roi\core.py
61
62
63
def shifted(self, dx: float, dy: float) -> RectangleRoi:
    """Return a new rectangle shifted by the given amount."""
    return self.model_copy(update={"x": self.x + dx, "y": self.y + dy})

Roi1D

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
Source code in src\himena\standards\roi\_base.py
56
57
58
59
class Roi1D(RoiModel):
    def shifted(self, dx: float) -> Self:
        """Return a new 1D ROI translated by the given amount."""
        raise NotImplementedError
shifted(dx)

Return a new 1D ROI translated by the given amount.

Source code in src\himena\standards\roi\_base.py
57
58
59
def shifted(self, dx: float) -> Self:
    """Return a new 1D ROI translated by the given amount."""
    raise NotImplementedError

Roi2D

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
Source code in src\himena\standards\roi\_base.py
62
63
64
65
66
67
68
69
class Roi2D(RoiModel):
    def bbox(self) -> Rect[float]:
        """Return the bounding box of the ROI."""
        raise NotImplementedError

    def shifted(self, dx: float, dy: float) -> Self:
        """Return a new 2D ROI translated by the given amount."""
        raise NotImplementedError
bbox()

Return the bounding box of the ROI.

Source code in src\himena\standards\roi\_base.py
63
64
65
def bbox(self) -> Rect[float]:
    """Return the bounding box of the ROI."""
    raise NotImplementedError
shifted(dx, dy)

Return a new 2D ROI translated by the given amount.

Source code in src\himena\standards\roi\_base.py
67
68
69
def shifted(self, dx: float, dy: float) -> Self:
    """Return a new 2D ROI translated by the given amount."""
    raise NotImplementedError

Roi3D

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
Source code in src\himena\standards\roi\_base.py
72
73
74
75
class Roi3D(RoiModel):
    def shifted(self, dx: float, dy: float, dz: float) -> Self:
        """Return a new 3D ROI translated by the given amount."""
        raise NotImplementedError
shifted(dx, dy, dz)

Return a new 3D ROI translated by the given amount.

Source code in src\himena\standards\roi\_base.py
73
74
75
def shifted(self, dx: float, dy: float, dz: float) -> Self:
    """Return a new 3D ROI translated by the given amount."""
    raise NotImplementedError

RoiListModel dataclass

List of ROIs, with useful methods.

Parameters:

Name Type Description Default
items str
array([], dtype=object)
indices str
array([], shape=(0, 0), dtype=int32)
axis_names str

Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

<dynamic>
Source code in src\himena\standards\roi\_list.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class RoiListModel(NDObjectCollection[RoiModel]):
    """List of ROIs, with useful methods."""

    def model_dump_typed(self) -> dict:
        return {
            "rois": [roi.model_dump_typed() for roi in self],
            "indices": self.indices.tolist() if self.indices is not None else None,
            "axis_names": self.axis_names,
        }

    @classmethod
    def construct(cls, dict_: dict) -> RoiListModel:
        """Construct an instance from a dictionary."""
        rois = []
        for roi_dict in dict_["rois"]:
            if not isinstance(roi_dict, dict):
                raise ValueError(f"Expected a dictionary for 'rois', got: {roi_dict!r}")
            roi_type = roi_dict.pop("type")
            roi = RoiModel.construct(roi_type, roi_dict)
            rois.append(roi)
        return cls(
            items=rois,
            indices=np.array(dict_["indices"], dtype=np.int32),
            axis_names=dict_["axis_names"],
        )

    @classmethod
    def model_validate_json(cls, text: str) -> RoiListModel:
        """Validate the json string and return an instance."""
        js = json.loads(text)
        return cls.construct(js)
construct(dict_) classmethod

Construct an instance from a dictionary.

Source code in src\himena\standards\roi\_list.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@classmethod
def construct(cls, dict_: dict) -> RoiListModel:
    """Construct an instance from a dictionary."""
    rois = []
    for roi_dict in dict_["rois"]:
        if not isinstance(roi_dict, dict):
            raise ValueError(f"Expected a dictionary for 'rois', got: {roi_dict!r}")
        roi_type = roi_dict.pop("type")
        roi = RoiModel.construct(roi_type, roi_dict)
        rois.append(roi)
    return cls(
        items=rois,
        indices=np.array(dict_["indices"], dtype=np.int32),
        axis_names=dict_["axis_names"],
    )
model_validate_json(text) classmethod

Validate the json string and return an instance.

Source code in src\himena\standards\roi\_list.py
35
36
37
38
39
@classmethod
def model_validate_json(cls, text: str) -> RoiListModel:
    """Validate the json string and return an instance."""
    js = json.loads(text)
    return cls.construct(js)

RoiModel

Base class for ROIs (Region of Interest) in images.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
Source code in src\himena\standards\roi\_base.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class RoiModel(BaseModel):
    """Base class for ROIs (Region of Interest) in images."""

    name: str | None = Field(None, description="Name of the ROI.")

    def model_dump_typed(self) -> dict:
        return {
            "type": _strip_roi_suffix(type(self).__name__.lower()),
            **self.model_dump(),
        }

    @classmethod
    def construct(cls, typ: str, dict_: dict) -> RoiModel:
        """Construct an instance from a dictionary."""
        model_type = pick_roi_model(typ)
        return model_type.model_validate(dict_)

    def to_mask(self, shape: tuple[int, ...]) -> NDArray[np.bool_]:
        raise NotImplementedError
construct(typ, dict_) classmethod

Construct an instance from a dictionary.

Source code in src\himena\standards\roi\_base.py
26
27
28
29
30
@classmethod
def construct(cls, typ: str, dict_: dict) -> RoiModel:
    """Construct an instance from a dictionary."""
    model_type = pick_roi_model(typ)
    return model_type.model_validate(dict_)

RotatedEllipseRoi

ROI that represents a rotated ellipse.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
start tuple[float, float]

(X, Y) coordinate of the start point.

required
end tuple[float, float]

(X, Y) coordinate of the end point.

required
width float

Width of the ROI.

required
Source code in src\himena\standards\roi\core.py
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
class RotatedEllipseRoi(RotatedRoi2D):
    """ROI that represents a rotated ellipse."""

    def area(self) -> float:
        return self.length() * self.width * math.pi / 4

    def to_mask(self, shape: tuple[int, ...]) -> NDArray[np.bool_]:
        _yy, _xx = np.indices(shape[-2:])
        start_x, start_y = self.start
        end_x, end_y = self.end
        length = math.hypot(end_x - start_x, end_y - start_y)
        cx, cy = (start_x + end_x) / 2, (start_y + end_y) / 2
        angle = self.angle_radian()
        comp_a = (_yy - cy) / length * 2
        comp_b = (_xx - cx) / self.width * 2
        comp_a, comp_b = (
            comp_a * math.cos(angle) - comp_b * math.sin(angle),
            comp_a * math.sin(angle) + comp_b * math.cos(angle),
        )
        return comp_a**2 + comp_b**2 <= 1

RotatedRectangleRoi

ROI that represents a rotated rectangle.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
start tuple[float, float]

(X, Y) coordinate of the start point.

required
end tuple[float, float]

(X, Y) coordinate of the end point.

required
width float

Width of the ROI.

required
Source code in src\himena\standards\roi\core.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
class RotatedRectangleRoi(RotatedRoi2D):
    """ROI that represents a rotated rectangle."""

    def area(self) -> float:
        return self.length() * self.width

    def bbox(self) -> Rect[float]:
        p00, p01, p11, p10 = self._get_vertices()
        xmin = min(p00[0], p01[0], p10[0], p11[0])
        xmax = max(p00[0], p01[0], p10[0], p11[0])
        ymin = min(p00[1], p01[1], p10[1], p11[1])
        ymax = max(p00[1], p01[1], p10[1], p11[1])
        return Rect(xmin, ymin, xmax - xmin, ymax - ymin)

    def _get_vertices(self):
        start_x, start_y = self.start
        end_x, end_y = self.end
        vx, vy = self._get_vx_vy()
        center = np.array([start_x + end_x, start_y + end_y]) / 2
        p00 = center - vx / 2 - vy / 2
        p01 = center - vx / 2 + vy / 2
        p10 = center + vx / 2 - vy / 2
        p11 = center + vx / 2 + vy / 2
        return p00, p01, p11, p10

    def to_mask(self, shape: tuple[int, ...]):
        vertices = np.stack(self._get_vertices(), axis=0)
        return _utils.polygon_mask(shape, vertices[:, ::-1])

SegmentedLineRoi

ROI that represents a segmented line.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
xs Any

List of x-coordinates.

required
ys Any

List of y-coordinates.

required
Source code in src\himena\standards\roi\core.py
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
class SegmentedLineRoi(PointsRoi2D):
    """ROI that represents a segmented line."""

    def length(self) -> np.float64:
        return np.sum(self.lengths())

    def lengths(self) -> NDArray[np.float64]:
        return np.hypot(np.diff(self.xs), np.diff(self.ys))

    def linspace(self, num: int) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
        """Return a tuple of x and y coordinates of np.linspace along the line."""
        tnots = np.cumsum(np.concatenate([[0], self.lengths()], dtype=np.float64))
        teval = np.linspace(0, tnots[-1], num)
        xi = np.interp(teval, tnots, self.xs)
        yi = np.interp(teval, tnots, self.ys)
        return xi, yi

    def arange(
        self, step: float = 1.0
    ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
        tnots = np.cumsum(np.concatenate([[0], self.lengths()], dtype=np.float64))
        length = tnots[-1]
        num, rem = divmod(length, step)
        teval = np.linspace(0, length - rem, int(num + 1))
        xi = np.interp(teval, tnots, self.xs)
        yi = np.interp(teval, tnots, self.ys)
        return xi, yi

    def to_mask(self, shape: tuple[int, ...]) -> NDArray[np.bool_]:
        arr = np.zeros(shape, dtype=bool)
        xs, ys = self.linspace(int(math.ceil(self.length())))
        xs = xs.round().astype(int)
        ys = ys.round().astype(int)
        arr[ys, xs] = True
        return arr
linspace(num)

Return a tuple of x and y coordinates of np.linspace along the line.

Source code in src\himena\standards\roi\core.py
374
375
376
377
378
379
380
def linspace(self, num: int) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
    """Return a tuple of x and y coordinates of np.linspace along the line."""
    tnots = np.cumsum(np.concatenate([[0], self.lengths()], dtype=np.float64))
    teval = np.linspace(0, tnots[-1], num)
    xi = np.interp(teval, tnots, self.xs)
    yi = np.interp(teval, tnots, self.ys)
    return xi, yi

SpanRoi

ROI that represents a span in 1D space.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
start float

Start of the span.

required
end float

End of the span.

required
Source code in src\himena\standards\roi\core.py
15
16
17
18
19
20
21
22
23
24
25
class SpanRoi(Roi1D):
    """ROI that represents a span in 1D space."""

    start: float = Field(..., description="Start of the span.")
    end: float = Field(..., description="End of the span.")

    def shifted(self, dx: float) -> SpanRoi:
        return SpanRoi(start=self.start + dx, end=self.end + dx)

    def width(self) -> float:
        return self.end - self.start

SplineRoi

ROI that represents a spline curve.

Parameters:

Name Type Description Default
name str | None

Name of the ROI.

None
degree int

Degree of the spline curve.

3
Source code in src\himena\standards\roi\core.py
414
415
416
417
class SplineRoi(Roi2D):
    """ROI that represents a spline curve."""

    degree: int = Field(3, description="Degree of the spline curve.", ge=1)

default_roi_label(nth)

Return a default label for the n-th ROI.

Source code in src\himena\standards\roi\_base.py
51
52
53
def default_roi_label(nth: int) -> str:
    """Return a default label for the n-th ROI."""
    return f"ROI-{nth}"

pick_roi_model(typ) cached

Pick an ROI model class from the given type string

Source code in src\himena\standards\roi\_base.py
36
37
38
39
40
41
42
@cache
def pick_roi_model(typ: str) -> type[RoiModel]:
    """Pick an ROI model class from the given type string"""
    for sub in iter_subclasses(RoiModel):
        if _strip_roi_suffix(sub.__name__.lower()) == typ:
            return sub
    raise ValueError(f"Unknown ROI type: {typ!r}")