Skip to content

Commit b9c3443

Browse files
Merge pull request #14 from fractal-analytics-platform/fix_2d_shape
fix bug when running 2D models on 1yx ome zarrs
2 parents 49e19ed + c9d7254 commit b9c3443

File tree

12 files changed

+91
-32
lines changed

12 files changed

+91
-32
lines changed

src/ilastik_tasks/ilastik_pixel_classification_segmentation.py

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,14 @@
2323
import json
2424
import logging
2525
import os
26-
from typing import Any, Optional
26+
from typing import Any, Union
2727

2828
import anndata as ad
2929
import dask.array as da
3030
import fractal_tasks_core
3131
import numpy as np
3232
import vigra
3333
import zarr
34-
from ilastik_tasks.ilastik_utils import (
35-
IlastikChannel1InputModel,
36-
IlastikChannel2InputModel,
37-
get_expected_number_of_channels,
38-
)
3934
from fractal_tasks_core.labels import prepare_label_group
4035
from fractal_tasks_core.masked_loading import masked_loading_wrapper
4136
from fractal_tasks_core.ngff import load_NgffImageMeta
@@ -56,10 +51,16 @@
5651
from ilastik.applets.dataSelection.opDataSelection import (
5752
PreloadedArrayDatasetInfo,
5853
)
59-
from pydantic import validate_call, Field
54+
from pydantic import Field, validate_call
6055
from skimage.measure import label, regionprops
6156
from skimage.morphology import remove_small_holes
6257

58+
from ilastik_tasks.ilastik_utils import (
59+
IlastikChannel1InputModel,
60+
IlastikChannel2InputModel,
61+
get_expected_number_of_channels,
62+
)
63+
6364
logger = logging.getLogger(__name__)
6465

