steinbock.classification
ilastik
special
ilastik
create_and_save_ilastik_project(ilastik_crop_files, ilastik_project_file)
Source code in steinbock/classification/ilastik/ilastik.py
def create_and_save_ilastik_project(
ilastik_crop_files: Sequence[Union[str, PathLike]],
ilastik_project_file: Union[str, PathLike],
):
dataset_id = str(uuid1())
shutil.copyfile(_project_file_template, ilastik_project_file)
with h5py.File(ilastik_project_file, mode="a", libver=_h5py_libver) as f:
infos_group = f["Input Data/infos"]
for i, ilastik_crop_file in enumerate(ilastik_crop_files):
rel_ilastik_crop_file = Path(ilastik_crop_file).relative_to(
Path(ilastik_project_file).parent
)
with h5py.File(
ilastik_crop_file, mode="r", libver=_h5py_libver
) as f_crop:
crop_shape = f_crop[_crop_dataset_path].shape
lane_group = infos_group.create_group(f"lane{i:04d}")
lane_group.create_group("Prediction Mask")
raw_data_group = lane_group.create_group("Raw Data")
_fix_raw_data_group_inplace(
raw_data_group,
dataset_id=dataset_id,
file_path=str(rel_ilastik_crop_file / _crop_dataset_path),
nickname=Path(ilastik_crop_file).stem,
shape=crop_shape,
)
create_ilastik_crop(ilastik_img, crop_size, rng)
Source code in steinbock/classification/ilastik/ilastik.py
def create_ilastik_crop(
ilastik_img: np.ndarray, crop_size: int, rng: np.random.Generator
) -> Tuple[Optional[int], Optional[int], Optional[np.ndarray]]:
yx_shape = ilastik_img.shape[1:]
if all(shape >= crop_size for shape in yx_shape):
x_start = rng.integers(ilastik_img.shape[2] - crop_size)
x_end = x_start + crop_size
y_start = rng.integers(ilastik_img.shape[1] - crop_size)
y_end = y_start + crop_size
ilastik_crop = ilastik_img[:, y_start:y_end, x_start:x_end]
return x_start, y_start, io.to_dtype(ilastik_crop, io.img_dtype)
return None, None, None
create_ilastik_crops_from_disk(ilastik_img_files, crop_size, seed=None)
Source code in steinbock/classification/ilastik/ilastik.py
def create_ilastik_crops_from_disk(
ilastik_img_files: Sequence[Union[str, PathLike]],
crop_size: int,
seed=None,
) -> Generator[
Tuple[Path, Optional[int], Optional[int], Optional[np.ndarray]],
None,
None,
]:
rng = np.random.default_rng(seed=seed)
for ilastik_img_file in ilastik_img_files:
x_start, y_start, ilastik_crop = create_ilastik_crop(
read_ilastik_image(ilastik_img_file), crop_size, rng
)
yield Path(ilastik_img_file), x_start, y_start, ilastik_crop
del ilastik_crop
create_ilastik_image(img, channel_groups=None, aggr_func=<function mean at 0x7faf1b5c6160>, prepend_mean=True, mean_factor=100.0, scale_factor=1)
Source code in steinbock/classification/ilastik/ilastik.py
def create_ilastik_image(
img: np.ndarray,
channel_groups: Optional[np.ndarray] = None,
aggr_func: Callable[[np.ndarray], np.ndarray] = np.mean,
prepend_mean: bool = True,
mean_factor: float = 100.0,
scale_factor: int = 1,
) -> np.ndarray:
ilastik_img = img
if channel_groups is not None:
ilastik_img = np.stack(
[
aggr_func(ilastik_img[channel_groups == channel_group], axis=0)
for channel_group in np.unique(channel_groups)
if not np.isnan(channel_group)
]
)
if prepend_mean:
mean_img = ilastik_img.mean(axis=0, keepdims=True) * mean_factor
ilastik_img = np.concatenate((mean_img, ilastik_img))
if scale_factor > 1:
# bilinear resizing (for compatibility with IMC Segmentation Pipeline)
output_shape = (
ilastik_img.shape[0],
ilastik_img.shape[1] * scale_factor,
ilastik_img.shape[2] * scale_factor,
)
ilastik_img = resize(
ilastik_img, output_shape, order=1, mode="symmetric"
)
return io.to_dtype(ilastik_img, io.img_dtype)
create_ilastik_images_from_disk(img_files, channel_groups=None, aggr_func=<function mean at 0x7faf1b5c6160>, prepend_mean=True, mean_factor=100.0, scale_factor=1)
Source code in steinbock/classification/ilastik/ilastik.py
def create_ilastik_images_from_disk(
img_files: Sequence[Union[str, PathLike]],
channel_groups: Optional[np.ndarray] = None,
aggr_func: Callable[[np.ndarray], np.ndarray] = np.mean,
prepend_mean: bool = True,
mean_factor: float = 100.0,
scale_factor: int = 1,
) -> Generator[Tuple[Path, np.ndarray], None, None]:
for img_file in img_files:
ilastik_img = create_ilastik_image(
io.read_image(img_file, ignore_dtype=True),
channel_groups=channel_groups,
aggr_func=aggr_func,
prepend_mean=prepend_mean,
mean_factor=mean_factor,
scale_factor=scale_factor,
)
yield Path(img_file), ilastik_img
del ilastik_img
fix_ilastik_crops_from_disk(ilastik_crop_files, orig_axis_order=None)
Source code in steinbock/classification/ilastik/ilastik.py
def fix_ilastik_crops_from_disk(
ilastik_crop_files: Sequence[Union[str, PathLike]],
orig_axis_order: Union[str, Sequence, None] = None,
) -> Generator[Tuple[Path, Tuple[int, ...], np.ndarray], None, None]:
for ilastik_crop_file in ilastik_crop_files:
with h5py.File(ilastik_crop_file, mode="r", libver=_h5py_libver) as f:
ilastik_crop_dataset = None
if _crop_dataset_path in f:
ilastik_crop_dataset = f[_crop_dataset_path]
elif Path(ilastik_crop_file).stem in f:
ilastik_crop_dataset = f[Path(ilastik_crop_file).stem]
elif len(f) == 1:
ilastik_crop_dataset = next(iter(f.values()))
else:
raise ValueError(f"Unknown dataset name: {ilastik_crop_file}")
if ilastik_crop_dataset.attrs.get("steinbock", False):
continue
ilastik_crop = ilastik_crop_dataset[()]
if orig_axis_order is not None:
orig_axis_order = list(orig_axis_order)
elif "axistags" in ilastik_crop_dataset.attrs:
axis_tags, _ = _str_decode(
ilastik_crop_dataset.attrs["axistags"]
)
axis_tags_json = json.loads(axis_tags)
orig_axis_order = [x["key"] for x in axis_tags_json["axes"]]
else:
raise ValueError(f"Unknown axis order: {ilastik_crop_file}")
if len(orig_axis_order) != ilastik_crop.ndim:
raise ValueError(f"Incompatible axis order: {ilastik_crop_file}")
channel_axis_index = orig_axis_order.index("c")
size_x = ilastik_crop.shape[orig_axis_order.index("x")]
size_y = ilastik_crop.shape[orig_axis_order.index("y")]
num_channels = ilastik_crop.size // (size_x * size_y)
if ilastik_crop.shape[channel_axis_index] != num_channels:
channel_axis_indices = (
i
for i, a in enumerate(orig_axis_order)
if a not in ("x", "y")
and ilastik_crop.shape[i] == num_channels
)
channel_axis_index = next(channel_axis_indices, None)
if channel_axis_index is None:
raise ValueError(f"Unknown channel axis: {ilastik_crop_file}")
new_axis_order = orig_axis_order.copy()
new_axis_order.insert(0, new_axis_order.pop(channel_axis_index))
new_axis_order.insert(1, new_axis_order.pop(new_axis_order.index("y")))
new_axis_order.insert(2, new_axis_order.pop(new_axis_order.index("x")))
transpose_axes = [orig_axis_order.index(a) for a in new_axis_order]
ilastik_crop = np.transpose(ilastik_crop, axes=transpose_axes)
ilastik_crop = np.reshape(ilastik_crop, ilastik_crop.shape[:3])
ilastik_crop = io.to_dtype(ilastik_crop, io.img_dtype)
yield Path(ilastik_crop_file), transpose_axes, ilastik_crop
del ilastik_crop
fix_ilastik_project_file_inplace(ilastik_project_file, ilastik_crop_dir, ilastik_probab_dir, ilastik_crop_shapes, transpose_axes)
Source code in steinbock/classification/ilastik/ilastik.py
def fix_ilastik_project_file_inplace(
ilastik_project_file: Union[str, PathLike],
ilastik_crop_dir: Union[str, PathLike],
ilastik_probab_dir: Union[str, PathLike],
ilastik_crop_shapes: Dict[str, Tuple[int, ...]],
transpose_axes: List[int],
):
rel_ilastik_crop_dir = Path(ilastik_crop_dir).relative_to(
Path(ilastik_project_file).parent
)
rel_ilastik_probab_dir = Path(ilastik_probab_dir).relative_to(
Path(ilastik_project_file).parent
)
with h5py.File(ilastik_project_file, "a", libver=_h5py_libver) as f:
input_data_group = f.get("Input Data")
if input_data_group is not None:
_fix_input_data_group_inplace(
input_data_group, rel_ilastik_crop_dir, ilastik_crop_shapes
)
pixel_classification_group = f.get("PixelClassification")
if pixel_classification_group is not None:
_fix_pixel_classification_group_inplace(
pixel_classification_group, transpose_axes
)
prediction_export_group = f["Prediction Export"]
if prediction_export_group is not None:
_fix_prediction_export_group_inplace(
prediction_export_group, rel_ilastik_probab_dir
)
list_ilastik_crop_files(crop_dir)
Source code in steinbock/classification/ilastik/ilastik.py
def list_ilastik_crop_files(crop_dir: Union[str, PathLike]) -> List[Path]:
return sorted(Path(crop_dir).rglob("*.h5"))
list_ilastik_image_files(img_dir)
Source code in steinbock/classification/ilastik/ilastik.py
def list_ilastik_image_files(img_dir: Union[str, PathLike]) -> List[Path]:
return sorted(Path(img_dir).rglob("*.h5"))
read_ilastik_crop(ilastik_crop_stem)
Source code in steinbock/classification/ilastik/ilastik.py
def read_ilastik_crop(ilastik_crop_stem: Union[str, PathLike]) -> np.ndarray:
ilastik_crop_file = io.as_path_with_suffix(ilastik_crop_stem, ".h5")
with h5py.File(ilastik_crop_file, mode="r", libver=_h5py_libver) as f:
return io.to_dtype(f[str(_crop_dataset_path)][()], io.img_dtype)
read_ilastik_image(ilastik_img_stem)
Source code in steinbock/classification/ilastik/ilastik.py
def read_ilastik_image(ilastik_img_stem: Union[str, PathLike]) -> np.ndarray:
ilastik_img_file = io.as_path_with_suffix(ilastik_img_stem, ".h5")
with h5py.File(ilastik_img_file, mode="r", libver=_h5py_libver) as f:
return io.to_dtype(f[str(_img_dataset_path)][()], io.img_dtype)
run_pixel_classification(ilastik_binary, ilastik_project_file, ilastik_img_files, ilastik_probab_dir, num_threads=None, memory_limit=None, ilastik_env=None)
Source code in steinbock/classification/ilastik/ilastik.py
def run_pixel_classification(
ilastik_binary: Union[str, PathLike],
ilastik_project_file: Union[str, PathLike],
ilastik_img_files: Sequence[Union[str, PathLike]],
ilastik_probab_dir: Union[str, PathLike],
num_threads: Optional[int] = None,
memory_limit: Optional[int] = None,
ilastik_env: Optional[Dict[str, str]] = None,
) -> subprocess.CompletedProcess:
output_filename_format = Path(ilastik_probab_dir) / "{nickname}.tiff"
args = [
str(ilastik_binary),
"--headless",
f"--project={ilastik_project_file}",
"--readonly",
"--input_axes=cyx",
"--export_source=Probabilities",
"--output_format=tiff",
f"--output_filename_format={output_filename_format}",
"--export_dtype=uint16",
"--output_axis_order=yxc",
"--pipeline_result_drange=(0.0,1.0)",
"--export_drange=(0,65535)",
]
for ilastik_img_file in ilastik_img_files:
args.append(str(Path(ilastik_img_file) / _img_dataset_path))
if ilastik_env is not None:
ilastik_env = ilastik_env.copy()
if num_threads is not None:
ilastik_env["LAZYFLOW_THREADS"] = num_threads
if memory_limit is not None:
ilastik_env["LAZYFLOW_TOTAL_RAM_MB"] = memory_limit
result = run_captured(args, env=ilastik_env)
ilastik_probab_files = Path(ilastik_probab_dir).rglob(
f"*-{_img_dataset_path}.tiff"
)
for ilastik_probab_file in sorted(ilastik_probab_files):
ilastik_probab_file.rename(
ilastik_probab_file.with_name(
ilastik_probab_file.name.replace(f"-{_img_dataset_path}", "")
)
)
return result
write_ilastik_crop(ilastik_crop, ilastik_crop_stem)
Source code in steinbock/classification/ilastik/ilastik.py
def write_ilastik_crop(
ilastik_crop: np.ndarray, ilastik_crop_stem: Union[str, PathLike]
):
ilastik_crop = io.to_dtype(ilastik_crop, io.img_dtype)
ilastik_crop_file = io.as_path_with_suffix(ilastik_crop_stem, ".h5")
with h5py.File(ilastik_crop_file, mode="w", libver=_h5py_libver) as f:
dataset = _create_or_replace_dataset(
f, _crop_dataset_path, ilastik_crop
)
dataset.attrs["display_mode"] = _str_encode(
_dataset_display_mode, ascii=True
)
dataset.attrs["axistags"] = _str_encode(_dataset_axistags, ascii=True)
dataset.attrs["steinbock"] = True
return ilastik_crop_file
write_ilastik_image(ilastik_img, ilastik_img_stem)
Source code in steinbock/classification/ilastik/ilastik.py
def write_ilastik_image(
ilastik_img: np.ndarray, ilastik_img_stem: Union[str, PathLike]
) -> Path:
ilastik_img = io.to_dtype(ilastik_img, io.img_dtype)
ilastik_img_file = io.as_path_with_suffix(ilastik_img_stem, ".h5")
with h5py.File(ilastik_img_file, mode="w", libver=_h5py_libver) as f:
dataset = _create_or_replace_dataset(f, _img_dataset_path, ilastik_img)
dataset.attrs["display_mode"] = _str_encode(
_dataset_display_mode, ascii=True
)
dataset.attrs["axistags"] = _str_encode(_dataset_axistags, ascii=True)
dataset.attrs["steinbock"] = True
return ilastik_img_file