torch_em.data.datasets.light_microscopy.dememseg

The DeMemSeg dataset contains annotations for prospore membrane segmentation in fluorescence microscopy images of sporulating budding yeast.

The dataset contains 2133 single cell crops of Saccharomyces cerevisiae with 7593 expert annotated prospore membranes (PSMs). Each crop comes from a 2d maximum intensity projection of a 3d z-stack. The original annotations overlap, because several prospore membranes can cover the same pixel. This loader flattens them into one instance label image. A pixel that belongs to several membranes keeps the label of the last membrane, so about 13 percent of the foreground pixels get an arbitrary label.

The dataset is located at https://ssbd.riken.jp/repository/443/ under the CC BY 4.0 license. This dataset is from the publication https://doi.org/10.1247/csf.25032. Please cite it if you use this dataset in your research.

  1"""The DeMemSeg dataset contains annotations for prospore membrane segmentation
  2in fluorescence microscopy images of sporulating budding yeast.
  3
  4The dataset contains 2133 single cell crops of Saccharomyces cerevisiae with 7593 expert annotated
  5prospore membranes (PSMs). Each crop comes from a 2d maximum intensity projection of a 3d z-stack.
  6The original annotations overlap, because several prospore membranes can cover the same pixel. This
  7loader flattens them into one instance label image. A pixel that belongs to several membranes keeps
  8the label of the last membrane, so about 13 percent of the foreground pixels get an arbitrary label.
  9
 10The dataset is located at https://ssbd.riken.jp/repository/443/ under the CC BY 4.0 license.
 11This dataset is from the publication https://doi.org/10.1247/csf.25032.
 12Please cite it if you use this dataset in your research.
 13"""
 14
 15import os
 16from glob import glob
 17from pathlib import Path
 18from natsort import natsorted
 19from typing import List, Literal, Optional, Tuple, Union
 20
 21import numpy as np
 22import imageio.v3 as imageio
 23
 24from torch.utils.data import DataLoader, Dataset
 25
 26import torch_em
 27
 28from .. import util
 29
 30
 31URL = "https://ssbd.riken.jp/data/ssbd-000443/zip/OriginalData_MMdetDataset.zip"
 32CHECKSUM = "b9772e343956358cf5e89459a28b02e6e3f0e05f403f640e8705957a0185a4c6"
 33
 34SPLITS = ("train", "val", "test")
 35
 36
 37def _create_instance_labels(data_dir: str, split: str) -> str:
 38    """Merge the per-instance masks of each crop into one instance label image."""
 39    import h5py
 40    from tqdm import tqdm
 41
 42    image_dir = os.path.join(data_dir, "images", split)
 43    mask_dir = os.path.join(data_dir, "masks", split)
 44
 45    preprocessed_dir = os.path.join(data_dir, "preprocessed", split)
 46    os.makedirs(preprocessed_dir, exist_ok=True)
 47
 48    image_paths = natsorted(glob(os.path.join(image_dir, "*.png")))
 49    for image_path in tqdm(image_paths, desc=f"Preprocess the '{split}' split"):
 50        stem = Path(image_path).stem
 51        output_path = os.path.join(preprocessed_dir, f"{stem}.h5")
 52        if os.path.exists(output_path):
 53            continue
 54
 55        mask_paths = natsorted(glob(os.path.join(mask_dir, f"{stem}_RoiRegion_*.png")))
 56        if not mask_paths:
 57            raise RuntimeError(f"Could not find any mask for the DeMemSeg image {image_path}.")
 58
 59        raw = imageio.imread(image_path)
 60        if raw.ndim == 3:
 61            raw = raw[..., 0]  # Only the first channel holds the membrane signal.
 62
 63        masks = [imageio.imread(p) > 0 for p in mask_paths]
 64        shape = masks[0].shape
 65
 66        # A crop at the border of the field is clipped, but its masks keep the full size.
 67        # The clipped crop sits in the center of the mask canvas, so pad it on both sides.
 68        if raw.shape != shape:
 69            raw = np.pad(raw, [((s - r) // 2, s - r - (s - r) // 2) for r, s in zip(raw.shape, shape)])
 70
 71        # Paint the large membranes first, so that a membrane inside a larger one keeps its label.
 72        labels = np.zeros(shape, dtype="uint16")
 73        for instance_id, mask in enumerate(sorted(masks, key=lambda mask: -mask.sum()), start=1):
 74            labels[mask] = instance_id
 75
 76        with h5py.File(output_path, "w") as f:
 77            f.create_dataset("raw", data=raw, compression="gzip")
 78            f.create_dataset("labels", data=labels, compression="gzip")
 79
 80    return preprocessed_dir
 81
 82
 83def get_dememseg_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 84    """Download the DeMemSeg dataset.
 85
 86    Args:
 87        path: Filepath to a folder where the downloaded data will be saved.
 88        download: Whether to download the data if it is not present.
 89
 90    Returns:
 91        The filepath to the extracted data.
 92    """
 93    data_dir = os.path.join(path, "OriginalData_MMdetDataset")
 94    if os.path.exists(data_dir):
 95        return data_dir
 96
 97    os.makedirs(path, exist_ok=True)
 98    zip_path = os.path.join(path, "OriginalData_MMdetDataset.zip")
 99    util.download_source(zip_path, URL, download, CHECKSUM)
100    util.unzip(zip_path=zip_path, dst=path)
101
102    return data_dir
103
104
105def get_dememseg_paths(
106    path: Union[os.PathLike, str], split: Literal["train", "val", "test"], download: bool = False,
107) -> List[str]:
108    """Get paths to the DeMemSeg data.
109
110    Args:
111        path: Filepath to a folder where the downloaded data will be saved.
112        split: The data split. Either 'train', 'val' or 'test'.
113        download: Whether to download the data if it is not present.
114
115    Returns:
116        List of filepaths for the preprocessed h5 data.
117    """
118    if split not in SPLITS:
119        raise ValueError(f"'{split}' is not a valid split. Choose from {list(SPLITS)}.")
120
121    data_dir = get_dememseg_data(path, download)
122    preprocessed_dir = _create_instance_labels(data_dir, split)
123
124    volume_paths = natsorted(glob(os.path.join(preprocessed_dir, "*.h5")))
125    if not volume_paths:
126        raise RuntimeError(f"Could not find any preprocessed DeMemSeg data in {preprocessed_dir}.")
127
128    return volume_paths
129
130
131def get_dememseg_dataset(
132    path: Union[os.PathLike, str],
133    patch_shape: Tuple[int, int],
134    split: Literal["train", "val", "test"],
135    offsets: Optional[List[List[int]]] = None,
136    boundaries: bool = False,
137    binary: bool = False,
138    download: bool = False,
139    **kwargs,
140) -> Dataset:
141    """Get the DeMemSeg dataset for prospore membrane segmentation.
142
143    Args:
144        path: Filepath to a folder where the downloaded data will be saved.
145        patch_shape: The 2D patch shape to use for training.
146        split: The data split. Either 'train', 'val' or 'test'.
147        offsets: Offset values for affinity computation used as target.
148        boundaries: Whether to compute boundaries as the target.
149        binary: Whether to use a binary segmentation target.
150        download: Whether to download the data if it is not present.
151        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
152
153    Returns:
154        The segmentation dataset.
155    """
156    if len(patch_shape) != 2:
157        raise ValueError(f"The DeMemSeg patch shape must be two-dimensional, got {patch_shape}.")
158
159    volume_paths = get_dememseg_paths(path, split, download)
160
161    kwargs, _ = util.add_instance_label_transform(
162        kwargs, add_binary_target=True, offsets=offsets, boundaries=boundaries, binary=binary,
163    )
164    kwargs = util.ensure_transforms(ndim=2, **kwargs)
165
166    return torch_em.default_segmentation_dataset(
167        raw_paths=volume_paths,
168        raw_key="raw",
169        label_paths=volume_paths,
170        label_key="labels",
171        patch_shape=patch_shape,
172        ndim=2,
173        **kwargs,
174    )
175
176
177def get_dememseg_loader(
178    path: Union[os.PathLike, str],
179    batch_size: int,
180    patch_shape: Tuple[int, int],
181    split: Literal["train", "val", "test"],
182    offsets: Optional[List[List[int]]] = None,
183    boundaries: bool = False,
184    binary: bool = False,
185    download: bool = False,
186    **kwargs,
187) -> DataLoader:
188    """Get the DeMemSeg dataloader for prospore membrane segmentation.
189
190    Args:
191        path: Filepath to a folder where the downloaded data will be saved.
192        batch_size: The batch size for training.
193        patch_shape: The 2D patch shape to use for training.
194        split: The data split. Either 'train', 'val' or 'test'.
195        offsets: Offset values for affinity computation used as target.
196        boundaries: Whether to compute boundaries as the target.
197        binary: Whether to use a binary segmentation target.
198        download: Whether to download the data if it is not present.
199        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or the PyTorch DataLoader.
200
201    Returns:
202        The DataLoader.
203    """
204    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
205    dataset = get_dememseg_dataset(
206        path=path,
207        patch_shape=patch_shape,
208        split=split,
209        offsets=offsets,
210        boundaries=boundaries,
211        binary=binary,
212        download=download,
213        **ds_kwargs,
214    )
215    return torch_em.get_data_loader(dataset, batch_size=batch_size, **loader_kwargs)
URL = 'https://ssbd.riken.jp/data/ssbd-000443/zip/OriginalData_MMdetDataset.zip'
CHECKSUM = 'b9772e343956358cf5e89459a28b02e6e3f0e05f403f640e8705957a0185a4c6'
SPLITS = ('train', 'val', 'test')
def get_dememseg_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 84def get_dememseg_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 85    """Download the DeMemSeg dataset.
 86
 87    Args:
 88        path: Filepath to a folder where the downloaded data will be saved.
 89        download: Whether to download the data if it is not present.
 90
 91    Returns:
 92        The filepath to the extracted data.
 93    """
 94    data_dir = os.path.join(path, "OriginalData_MMdetDataset")
 95    if os.path.exists(data_dir):
 96        return data_dir
 97
 98    os.makedirs(path, exist_ok=True)
 99    zip_path = os.path.join(path, "OriginalData_MMdetDataset.zip")
100    util.download_source(zip_path, URL, download, CHECKSUM)
101    util.unzip(zip_path=zip_path, dst=path)
102
103    return data_dir

Download the DeMemSeg dataset.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • download: Whether to download the data if it is not present.
Returns:

The filepath to the extracted data.

def get_dememseg_paths( path: Union[os.PathLike, str], split: Literal['train', 'val', 'test'], download: bool = False) -> List[str]:
106def get_dememseg_paths(
107    path: Union[os.PathLike, str], split: Literal["train", "val", "test"], download: bool = False,
108) -> List[str]:
109    """Get paths to the DeMemSeg data.
110
111    Args:
112        path: Filepath to a folder where the downloaded data will be saved.
113        split: The data split. Either 'train', 'val' or 'test'.
114        download: Whether to download the data if it is not present.
115
116    Returns:
117        List of filepaths for the preprocessed h5 data.
118    """
119    if split not in SPLITS:
120        raise ValueError(f"'{split}' is not a valid split. Choose from {list(SPLITS)}.")
121
122    data_dir = get_dememseg_data(path, download)
123    preprocessed_dir = _create_instance_labels(data_dir, split)
124
125    volume_paths = natsorted(glob(os.path.join(preprocessed_dir, "*.h5")))
126    if not volume_paths:
127        raise RuntimeError(f"Could not find any preprocessed DeMemSeg data in {preprocessed_dir}.")
128
129    return volume_paths

Get paths to the DeMemSeg data.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • split: The data split. Either 'train', 'val' or 'test'.
  • download: Whether to download the data if it is not present.
Returns:

List of filepaths for the preprocessed h5 data.

def get_dememseg_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, int], split: Literal['train', 'val', 'test'], offsets: Optional[List[List[int]]] = None, boundaries: bool = False, binary: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
132def get_dememseg_dataset(
133    path: Union[os.PathLike, str],
134    patch_shape: Tuple[int, int],
135    split: Literal["train", "val", "test"],
136    offsets: Optional[List[List[int]]] = None,
137    boundaries: bool = False,
138    binary: bool = False,
139    download: bool = False,
140    **kwargs,
141) -> Dataset:
142    """Get the DeMemSeg dataset for prospore membrane segmentation.
143
144    Args:
145        path: Filepath to a folder where the downloaded data will be saved.
146        patch_shape: The 2D patch shape to use for training.
147        split: The data split. Either 'train', 'val' or 'test'.
148        offsets: Offset values for affinity computation used as target.
149        boundaries: Whether to compute boundaries as the target.
150        binary: Whether to use a binary segmentation target.
151        download: Whether to download the data if it is not present.
152        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
153
154    Returns:
155        The segmentation dataset.
156    """
157    if len(patch_shape) != 2:
158        raise ValueError(f"The DeMemSeg patch shape must be two-dimensional, got {patch_shape}.")
159
160    volume_paths = get_dememseg_paths(path, split, download)
161
162    kwargs, _ = util.add_instance_label_transform(
163        kwargs, add_binary_target=True, offsets=offsets, boundaries=boundaries, binary=binary,
164    )
165    kwargs = util.ensure_transforms(ndim=2, **kwargs)
166
167    return torch_em.default_segmentation_dataset(
168        raw_paths=volume_paths,
169        raw_key="raw",
170        label_paths=volume_paths,
171        label_key="labels",
172        patch_shape=patch_shape,
173        ndim=2,
174        **kwargs,
175    )

Get the DeMemSeg dataset for prospore membrane segmentation.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • patch_shape: The 2D patch shape to use for training.
  • split: The data split. Either 'train', 'val' or 'test'.
  • offsets: Offset values for affinity computation used as target.
  • boundaries: Whether to compute boundaries as the target.
  • binary: Whether to use a binary segmentation target.
  • download: Whether to download the data if it is not present.
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset.
Returns:

The segmentation dataset.

def get_dememseg_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, int], split: Literal['train', 'val', 'test'], offsets: Optional[List[List[int]]] = None, boundaries: bool = False, binary: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
178def get_dememseg_loader(
179    path: Union[os.PathLike, str],
180    batch_size: int,
181    patch_shape: Tuple[int, int],
182    split: Literal["train", "val", "test"],
183    offsets: Optional[List[List[int]]] = None,
184    boundaries: bool = False,
185    binary: bool = False,
186    download: bool = False,
187    **kwargs,
188) -> DataLoader:
189    """Get the DeMemSeg dataloader for prospore membrane segmentation.
190
191    Args:
192        path: Filepath to a folder where the downloaded data will be saved.
193        batch_size: The batch size for training.
194        patch_shape: The 2D patch shape to use for training.
195        split: The data split. Either 'train', 'val' or 'test'.
196        offsets: Offset values for affinity computation used as target.
197        boundaries: Whether to compute boundaries as the target.
198        binary: Whether to use a binary segmentation target.
199        download: Whether to download the data if it is not present.
200        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or the PyTorch DataLoader.
201
202    Returns:
203        The DataLoader.
204    """
205    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
206    dataset = get_dememseg_dataset(
207        path=path,
208        patch_shape=patch_shape,
209        split=split,
210        offsets=offsets,
211        boundaries=boundaries,
212        binary=binary,
213        download=download,
214        **ds_kwargs,
215    )
216    return torch_em.get_data_loader(dataset, batch_size=batch_size, **loader_kwargs)

Get the DeMemSeg dataloader for prospore membrane segmentation.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • batch_size: The batch size for training.
  • patch_shape: The 2D patch shape to use for training.
  • split: The data split. Either 'train', 'val' or 'test'.
  • offsets: Offset values for affinity computation used as target.
  • boundaries: Whether to compute boundaries as the target.
  • binary: Whether to use a binary segmentation target.
  • download: Whether to download the data if it is not present.
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset or the PyTorch DataLoader.
Returns:

The DataLoader.