6566
__OME_NGFF_VERSION__ = fractal_tasks_core.__OME_NGFF_VERSION__
@@ -82,7 +83,7 @@ def segment_ROI(
8283
foreground_class: int = 0,
8384
threshold: float = 0.5,
8485
min_size: int = 15,
85-
label_dtype: Optional[np.dtype] = None,
86+
label_dtype: Union[np.dtype, None] = None,
8687
relabeling: bool = True,
8788
) -> np.ndarray:
8889
"""Run the Ilastik model on a single ROI.
@@ -130,9 +131,7 @@ def segment_ROI(
130131
ilastik_labels = ilastik_output > threshold
131132

132133
# remove small holes
133-
ilastik_labels = remove_small_holes(
134-
ilastik_labels, area_threshold=min_size
135-
)
134+
ilastik_labels = remove_small_holes(ilastik_labels, area_threshold=min_size)
136135

137136
# label image
138137
ilastik_labels = label(ilastik_labels)
@@ -148,12 +147,16 @@ def segment_ROI(
148147
or (label_props[i].axis_major_length < 1)
149148
or (label_props[i].major_axis_length < 1)
150149
]
151-
logger.info(f"number of labels before filtering for size = {ilastik_labels.max()}")
150+
logger.info(
151+
f"number of labels before filtering for size = {ilastik_labels.max()}"
152+
)
152153
ilastik_labels[np.isin(ilastik_labels, labels2remove)] = 0
153154
ilastik_labels = label(ilastik_labels)
154-
logger.info(f"number of labels after filtering for size = {ilastik_labels.max()}")
155+
logger.info(
156+
f"number of labels after filtering for size = {ilastik_labels.max()}"
157+
)
155158
label_props = regionprops(ilastik_labels)
156-
159+
157160
# Shift labels and update relabeling counters
158161
if relabeling:
159162
num_labels_roi = np.max(ilastik_labels)
@@ -186,8 +189,8 @@ def ilastik_pixel_classification_segmentation(
186189
default_factory=IlastikChannel2InputModel
187190
),
188191
input_ROI_table: str = "FOV_ROI_table",
189-
output_ROI_table: Optional[str] = None,
190-
output_label_name: Optional[str] = None,
192+
output_ROI_table: Union[str, None] = None,
193+
output_label_name: Union[str, None] = None,
191194
use_masks: bool = True,
192195
# Ilastik-related arguments
193196
ilastik_model: str,
@@ -252,10 +255,10 @@ def ilastik_pixel_classification_segmentation(
252255

253256
# Setup Ilastik headless shell
254257
shell = setup_ilastik(ilastik_model)
255-
258+
256259
# Check if channel input fits expected number of channels of model
257260
expected_num_channels = get_expected_number_of_channels(shell)
258-
261+
259262
if expected_num_channels == 2 and not channel2.is_set():
260263
raise ValueError(
261264
"Ilastik model expects two channels as "
@@ -265,7 +268,7 @@ def ilastik_pixel_classification_segmentation(
265268
raise ValueError(
266269
"Ilastik model expects 1 channel as " "input but two channels were provided"
267270
)
268-
271+
269272
elif expected_num_channels > 2:
270273
raise NotImplementedError(
271274
f"Expected {expected_num_channels} channels, "
@@ -286,7 +289,7 @@ def ilastik_pixel_classification_segmentation(
286289
ind_channel_c2 = omero_channel_2.index
287290
else:
288291
return
289-
292+
290293
# Set channel label
291294
if output_label_name is None:
292295
try:
@@ -516,6 +519,21 @@ def ilastik_pixel_classification_segmentation(
516519
f"{len(overlap_list)} bounding-box pairs overlap"
517520
)
518521

522+
# Check that the shape of the new label image matches the expected shape
523+
expected_shape = (
524+
e_z - s_z,
525+
e_y - s_y,
526+
e_x - s_x,
527+
)
528+
if new_label_img.shape != expected_shape:
529+
try:
530+
new_label_img = da.broadcast_to(new_label_img, expected_shape)
531+
except:
532+
raise ValueError(
533+
f"Shape mismatch: {new_label_img.shape} != {expected_shape} "
534+
"Between the segmented label image and expected shape in the zarr array."
535+
)
536+
519537
# Compute and store 0-th level to disk
520538
da.array(new_label_img).to_zarr(
521539
url=mask_zarr,
@@ -564,7 +582,6 @@ def ilastik_pixel_classification_segmentation(
564582
)
565583

566584

567-
568585
if __name__ == "__main__":
569586
from fractal_task_tools.task_wrapper import run_fractal_task
570587

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
{
2+
"backend": "anndata_v1",
23
"encoding-type": "anndata",
3-
"encoding-version": "0.1.0"
4+
"encoding-version": "0.1.0",
5+
"fractal_table_version": "1",
6+
"index_key": "FieldIndex",
7+
"index_type": "str",
8+
"type": "roi_table"
49
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"chunks": [
3-
2,
4-
8
3+
1,
4+
6
55
],
66
"compressor": {
77
"blocksize": 0,
@@ -10,13 +10,13 @@
1010
"id": "blosc",
1111
"shuffle": 1
1212
},
13-
"dtype": "<f4",
13+
"dtype": "<f8",
1414
"fill_value": 0.0,
1515
"filters": null,
1616
"order": "C",
1717
"shape": [
18-
2,
19-
8
18+
1,
19+
6
2020
],
2121
"zarr_format": 2
2222
}
-16 Bytes
Binary file not shown.

tests/data/test_mip_ome.zarr/tables/FOV_ROI_table/obs/FieldIndex/.zarray

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"chunks": [
3-
2
3+
1
44
],
55
"compressor": {
66
"blocksize": 0,
@@ -18,7 +18,7 @@
1818
],
1919
"order": "C",
2020
"shape": [
21-
2
21+
1
2222
],
2323
"zarr_format": 2
2424
}
-9 Bytes
Binary file not shown.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"_index": "_index",
3+
"column-order": [],
4+
"encoding-type": "dataframe",
5+
"encoding-version": "0.2.0"
6+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"zarr_format": 2
3+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"chunks": [
3+
6
4+
],
5+
"compressor": {
6+
"blocksize": 0,
7+
"clevel": 5,
8+
"cname": "lz4",
9+
"id": "blosc",
10+
"shuffle": 1
11+
},
12+
"dtype": "|O",
13+
"fill_value": 0,
14+
"filters": [
15+
{
16+
"id": "vlen-utf8"
17+
}
18+
],
19+
"order": "C",
20+
"shape": [
21+
6
22+
],
23+
"zarr_format": 2
24+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"encoding-type": "string-array",
3+
"encoding-version": "0.2.0"
4+
}

0 commit comments

Comments
 (0)