Skip to content

Commit bd7cade

Browse files
committed
relabeling added
1 parent 2b93c28 commit bd7cade

File tree

3 files changed

+43
-1
lines changed

3 files changed

+43
-1
lines changed

src/ilastik_tasks/__FRACTAL_MANIFEST__.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@
9595
"type": "string",
9696
"description": "Path to the Ilastik model (e.g. `\"somemodel.ilp\"`)."
9797
},
98+
"relabeling": {
99+
"default": true,
100+
"title": "Relabeling",
101+
"type": "boolean",
102+
"description": "If `True`, apply relabeling so that label values are unique for all objects in the well."
103+
},
98104
"foreground_class": {
99105
"default": 0,
100106
"title": "Foreground Class",

src/ilastik_tasks/ilastik_pixel_classification_segmentation.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,22 +76,29 @@ def setup_ilastik(model_path: str):
7676

7777
def segment_ROI(
7878
input_data: np.ndarray,
79+
num_labels_tot: dict[str, int],
7980
shell: Any,
8081
foreground_class: int = 0,
8182
threshold: float = 0.5,
8283
min_size: int = 15,
8384
label_dtype: Optional[np.dtype] = None,
85+
relabeling: bool = True,
8486
) -> np.ndarray:
8587
"""Run the Ilastik model on a single ROI.
8688
8789
Args:
8890
input_data: np.ndarray of shape (t, z, y, x, c).
91+
num_labels_tot: Number of labels already in total image. Used for
92+
relabeling purposes. Using a dict to have a mutable object that
93+
can be edited from within the function without having to be passed
94+
back through the masked_loading_wrapper.
8995
shell: Ilastik headless shell.
9096
foreground_class: Class to be considered as foreground
9197
during prediction thresholding.
9298
threshold: Threshold for the Ilastik model.
9399
min_size: Minimum size for the Ilastik model.
94100
label_dtype: Label images are cast into this `np.dtype`.
101+
relabeling: Whether relabeling based on num_labels_tot is performed.
95102
96103
Returns:
97104
np.ndarray: Segmented image. Shape (z, y, x).
@@ -145,6 +152,23 @@ def segment_ROI(
145152
ilastik_labels = label(ilastik_labels)
146153
logger.info(f"number of labels after filtering for size = {ilastik_labels.max()}")
147154
label_props = regionprops(ilastik_labels)
155+
156+
# Shift labels and update relabeling counters
157+
if relabeling:
158+
num_labels_roi = np.max(ilastik_labels)
159+
ilastik_labels[ilastik_labels > 0] += num_labels_tot["num_labels_tot"]
160+
num_labels_tot["num_labels_tot"] += num_labels_roi
161+
162+
# Write some logs
163+
logger.info(f"ROI had {num_labels_roi=}, {num_labels_tot=}")
164+
165+
# Check that total number of labels is under control
166+
if num_labels_tot["num_labels_tot"] > np.iinfo(label_dtype).max:
167+
raise ValueError(
168+
"ERROR in re-labeling:"
169+
f"Reached {num_labels_tot} labels, "
170+
f"but dtype={label_dtype}"
171+
)
148172

149173
return ilastik_labels.astype(label_dtype)
150174

@@ -166,6 +190,7 @@ def ilastik_pixel_classification_segmentation(
166190
use_masks: bool = True,
167191
# Ilastik-related arguments
168192
ilastik_model: str,
193+
relabeling: bool = True,
169194
foreground_class: int = 0,
170195
threshold: float = 0.5,
171196
min_size: int = 15,
@@ -197,6 +222,8 @@ def ilastik_pixel_classification_segmentation(
197222
loading is relevant when only a subset of the bounding box should
198223
actually be processed (e.g. running within `emb_ROI_table`).
199224
ilastik_model: Path to the Ilastik model (e.g. `"somemodel.ilp"`).
225+
relabeling: If `True`, apply relabeling so that label values are
226+
unique for all objects in the well.
200227
foreground_class: Class to be considered as foreground during
201228
prediction thresholding.
202229
threshold: Probabiltiy threshold for the Ilastik model.
@@ -385,9 +412,13 @@ def ilastik_pixel_classification_segmentation(
385412

386413
# Initialize other things
387414
logger.info(f"Start ilastik pixel classification task for {zarr_url}")
415+
logger.info(f"relabeling: {relabeling}")
388416
logger.info(f"{data_zyx.shape}")
389417
logger.info(f"{data_zyx.chunks}")
390418

419+
# Counters for relabeling
420+
num_labels_tot = {"num_labels_tot": 0}
421+
391422
# Iterate over ROIs
392423
num_ROIs = len(list_indices)
393424

@@ -433,10 +464,12 @@ def ilastik_pixel_classification_segmentation(
433464
# Prepare keyword arguments for segment_ROI function
434465
kwargs_segment_ROI = {
435466
"shell": shell,
467+
"num_labels_tot": num_labels_tot,
436468
"foreground_class": foreground_class,
437469
"threshold": threshold,
438470
"min_size": min_size,
439471
"label_dtype": label_dtype,
472+
"relabeling": relabeling,
440473
}
441474

442475
# Prepare keyword arguments for preprocessing function

tests/test_ilastik_pixel_classification_segmentation.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,19 @@ def test_ilastik_pixel_classification_segmentation_task_3D(test_data_dir_3d):
4242
channel2=IlastikChannel2InputModel(label="ECadherin_2"),
4343
ilastik_model=str(ilastik_model),
4444
output_label_name="test_label",
45+
relabeling=True,
4546
)
4647

4748
# Test failing of task if model was trained with two channels
4849
# but only one is provided
4950
with pytest.raises(ValueError):
5051
ilastik_pixel_classification_segmentation(
5152
zarr_url=zarr_url,
52-
level=0,
53+
level=4,
5354
channel=IlastikChannel1InputModel(label="DAPI_2"),
5455
channel2=None,
5556
ilastik_model=str(ilastik_model),
5657
output_label_name="test_label_single_channel",
5758
)
59+
60+

0 commit comments

Comments
 (0)