Skip to content

Commit 49e19ed

Browse files
Merge pull request #13 from rhornb/main
Single channel input model fix
2 parents 41ae86e + e8c0e9f commit 49e19ed

File tree

173 files changed

+956
-89
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

173 files changed

+956
-89
lines changed

.github/workflows/build_and_test.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ jobs:
1717
strategy:
1818
matrix:
1919
os: [ubuntu-22.04, macos-latest]
20-
python-version: ["3.10"]
20+
python-version: ["3.11"]
2121
name: "Core, Python ${{ matrix.python-version }}, ${{ matrix.os }}"
2222
runs-on: ${{ matrix.os }}
23-
timeout-minutes: 10
23+
timeout-minutes: 30
2424

2525
steps:
2626
- uses: actions/checkout@v4
@@ -29,7 +29,7 @@ jobs:
2929

3030
- uses: prefix-dev/[email protected]
3131
with:
32-
pixi-version: v0.44.0
32+
pixi-version: v0.50.2
3333

3434
- name: Check if manifest has changed
3535
run: pixi run fractal-manifest check --package ilastik-tasks

.gitignore

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ lib/
2020
lib64/
2121
parts/
2222
sdist/
23-
var/
2423
wheels/
2524
*.egg-info/
2625
.installed.cfg
@@ -112,8 +111,10 @@ ENV/
112111
.idea/
113112

