Skip to content

_measure_features_utils

Utility models and functions for the measure_features task.

AdvancedOptions

Bases: BaseModel

Advanced options for feature measurement.

Source code in fractal_tasks_core/_measure_features_utils.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
class AdvancedOptions(BaseModel):
    """Advanced options for feature measurement."""

    level_path: str | None = None
    """
    Optional path to the pyramid level to use for the measurement.
    If None, the highest resolution level will be used.
    """

    use_scaling: bool = True
    """
    Whether to use pixel scaling from the OME-Zarr metadata. This will scale the
    features according to the physical pixel size, e.g. area will be in
    square microns instead of square pixels. Defaults to True.
    """

    use_cache: bool = True
    """
    Whether to cache in the regionprops function. This can speed up the measurement for
    but can also increase memory usage. Defaults to True.
    """

    table_backend: AVAILABLE_TABLE_BACKENDS = DEFAULT_TABLE_BACKEND
    """
    Table backend to use for the output table. Defaults to "anndata".
    """

level_path: str | None = None class-attribute instance-attribute

Optional path to the pyramid level to use for the measurement. If None, the highest resolution level will be used.

table_backend: AVAILABLE_TABLE_BACKENDS = DEFAULT_TABLE_BACKEND class-attribute instance-attribute

Table backend to use for the output table. Defaults to "anndata".

use_cache: bool = True class-attribute instance-attribute

Whether to cache in the regionprops function. This can speed up the measurement for but can also increase memory usage. Defaults to True.

use_scaling: bool = True class-attribute instance-attribute

Whether to use pixel scaling from the OME-Zarr metadata. This will scale the features according to the physical pixel size, e.g. area will be in square microns instead of square pixels. Defaults to True.

InputChannel

Bases: BaseModel

Input channel configuration for measurement tasks.

This model is used to select a channel by label, wavelength ID, or index.

Source code in fractal_tasks_core/_measure_features_utils.py
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class InputChannel(BaseModel):
    """Input channel configuration for measurement tasks.

    This model is used to select a channel by label, wavelength ID, or index.
    """

    mode: Literal["label", "wavelength_id", "index"] = "label"
    """
    Specifies how to interpret the identifier. Can be "label", "wavelength_id",
    or "index" (must be an integer string).
    """
    identifier: str
    """
    Unique identifier for the channel. This can be a channel label, wavelength
    ID, or index.
    """

    def to_channel_selection_models(self) -> ChannelSelectionModel:
        """Convert to ChannelSelectionModel.

        Returns:
            ChannelSelectionModel: Channel selection model.
        """
        return ChannelSelectionModel(identifier=self.identifier, mode=self.mode)

identifier: str instance-attribute

Unique identifier for the channel. This can be a channel label, wavelength ID, or index.

mode: Literal['label', 'wavelength_id', 'index'] = 'label' class-attribute instance-attribute

Specifies how to interpret the identifier. Can be "label", "wavelength_id", or "index" (must be an integer string).

to_channel_selection_models()

Convert to ChannelSelectionModel.

RETURNS DESCRIPTION
ChannelSelectionModel

Channel selection model.

TYPE: ChannelSelectionModel

Source code in fractal_tasks_core/_measure_features_utils.py
86
87
88
89
90
91
92
def to_channel_selection_models(self) -> ChannelSelectionModel:
    """Convert to ChannelSelectionModel.

    Returns:
        ChannelSelectionModel: Channel selection model.
    """
    return ChannelSelectionModel(identifier=self.identifier, mode=self.mode)

IntensityFeatures

Bases: BaseModel

Intensity features extracted from regionprops.

Source code in fractal_tasks_core/_measure_features_utils.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
class IntensityFeatures(BaseModel):
    """Intensity features extracted from regionprops."""

    type: Literal["IntensityFeatures"] = "IntensityFeatures"
    """
    Features included in this model are: intensity_mean, intensity_median,
    intensity_max, intensity_min, intensity_std.
    """
    channels: list[InputChannel] | None = None
    """
    List of channels to extract intensity features from. If None all
    channels will be used.
    """

    def property_names(self, is_2d: bool) -> list[str]:
        return [
            "intensity_mean",
            "intensity_median",
            "intensity_max",
            "intensity_min",
            "intensity_std",
        ]

    def to_channel_selection_models(self) -> list[ChannelSelectionModel] | None:
        """Convert to list of ChannelSelectionModel.

        Returns:
            list[ChannelSelectionModel] | None: List of channel selection models,
                or None if no channels are specified.
        """
        if self.channels is None:
            return None
        return [channel.to_channel_selection_models() for channel in self.channels]

