Skip to content

Commit 600c490

Browse files
authored
Merge pull request #4 from rhornb/pred_error
channel input model fix
2 parents 994f885 + d787284 commit 600c490

File tree

3 files changed

+105
-37
lines changed

3 files changed

+105
-37
lines changed

src/ilastik_tasks/__FRACTAL_MANIFEST__.json

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
},
1616
"args_schema_parallel": {
1717
"$defs": {
18-
"ChannelInputModel": {
19-
"description": "A channel which is specified by either `wavelength_id` or `label`.",
18+
"CellposeChannel1InputModel": {
19+
"description": "Channel input for cellpose with normalization options.",
2020
"properties": {
2121
"wavelength_id": {
2222
"title": "Wavelength Id",
@@ -27,9 +27,78 @@
2727
"title": "Label",
2828
"type": "string",
2929
"description": "Name of the channel. Can only be specified if wavelength_id is not set."
30+
},
31+
"normalize": {
32+
"$ref": "#/$defs/CellposeCustomNormalizer",
33+
"title": "Normalize",
34+
"description": "Validator to handle different normalization scenarios for Cellpose models"
3035
}
3136
},
32-
"title": "ChannelInputModel",
37+
"title": "CellposeChannel1InputModel",
38+
"type": "object"
39+
},
40+
"CellposeChannel2InputModel": {
41+
"description": "Channel input for secondary cellpose channel with normalization options.",
42+
"properties": {
43+
"wavelength_id": {
44+
"title": "Wavelength Id",
45+
"type": "string",
46+
"description": "Unique ID for the channel wavelength, e.g. `A01_C01`. Can only be specified if label is not set."
47+
},
48+
"label": {
49+
"title": "Label",
50+
"type": "string",
51+
"description": "Name of the channel. Can only be specified if wavelength_id is not set."
52+
},
53+
"normalize": {
54+
"$ref": "#/$defs/CellposeCustomNormalizer",
55+
"title": "Normalize",
56+
"description": "Validator to handle different normalization scenarios for Cellpose models"
57+
}
58+
},
59+
"title": "CellposeChannel2InputModel",
60+
"type": "object"
61+
},
62+
"CellposeCustomNormalizer": {
63+
"description": "Validator to handle different normalization scenarios for Cellpose models",
64+
"properties": {
65+
"type": {
66+
"default": "default",
67+
"enum": [
68+
"default",
69+
"custom",
70+
"no_normalization"
71+
],
72+
"title": "Type",
73+
"type": "string",
74+
"description": "One of `default` (Cellpose default normalization), `custom` (using the other custom parameters) or `no_normalization`."
75+
},
76+
"lower_percentile": {
77+
"maximum": 100.0,
78+
"minimum": 0.0,
79+
"title": "Lower Percentile",
80+
"type": "number",
81+
"description": "Specify a custom lower-bound percentile for rescaling as a float value between 0 and 100. Set to 1 to run the same as default). You can only specify percentiles or bounds, not both."
82+
},
83+
"upper_percentile": {
84+
"maximum": 100.0,
85+
"minimum": 0.0,
86+
"title": "Upper Percentile",
87+
"type": "number",
88+
"description": "Specify a custom upper-bound percentile for rescaling as a float value between 0 and 100. Set to 99 to run the same as default, set to e.g. 99.99 if the default rescaling was too harsh. You can only specify percentiles or bounds, not both."
89+
},
90+
"lower_bound": {
91+
"title": "Lower Bound",
92+
"type": "integer",
93+
"description": "Explicit lower bound value to rescale the image at. Needs to be an integer, e.g. 100. You can only specify percentiles or bounds, not both."
94+
},
95+
"upper_bound": {
96+
"title": "Upper Bound",
97+
"type": "integer",
98+
"description": "Explicit upper bound value to rescale the image at. Needs to be an integer, e.g. 2000. You can only specify percentiles or bounds, not both."
99+
}
100+
},
101+
"title": "CellposeCustomNormalizer",
33102
"type": "object"
34103
}
35104
},
@@ -46,16 +115,12 @@
46115
"description": "Pyramid level of the image to be segmented. Choose `0` to process at full resolution."
47116
},
48117
"channel": {
49-
"$ref": "#/$defs/ChannelInputModel",
118+
"$ref": "#/$defs/CellposeChannel1InputModel",
50119
"title": "Channel",
51120
"description": "Primary channel for pixel classification; requires either `wavelength_id` (e.g. `A01_C01`) or `label` (e.g. `DAPI`)."
52121
},
53122
"channel2": {
54-
"allOf": [
55-
{
56-
"$ref": "#/$defs/ChannelInputModel"
57-
}
58-
],
123+
"$ref": "#/$defs/CellposeChannel2InputModel",
59124
"title": "Channel2",
60125
"description": "Second channel for pixel classification (in the same format as `channel`). Use only if second channel has also been used during Ilastik model training."
61126
},

src/ilastik_tasks/ilastik_pixel_classification_segmentation.py

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@
3131
import numpy as np
3232
import vigra
3333
import zarr
34-
from fractal_tasks_core.channels import ChannelInputModel, get_channel_from_image_zarr
34+
from fractal_tasks_core.tasks.cellpose_utils import (
35+
CellposeChannel1InputModel,
36+
)
37+
from fractal_tasks_core.tasks.cellpose_utils import (
38+
CellposeChannel2InputModel,
39+
)
3540
from fractal_tasks_core.labels import prepare_label_group
3641
from fractal_tasks_core.masked_loading import masked_loading_wrapper
3742
from fractal_tasks_core.ngff import load_NgffImageMeta
@@ -52,7 +57,7 @@
5257
from ilastik.applets.dataSelection.opDataSelection import (
5358
PreloadedArrayDatasetInfo,
5459
)
55-
from pydantic import validate_call
60+
from pydantic import validate_call, Field
5661
from skimage.measure import label, regionprops
5762
from skimage.morphology import remove_small_holes
5863

@@ -153,8 +158,10 @@ def ilastik_pixel_classification_segmentation(
153158
zarr_url: str,
154159
# Task-specific arguments
155160
level: int,
156-
channel: ChannelInputModel,
157-
channel2: Optional[ChannelInputModel] = None,
161+
channel: CellposeChannel1InputModel,
162+
channel2: CellposeChannel2InputModel = Field(
163+
default_factory=CellposeChannel2InputModel
164+
),
158165
input_ROI_table: str = "FOV_ROI_table",
159166
output_ROI_table: Optional[str] = None,
160167
output_label_name: Optional[str] = None,
@@ -233,32 +240,24 @@ def ilastik_pixel_classification_segmentation(
233240
)
234241

235242
# Find channel index
236-
tmp_channel = get_channel_from_image_zarr(
237-
image_zarr_path=zarr_url,
238-
wavelength_id=channel.wavelength_id,
239-
label=channel.label,
240-
)
241-
if tmp_channel:
242-
ind_channel = tmp_channel.index
243+
omero_channel = channel.get_omero_channel(zarr_url)
244+
if omero_channel:
245+
ind_channel = omero_channel.index
243246
else:
244247
return
245248

246249
# Find channel index for second channel, if one is provided
247-
if channel2:
248-
tmp_channel_2 = get_channel_from_image_zarr(
249-
image_zarr_path=zarr_url,
250-
wavelength_id=channel2.wavelength_id,
251-
label=channel2.label,
252-
)
253-
if tmp_channel_2:
254-
ind_channel_c2 = tmp_channel_2.index
250+
if channel2.is_set():
251+
omero_channel_2 = channel2.get_omero_channel(zarr_url)
252+
if omero_channel_2:
253+
ind_channel_c2 = omero_channel_2.index
255254
else:
256-
return ValueError(f"Channel {channel2} could not be loaded.")
257-
258-
# Set output channel label if none is provided
255+
return
256+
257+
# Set channel label
259258
if output_label_name is None:
260259
try:
261-
channel_label = tmp_channel.label
260+
channel_label = omero_channel.label
262261
output_label_name = f"label_{channel_label}"
263262
except (KeyError, IndexError):
264263
output_label_name = f"label_{ind_channel}"

tests/test_ilastik_pixel_classification_segmentation.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33

44
import pytest
55
from devtools import debug
6-
from fractal_tasks_core.channels import ChannelInputModel
7-
6+
from fractal_tasks_core.tasks.cellpose_utils import (
7+
CellposeChannel1InputModel,
8+
)
9+
from fractal_tasks_core.tasks.cellpose_utils import (
10+
CellposeChannel2InputModel,
11+
)
812
from src.ilastik_tasks.ilastik_pixel_classification_segmentation import (
913
ilastik_pixel_classification_segmentation,
1014
)
@@ -36,8 +40,8 @@ def test_ilastik_pixel_classification_segmentation_task_3D(test_data_dir_3d):
3640
ilastik_pixel_classification_segmentation(
3741
zarr_url=zarr_url,
3842
level=4,
39-
channel=ChannelInputModel(label="DAPI_2"),
40-
channel2=ChannelInputModel(label="ECadherin_2"),
43+
channel=CellposeChannel1InputModel(label="DAPI_2"),
44+
channel2=CellposeChannel2InputModel(label="ECadherin_2"),
4145
ilastik_model=str(ilastik_model),
4246
output_label_name="test_label",
4347
)
@@ -48,7 +52,7 @@ def test_ilastik_pixel_classification_segmentation_task_3D(test_data_dir_3d):
4852
ilastik_pixel_classification_segmentation(
4953
zarr_url=zarr_url,
5054
level=0,
51-
channel=ChannelInputModel(label="DAPI_2"),
55+
channel=CellposeChannel1InputModel(label="DAPI_2"),
5256
channel2=None,
5357
ilastik_model=str(ilastik_model),
5458
output_label_name="test_label_single_channel",

0 commit comments

Comments
 (0)