114113
*.zarr
115-
# Test data loaded via pooch
116-
/tests/data/10_5281_zenodo_14883998
114+
# Add exeptions for specific zarr files
115+
!tests/data/AssayPlate_Greiner_CELLSTAR655090_B03_0.zarr
116+
!tests/data/test_mip_ome.zarr
117+
117118
# pixi environments
118119
.pixi
119120
*.egg-info

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ dependencies = []
5252
[project.optional-dependencies]
5353
dev = [
5454
"devtools",
55+
"notebook",
5556
"pooch",
5657
"hatch",
5758
"pytest",

src/ilastik_tasks/ilastik_pixel_classification_segmentation.py

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from ilastik_tasks.ilastik_utils import (
3535
IlastikChannel1InputModel,
3636
IlastikChannel2InputModel,
37+
get_expected_number_of_channels,
3738
)
3839
from fractal_tasks_core.labels import prepare_label_group
3940
from fractal_tasks_core.masked_loading import masked_loading_wrapper
@@ -251,18 +252,25 @@ def ilastik_pixel_classification_segmentation(
251252

252253
# Setup Ilastik headless shell
253254
shell = setup_ilastik(ilastik_model)
254-
255-
# Check model channel requirements
256-
expected_channels = check_ilastik_model_channels(shell)
257-
if expected_channels == 2 and channel2 is None:
255+
256+
# Check if channel input fits expected number of channels of model
257+
expected_num_channels = get_expected_number_of_channels(shell)
258+
259+
if expected_num_channels == 2 and not channel2.is_set():
258260
raise ValueError(
259261
"Ilastik model expects two channels as "
260262
"input but only one channel was provided"
261263
)
262-
elif expected_channels == 1 and channel2 is not None:
264+
elif expected_num_channels == 1 and channel2.is_set():
263265
raise ValueError(
264266
"Ilastik model expects 1 channel as " "input but two channels were provided"
265267
)
268+
269+
elif expected_num_channels > 2:
270+
raise NotImplementedError(
271+
f"Expected {expected_num_channels} channels, "
272+
"but a maximum of channels are currently supported."
273+
)
266274

267275
# Find channel index
268276
omero_channel = channel.get_omero_channel(zarr_url)
@@ -290,7 +298,7 @@ def ilastik_pixel_classification_segmentation(
290298
# Load ZYX data
291299
data_zyx = da.from_zarr(f"{zarr_url}/{level}")[ind_channel]
292300
logger.info(f"{data_zyx.shape=}")
293-
if channel2:
301+
if channel2.is_set():
294302
data_zyx_c2 = da.from_zarr(f"{zarr_url}/{level}")[ind_channel_c2]
295303
logger.info(f"Second channel: {data_zyx_c2.shape=}")
296304

@@ -437,7 +445,7 @@ def ilastik_pixel_classification_segmentation(
437445
logger.info(f"Now processing ROI {i_ROI+1}/{num_ROIs}")
438446

439447
# Prepare single-channel or dual-channel input for Ilastik
440-
if channel2:
448+
if channel2.is_set():
441449
# Dual channel mode
442450
img_1 = load_region(
443451
data_zyx,
@@ -556,24 +564,6 @@ def ilastik_pixel_classification_segmentation(
556564
)
557565

558566

559-
def check_ilastik_model_channels(shell) -> int:
560-
"""Check number of input channels expected by Ilastik model.
561-
562-
Args:
563-
shell: Initialized Ilastik shell with loaded model
564-
565-
Returns:
566-
int: Number of expected input channels
567-
"""
568-
# Get dataSelection applet from workflow
569-
data_selection = shell.workflow.dataSelectionApplet
570-
571-
# Get slot info containing expected channels
572-
slot_info = data_selection.topLevelOperator.DatasetRoles.value
573-
574-
# Return number of expected channels
575-
return len(slot_info)
576-
577567

578568
if __name__ == "__main__":
579569
from fractal_task_tools.task_wrapper import run_fractal_task

src/ilastik_tasks/ilastik_utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,20 @@ def get_omero_channel(self, zarr_url) -> OmeroChannel:
111111
f"Original error: {str(e)}"
112112
)
113113
return None
114+
115+
116+
def get_expected_number_of_channels(shell) -> int:
117+
"""
118+
Get the expected number of channels from the trained ilastik model
119+
"""
120+
opPixelClassification = shell.workflow.pcApplet.topLevelOperator
121+
len_input_images = len(opPixelClassification.InputImages)
122+
channel_number = []
123+
for i in range(len_input_images):
124+
channel = opPixelClassification.InputImages[i].meta.getTaggedShape()["c"]
125+
channel_number.append(channel)
126+
127+
if len(set(channel_number)) != 1:
128+
raise ValueError("Inconsistent number of channels across input images.")
129+
130+
return channel_number[0]

tests/conftest.py

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
import os
2-
import shutil
31
from pathlib import Path
42

5-
import pooch
63
import pytest
74

85

@@ -12,43 +9,45 @@ def testdata_path() -> Path:
129
return TEST_DIR / "data/"
1310

1411

15-
@pytest.fixture(scope="session")
16-
def zenodo_zarr_3d(testdata_path: Path) -> str:
17-
"""
18-
This takes care of multiple steps:
19-
20-
1. Download/unzip two Zarr containers (3D and MIP) from Zenodo, via pooch
21-
2. Copy the two Zarr containers into tests/data
22-
3. Modify the Zarrs in tests/data, to add whatever is not in Zenodo
23-
"""
24-
25-
# 1) Download Zarrs from Zenodo
26-
DOI = "10.5281/zenodo.14883998"
27-
DOI_slug = DOI.replace("/", "_").replace(".", "_")
28-
rootfolder = testdata_path / DOI_slug
29-
folder = rootfolder / "AssayPlate_Greiner_CELLSTAR655090.zarr"
30-
31-
registry = {
32-
"AssayPlate_Greiner_CELLSTAR655090.zarr.zip": None,
33-
}
34-
base_url = f"doi:{DOI}"
35-
POOCH = pooch.create(
36-
pooch.os_cache("pooch") / DOI_slug,
37-
base_url,
38-
registry=registry,
39-
retry_if_failed=10,
40-
allow_updates=False,
41-
)
42-
43-
file_name = "AssayPlate_Greiner_CELLSTAR655090.zarr"
44-
# 2) Download/unzip a single Zarr from Zenodo
45-
file_paths = POOCH.fetch(
46-
f"{file_name}.zip", processor=pooch.Unzip(extract_dir=file_name)
47-
)
48-
zarr_full_path = file_paths[0].split(file_name)[0] + file_name
49-
50-
# 3) Copy the downloaded Zarr into tests/data
51-
if os.path.isdir(str(folder)):
52-
shutil.rmtree(str(folder))
53-
shutil.copytree(Path(zarr_full_path) / file_name, folder)
54-
return Path(folder)
12+
# This fixture is not used in the current context, but it can be uncommented if needed.
13+
#
14+
# @pytest.fixture(scope="session")
15+
# def zenodo_zarr_3d(testdata_path: Path) -> str:
16+
# """
17+
# This takes care of multiple steps:
18+
#
19+
# 1. Download/unzip two Zarr containers (3D and MIP) from Zenodo, via pooch
20+
# 2. Copy the two Zarr containers into tests/data
21+
# 3. Modify the Zarrs in tests/data, to add whatever is not in Zenodo
22+
# """
23+
#
24+
# # 1) Download Zarrs from Zenodo
25+
# DOI = "10.5281/zenodo.14883998"
26+
# DOI_slug = DOI.replace("/", "_").replace(".", "_")
27+
# rootfolder = testdata_path / DOI_slug
28+
# folder = rootfolder / "AssayPlate_Greiner_CELLSTAR655090.zarr"
29+
#
30+
# registry = {
31+
# "AssayPlate_Greiner_CELLSTAR655090.zarr.zip": None,
32+
# }
33+
# base_url = f"doi:{DOI}"
34+
# POOCH = pooch.create(
35+
# pooch.os_cache("pooch") / DOI_slug,
36+
# base_url,
37+
# registry=registry,
38+
# retry_if_failed=10,
39+
# allow_updates=False,
40+
# )
41+
#
42+
# file_name = "AssayPlate_Greiner_CELLSTAR655090.zarr"
43+
# # 2) Download/unzip a single Zarr from Zenodo
44+
# file_paths = POOCH.fetch(
45+
# f"{file_name}.zip", processor=pooch.Unzip(extract_dir=file_name)
46+
# )
47+
# zarr_full_path = file_paths[0].split(file_name)[0] + file_name
48+
#
49+
# # 3) Copy the downloaded Zarr into tests/data
50+
# if os.path.isdir(str(folder)):
51+
# shutil.rmtree(str(folder))
52+
# shutil.copytree(Path(zarr_full_path) / file_name, folder)
53+
# return Path(folder)
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
{
2+
"multiscales": [
3+
{
4+
"axes": [
5+
{
6+
"name": "c",
7+
"type": "channel"
8+
},
9+
{
10+
"name": "z",
11+
"type": "space",
12+
"unit": "micrometer"
13+
},
14+
{
15+
"name": "y",
16+
"type": "space",
17+
"unit": "micrometer"
18+
},
19+
{
20+
"name": "x",
21+
"type": "space",
22+
"unit": "micrometer"
23+
}
24+
],
25+
"datasets": [
26+
{
27+
"coordinateTransformations": [
28+
{
29+
"scale": [
30+
1.0,
31+
1.0,
32+
5.153617443012883,
33+
5.153617443012883
34+
],
35+
"type": "scale"
36+
}
37+
],
38+
"path": "0"
39+
},
40+
{
41+
"coordinateTransformations": [
42+
{
43+
"scale": [
44+
1.0,
45+
1.0,
46+
10.307234886025766,
47+
10.307234886025766
48+
],
49+
"type": "scale"
50+
}
51+
],
52+
"path": "1"
53+
},
54+
{
55+
"coordinateTransformations": [
56+
{
57+
"scale": [
58+
1.0,
59+
1.0,
60+
20.61446977205153,
61+
20.61446977205153
62+
],
63+
"type": "scale"
64+
}
65+
],
66+
"path": "2"
67+
},
68+
{
69+
"coordinateTransformations": [
70+
{
71+
"scale": [
72+
1.0,
73+
1.0,
74+
41.22893954410306,
75+
41.22893954410306
76+
],
77+
"type": "scale"
78+
}
79+
],
80+
"path": "3"
81+
},
82+
{
83+
"coordinateTransformations": [
84+
{
85+
"scale": [
86+
1.0,
87+
1.0,
88+
82.45787908820613,
89+
82.45787908820613
90+
],
91+
"type": "scale"
92+
}
93+
],
94+
"path": "4"
95+
}
96+
],
97+
"version": "0.4"
98+
}
99+
],
100+
"omero": {
101+
"channels": [
102+
{
103+
"active": true,
104+
"color": "0000FF",
105+
"label": "DAPI_2",
106+
"wavelength_id": "A01_C01",
107+
"window": {
108+
"end": 3500.0,
109+
"max": 65535.0,
110+
"min": 0.0,
111+
"start": 0.0
112+
}
113+
},
114+
{
115+
"active": true,
116+
"color": "FF0000",
117+
"label": "ECadherin_2",
118+
"wavelength_id": "A02_C04",
119+
"window": {
120+
"end": 1800.0,
121+
"max": 65535.0,
122+
"min": 0.0,
123+
"start": 0.0
124+
}
125+
}
126+
]
127+
}
128+
}

0 commit comments

Comments
 (0)