channels: list[InputChannel] | None = None class-attribute instance-attribute

List of channels to extract intensity features from. If None all channels will be used.

type: Literal['IntensityFeatures'] = 'IntensityFeatures' class-attribute instance-attribute

Features included in this model are: intensity_mean, intensity_median, intensity_max, intensity_min, intensity_std.

to_channel_selection_models()

Convert to list of ChannelSelectionModel.

RETURNS DESCRIPTION
list[ChannelSelectionModel] | None

list[ChannelSelectionModel] | None: List of channel selection models, or None if no channels are specified.

Source code in fractal_tasks_core/_measure_features_utils.py
118
119
120
121
122
123
124
125
126
127
def to_channel_selection_models(self) -> list[ChannelSelectionModel] | None:
    """Convert to list of ChannelSelectionModel.

    Returns:
        list[ChannelSelectionModel] | None: List of channel selection models,
            or None if no channels are specified.
    """
    if self.channels is None:
        return None
    return [channel.to_channel_selection_models() for channel in self.channels]

ShapeFeatures

Bases: BaseModel

Shape features extracted from regionprops.

Source code in fractal_tasks_core/_measure_features_utils.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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
class ShapeFeatures(BaseModel):
    """Shape features extracted from regionprops."""

    type: Literal["ShapeFeatures"] = "ShapeFeatures"
    """
    Features included in this model are: area, area_bbox, num_pixels,
    equivalent_diameter_area, axis_major_length, axis_minor_length, euler_number,
    and if 2D also feret_diameter_max, perimeter, perimeter_crofton, eccentricity,
    orientation.
    """

    include_convex_hull_properties: bool = False
    """
    Whether to include convex hull related properties like area_convex, area_filled,
    extent, solidity. These are not included since they can sometimes return
    unexpected Warnings.
    """

    def property_names(self, is_2d: bool) -> list[str]:
        _base_properties = [
            "area",
            "area_bbox",
            "num_pixels",
            "equivalent_diameter_area",
            "axis_major_length",
            "axis_minor_length",
            "euler_number",
        ]
        _base_2d_properties = [
            "feret_diameter_max",
            "perimeter",
            "perimeter_crofton",
            "eccentricity",
            "orientation",
        ]
        _convex_hull_properties = [
            "area_convex",
            "area_filled",
            "extent",
            "solidity",
        ]
        properties: list[str] = []
        properties.extend(_base_properties)
        if is_2d:
            properties.extend(_base_2d_properties)
        if self.include_convex_hull_properties:
            properties.extend(_convex_hull_properties)

        return properties

include_convex_hull_properties: bool = False class-attribute instance-attribute

Whether to include convex hull related properties like area_convex, area_filled, extent, solidity. These are not included since they can sometimes return unexpected Warnings.

type: Literal['ShapeFeatures'] = 'ShapeFeatures' class-attribute instance-attribute

Features included in this model are: area, area_bbox, num_pixels, equivalent_diameter_area, axis_major_length, axis_minor_length, euler_number, and if 2D also feret_diameter_max, perimeter, perimeter_crofton, eccentricity, orientation.

_prepare_regionprops_kwargs(image, list_features, use_scaling=True, use_cache=True)

Prepare keyword arguments for regionprops based on the requested features.

This includes determining which channels to load for intensity features.

PARAMETER DESCRIPTION
image

The image to check against.

TYPE: Image

list_features

List of requested features.

TYPE: list[SupportedFeatures]

use_scaling

Whether to use pixel scaling from the image metadata. Defaults to True.

TYPE: bool DEFAULT: True

use_cache

Whether to cache the loaded images. Defaults to True.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
tuple[dict, list[ChannelSelectionModel] | None]

tuple[dict, list[ChannelSelectionModel] | None]: Keyword arguments to pass to regionprops, e.g. {"intensity_image": ...}, and the list of channel selection models.

Source code in fractal_tasks_core/_measure_features_utils.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
def _prepare_regionprops_kwargs(
    image: Image,
    list_features: list[SupportedFeatures],
    use_scaling: bool = True,
    use_cache: bool = True,
) -> tuple[dict, list[ChannelSelectionModel] | None]:
    """Prepare keyword arguments for regionprops based on the requested features.

    This includes determining which channels to load for intensity features.

    Args:
        image (Image): The image to check against.
        list_features (list[SupportedFeatures]): List of requested features.
        use_scaling (bool): Whether to use pixel scaling from the image metadata.
            Defaults to True.
        use_cache (bool): Whether to cache the loaded images. Defaults to True.

    Returns:
        tuple[dict, list[ChannelSelectionModel] | None]: Keyword arguments to pass
            to regionprops, e.g. {"intensity_image": ...}, and the list
            of channel selection models.
    """
    is_2d = image.is_2d
    kwargs = {}
    properties = ["label"]  # label is always needed for regionprops
    channel_selection_models = None
    for feature in list_features:
        properties.extend(feature.property_names(is_2d=is_2d))
        if isinstance(feature, IntensityFeatures):
            channel_selection_models = feature.to_channel_selection_models()

    if channel_selection_models is not None:
        channel_identifiers = {
            i: m.identifier for i, m in enumerate(channel_selection_models)
        }
    else:
        channel_identifiers = dict(enumerate(image.channel_labels))

    px_size = image.pixel_size
    if is_2d:
        spacings = [px_size.get(ax, 1.0) for ax in "yx"]
    else:
        spacings = [px_size.get(ax, 1.0) for ax in "yxz"]

    kwargs["properties"] = properties
    kwargs["channel_identifiers"] = channel_identifiers
    kwargs["spacings"] = spacings if use_scaling else None
    kwargs["use_cache"] = use_cache
    return kwargs, channel_selection_models

region_props_features_func(image, label, roi, properties, channel_identifiers=None, spacings=None, use_cache=True)

Extract region properties features from a label image within a ROI.

Source code in fractal_tasks_core/_measure_features_utils.py
215
216
217
218
219
220
221
222
223
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
257
258
259
260
261
def region_props_features_func(
    image: np.ndarray,
    label: np.ndarray,
    roi: Roi,
    properties: list[str],
    channel_identifiers: dict[int, str] | None = None,
    spacings: list[float] | None = None,
    use_cache: bool = True,
) -> dict:
    """Extract region properties features from a label image within a ROI."""
    if image.ndim not in (3, 4):
        raise ValueError("Image must be 3D yxc or 4D yxzc ")
    if image.ndim != label.ndim:
        raise ValueError("Image and label must have the same number of dimensions")
    # Since we query the label image as yxc or yxzc, we need to ensure
    # it has a single channel and we need to squeeze the channel dimension
    if label.shape[-1] != 1:
        raise ValueError("Label image must have a single channel")
    label = label[..., 0]

    # Perform region props extraction
    props = measure.regionprops_table(
        label_image=label,
        intensity_image=image,
        properties=properties,
        spacing=spacings,
        cache=use_cache,
    )
    # Rename channel-specific features
    if channel_identifiers is not None:
        for i, identifier in channel_identifiers.items():
            for key in list(props.keys()):
                suffix = f"-{i}"
                if key.endswith(suffix):
                    old_base, _ = key.split(suffix)
                    new_key = old_base + f"-{identifier}"
                    props[new_key] = props.pop(key)
    # Add some more metadata columns, e.g. the ROI name
    num_regions = len(props["label"])
    props["region"] = [roi.get_name()] * num_regions

    # Check if time axis is present in the ROI and add the time point as a column
    t_slice = roi.get(axis_name="t")
    if t_slice is not None and t_slice.start is not None:
        # Add the time point as a column if the ROI has a time axis
        props["time_index"] = [t_slice.start] * num_regions
    return props