torch_em.data.datasets.electron_microscopy.hela_metaphase

The HeLa Metaphase dataset contains 3D SBF-SEM volumes of two metaphase HeLa cells with semantic segmentation annotations of organelles.

It contains two sub-datasets, each showing one quadrant of a metaphase cell (30 nm z-resolution):

  • control: A control cell (578 slices of 1477 x 1453 pixels) with the labels 0 (exterior), 1 (mitochondria), 2 (ER), 3 (ER in close proximity to chromosomes, within 320 nm), 4 (chromosomes), 5 (centrioles) and 6 (peripheral ER). It also has a binary cell outline annotation.
  • reep3_4_kd: A REEP3/4 knock-down cell (471 slices of 1586 x 1600 pixels) with the labels 0 (exterior), 1 (mitochondria), 2 (lipid droplets), 3 (ER) and 4 (chromosomes).

Note that the label indices differ between the two sub-datasets. The annotations were created with Microscopy Image Browser and cover one quadrant of the cell and the insides of the spindle area.

The data is available at EMPIAR-10994 (https://doi.org/10.6019/EMPIAR-10994) and is distributed under the CC0 license. It was published in https://doi.org/10.1091/mbc.e18-11-0698. Please cite this publication if you use the dataset in your research.

  1"""The HeLa Metaphase dataset contains 3D SBF-SEM volumes of two metaphase HeLa cells with
  2semantic segmentation annotations of organelles.
  3
  4It contains two sub-datasets, each showing one quadrant of a metaphase cell (30 nm z-resolution):
  5- control: A control cell (578 slices of 1477 x 1453 pixels) with the labels
  6  0 (exterior), 1 (mitochondria), 2 (ER), 3 (ER in close proximity to chromosomes, within 320 nm),
  7  4 (chromosomes), 5 (centrioles) and 6 (peripheral ER). It also has a binary cell outline annotation.
  8- reep3_4_kd: A REEP3/4 knock-down cell (471 slices of 1586 x 1600 pixels) with the labels
  9  0 (exterior), 1 (mitochondria), 2 (lipid droplets), 3 (ER) and 4 (chromosomes).
 10
 11Note that the label indices differ between the two sub-datasets. The annotations were created
 12with Microscopy Image Browser and cover one quadrant of the cell and the insides of the spindle area.
 13
 14The data is available at EMPIAR-10994 (https://doi.org/10.6019/EMPIAR-10994) and is distributed under
 15the CC0 license. It was published in https://doi.org/10.1091/mbc.e18-11-0698.
 16Please cite this publication if you use the dataset in your research.
 17"""
 18
 19import os
 20import hashlib
 21import time
 22from warnings import warn
 23from concurrent.futures import ThreadPoolExecutor
 24from typing import List, Literal, Tuple, Union
 25
 26import numpy as np
 27import requests
 28from torch.utils.data import Dataset, DataLoader
 29
 30import torch_em
 31
 32from .. import util
 33
 34
 35BASE_URL = "https://ftp.ebi.ac.uk/empiar/world_availability/10994/data"
 36# The URL patterns of the (processed) raw slices and the label slices for each sub-dataset.
 37# NOTE: Only the processed tif slices are downloaded (not the raw dm4 files, nor the full 8.7 GB entry).
 38URLS = {
 39    "control": {
 40        "raw": BASE_URL + "/160621_HeLa_Control/02_processed_dataset/160621_HeLa_R01_{i:03d}.tif",
 41        "organelles": BASE_URL + "/160621_HeLa_Control/03_models/OrganelleModel_TIF/Labels_160621_HeLa_R01_combined3_{i:03d}.tif",  # noqa
 42        "cell_outline": BASE_URL + "/160621_HeLa_Control/03_models/CellOutlineModel_TIF/Labels_cell_outlines_{i:03d}.tif",  # noqa
 43    },
 44    "reep3_4_kd": {
 45        "raw": BASE_URL + "/161107_HeLa_REEP3-4_KD_LBR/02_processed_dataset/161107_HeLa_REEP3-4_KD_LBR_{i:03d}.tif",
 46        "organelles": BASE_URL + "/161107_HeLa_REEP3-4_KD_LBR/03_models/Model_TIF/Labels_161107_HeLa_REEP3-4_KD_LBR_{i:03d}.tif",  # noqa
 47    },
 48}
 49NUM_SLICES = {"control": 578, "reep3_4_kd": 471}
 50# The sha256 checksums of the stacked raw volumes (the bytes of the uint8 array in zyx order).
 51CHECKSUMS = {
 52    "control": "882bc5f1794a35b96617964edb294aee243b30ed57758b964738f244bd2b481f",
 53    "reep3_4_kd": "9bc5f9689f0e96d3aafd0bd97cfb5f50dff2e82fb0d7dbbe2fc8f09832c0597c",
 54}
 55
 56# The organelle label of slice 455 of the control cell is an empty folder in the EMPIAR entry.
 57# It is filled with the label of the previous slice (the z resolution is 30 nm).
 58MISSING_LABELS = {"control": {"organelles": [455]}}
 59
 60NAMES = list(URLS.keys())
 61LABEL_TYPES = {"control": ["organelles", "cell_outline"], "reep3_4_kd": ["organelles"]}
 62
 63
 64def _download_slice(url, tmp_dir, key, i):
 65    import tifffile
 66
 67    out_path = os.path.join(tmp_dir, f"{key}_{i:03d}.tif")
 68    if not os.path.exists(out_path):
 69        for attempt in range(5):
 70            try:
 71                with requests.get(url, timeout=120) as r:
 72                    r.raise_for_status()
 73                    content = r.content
 74                # The server may answer with an html error page instead of the tif under load.
 75                if content[:2] not in (b"II", b"MM"):
 76                    raise requests.ConnectionError(f"Did not receive a tif file from {url}.")
 77                break
 78            except (requests.ConnectionError, requests.Timeout):
 79                if attempt == 4:
 80                    raise
 81                time.sleep(2 ** attempt)
 82        with open(out_path + ".incomplete", "wb") as f:
 83            f.write(content)
 84        os.replace(out_path + ".incomplete", out_path)
 85    return tifffile.imread(out_path)
 86
 87
 88def _create_volume(name, h5_path, tmp_dir):
 89    import h5py
 90
 91    n_slices = NUM_SLICES[name]
 92    sha = hashlib.sha256()
 93    tmp_h5 = h5_path + ".incomplete"
 94
 95    with h5py.File(tmp_h5, "w") as f, ThreadPoolExecutor(max_workers=4) as pool:
 96        datasets = {}
 97        for key, url in URLS[name].items():
 98            # Download in blocks of slices, so that tifs are read in order but fetched in parallel.
 99            ds_name = "raw" if key == "raw" else f"labels/{key}"
100            for start in range(1, n_slices + 1, 32):
101                ids = range(start, min(start + 32, n_slices + 1))
102                missing = MISSING_LABELS.get(name, {}).get(key, [])
103                slices = list(pool.map(
104                    lambda i: None if i in missing else _download_slice(url.format(i=i), tmp_dir, key, i), ids
105                ))
106                if ds_name not in datasets:
107                    shape = (n_slices,) + slices[0].shape
108                    datasets[ds_name] = f.create_dataset(
109                        ds_name, shape=shape, dtype="uint8", chunks=(1,) + slices[0].shape, compression="gzip"
110                    )
111                for i, slice_ in zip(ids, slices):
112                    if slice_ is None:
113                        warn(f"The {key} label of slice {i} is missing in EMPIAR-10994, using the previous slice.")
114                        slice_ = datasets[ds_name][i - 2]
115                    assert slice_.shape == datasets[ds_name].shape[1:], f"Unexpected shape of slice {i} for {key}."
116                    datasets[ds_name][i - 1] = slice_.astype("uint8")
117                    if key == "raw":
118                        sha.update(np.ascontiguousarray(slice_, dtype="uint8").tobytes())
119
120    checksum = sha.hexdigest()
121    if CHECKSUMS[name] is not None and checksum != CHECKSUMS[name]:
122        os.remove(tmp_h5)
123        raise RuntimeError(
124            f"The checksum of the downloaded data does not match the expected checksum. "
125            f"Expected: {CHECKSUMS[name]}, got: {checksum}"
126        )
127    os.replace(tmp_h5, h5_path)
128
129
130def get_hela_metaphase_data(
131    path: Union[os.PathLike, str], name: Literal["control", "reep3_4_kd"], download: bool = False
132) -> str:
133    """Download the HeLa Metaphase dataset.
134
135    The slices are downloaded from EMPIAR-10994 and stacked into a single hdf5 file per sub-dataset.
136
137    Args:
138        path: Filepath to a folder where the downloaded data will be saved.
139        name: The name of the sub-dataset. Either 'control' or 'reep3_4_kd'.
140        download: Whether to download the data if it is not present.
141
142    Returns:
143        The filepath to the hdf5 file with the data.
144    """
145    if name not in URLS:
146        raise ValueError(f"'{name}' is not a valid sub-dataset name. Please choose from {NAMES}.")
147
148    h5_path = os.path.join(path, f"hela_metaphase_{name}.h5")
149    if os.path.exists(h5_path):
150        return h5_path
151    if not download:
152        raise RuntimeError(f"Cannot find the data at {h5_path}, but download was set to False")
153
154    tmp_dir = os.path.join(path, f"slices_{name}")
155    os.makedirs(tmp_dir, exist_ok=True)
156    _create_volume(name, h5_path, tmp_dir)
157
158    # The individual slices are not needed anymore.
159    for fname in os.listdir(tmp_dir):
160        os.remove(os.path.join(tmp_dir, fname))
161    os.rmdir(tmp_dir)
162
163    return h5_path
164
165
166def get_hela_metaphase_paths(
167    path: Union[os.PathLike, str],
168    names: Union[Literal["control", "reep3_4_kd"], List[str], None] = None,
169    download: bool = False,
170) -> List[str]:
171    """Get paths to the HeLa Metaphase data.
172
173    Args:
174        path: Filepath to a folder where the downloaded data will be saved.
175        names: The name(s) of the sub-dataset(s). Either 'control' or 'reep3_4_kd', or a list of them.
176            By default, both are used.
177        download: Whether to download the data if it is not present.
178
179    Returns:
180        List of filepaths for the data.
181    """
182    if names is None:
183        names = NAMES
184    elif isinstance(names, str):
185        names = [names]
186
187    return [get_hela_metaphase_data(path, name, download) for name in names]
188
189
190def get_hela_metaphase_dataset(
191    path: Union[os.PathLike, str],
192    patch_shape: Tuple[int, int, int],
193    names: Union[Literal["control", "reep3_4_kd"], List[str], None] = None,
194    label_type: Literal["organelles", "cell_outline"] = "organelles",
195    download: bool = False,
196    **kwargs
197) -> Dataset:
198    """Get the HeLa Metaphase dataset for organelle segmentation in SBF-SEM.
199
200    The 'organelles' labels are semantic (multi-class) labels, see the module description for the label indices.
201    The 'cell_outline' labels are binary and only available for the 'control' sub-dataset.
202
203    Args:
204        path: Filepath to a folder where the downloaded data will be saved.
205        patch_shape: The patch shape to use for training.
206        names: The name(s) of the sub-dataset(s). Either 'control' or 'reep3_4_kd', or a list of them.
207            By default, both are used (only possible for 'organelles' labels).
208        label_type: The type of labels. Either 'organelles' or 'cell_outline'.
209        download: Whether to download the data if it is not present.
210        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
211
212    Returns:
213        The segmentation dataset.
214    """
215    if label_type not in ("organelles", "cell_outline"):
216        raise ValueError(f"'{label_type}' is not a valid label type. Please choose 'organelles' or 'cell_outline'.")
217    if label_type == "cell_outline":
218        if names is None:
219            names = "control"
220        if names not in ("control", ["control"]):
221            raise ValueError("The 'cell_outline' labels are only available for the 'control' sub-dataset.")
222
223    paths = get_hela_metaphase_paths(path, names, download)
224
225    return torch_em.default_segmentation_dataset(
226        raw_paths=paths,
227        raw_key="raw",
228        label_paths=paths,
229        label_key=f"labels/{label_type}",
230        patch_shape=patch_shape,
231        is_seg_dataset=True,
232        **kwargs
233    )
234
235
236def get_hela_metaphase_loader(
237    path: Union[os.PathLike, str],
238    patch_shape: Tuple[int, int, int],
239    batch_size: int,
240    names: Union[Literal["control", "reep3_4_kd"], List[str], None] = None,
241    label_type: Literal["organelles", "cell_outline"] = "organelles",
242    download: bool = False,
243    **kwargs
244) -> DataLoader:
245    """Get the DataLoader for organelle segmentation in the HeLa Metaphase dataset.
246
247    Args:
248        path: Filepath to a folder where the downloaded data will be saved.
249        patch_shape: The patch shape to use for training.
250        batch_size: The batch size for training.
251        names: The name(s) of the sub-dataset(s). Either 'control' or 'reep3_4_kd', or a list of them.
252            By default, both are used (only possible for 'organelles' labels).
253        label_type: The type of labels. Either 'organelles' or 'cell_outline'.
254        download: Whether to download the data if it is not present.
255        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
256
257    Returns:
258        The DataLoader.
259    """
260    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
261    dataset = get_hela_metaphase_dataset(path, patch_shape, names, label_type, download, **ds_kwargs)
262    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
BASE_URL = 'https://ftp.ebi.ac.uk/empiar/world_availability/10994/data'
URLS = {'control': {'raw': 'https://ftp.ebi.ac.uk/empiar/world_availability/10994/data/160621_HeLa_Control/02_processed_dataset/160621_HeLa_R01_{i:03d}.tif', 'organelles': 'https://ftp.ebi.ac.uk/empiar/world_availability/10994/data/160621_HeLa_Control/03_models/OrganelleModel_TIF/Labels_160621_HeLa_R01_combined3_{i:03d}.tif', 'cell_outline': 'https://ftp.ebi.ac.uk/empiar/world_availability/10994/data/160621_HeLa_Control/03_models/CellOutlineModel_TIF/Labels_cell_outlines_{i:03d}.tif'}, 'reep3_4_kd': {'raw': 'https://ftp.ebi.ac.uk/empiar/world_availability/10994/data/161107_HeLa_REEP3-4_KD_LBR/02_processed_dataset/161107_HeLa_REEP3-4_KD_LBR_{i:03d}.tif', 'organelles': 'https://ftp.ebi.ac.uk/empiar/world_availability/10994/data/161107_HeLa_REEP3-4_KD_LBR/03_models/Model_TIF/Labels_161107_HeLa_REEP3-4_KD_LBR_{i:03d}.tif'}}
NUM_SLICES = {'control': 578, 'reep3_4_kd': 471}
CHECKSUMS = {'control': '882bc5f1794a35b96617964edb294aee243b30ed57758b964738f244bd2b481f', 'reep3_4_kd': '9bc5f9689f0e96d3aafd0bd97cfb5f50dff2e82fb0d7dbbe2fc8f09832c0597c'}
MISSING_LABELS = {'control': {'organelles': [455]}}
NAMES = ['control', 'reep3_4_kd']
LABEL_TYPES = {'control': ['organelles', 'cell_outline'], 'reep3_4_kd': ['organelles']}
def get_hela_metaphase_data( path: Union[os.PathLike, str], name: Literal['control', 'reep3_4_kd'], download: bool = False) -> str:
131def get_hela_metaphase_data(
132    path: Union[os.PathLike, str], name: Literal["control", "reep3_4_kd"], download: bool = False
133) -> str:
134    """Download the HeLa Metaphase dataset.
135
136    The slices are downloaded from EMPIAR-10994 and stacked into a single hdf5 file per sub-dataset.
137
138    Args:
139        path: Filepath to a folder where the downloaded data will be saved.
140        name: The name of the sub-dataset. Either 'control' or 'reep3_4_kd'.
141        download: Whether to download the data if it is not present.
142
143    Returns:
144        The filepath to the hdf5 file with the data.
145    """
146    if name not in URLS:
147        raise ValueError(f"'{name}' is not a valid sub-dataset name. Please choose from {NAMES}.")
148
149    h5_path = os.path.join(path, f"hela_metaphase_{name}.h5")
150    if os.path.exists(h5_path):
151        return h5_path
152    if not download:
153        raise RuntimeError(f"Cannot find the data at {h5_path}, but download was set to False")
154
155    tmp_dir = os.path.join(path, f"slices_{name}")
156    os.makedirs(tmp_dir, exist_ok=True)
157    _create_volume(name, h5_path, tmp_dir)
158
159    # The individual slices are not needed anymore.
160    for fname in os.listdir(tmp_dir):
161        os.remove(os.path.join(tmp_dir, fname))
162    os.rmdir(tmp_dir)
163
164    return h5_path

Download the HeLa Metaphase dataset.

The slices are downloaded from EMPIAR-10994 and stacked into a single hdf5 file per sub-dataset.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • name: The name of the sub-dataset. Either 'control' or 'reep3_4_kd'.
  • download: Whether to download the data if it is not present.
Returns:

The filepath to the hdf5 file with the data.

def get_hela_metaphase_paths( path: Union[os.PathLike, str], names: Union[Literal['control', 'reep3_4_kd'], List[str], NoneType] = None, download: bool = False) -> List[str]:
167def get_hela_metaphase_paths(
168    path: Union[os.PathLike, str],
169    names: Union[Literal["control", "reep3_4_kd"], List[str], None] = None,
170    download: bool = False,
171) -> List[str]:
172    """Get paths to the HeLa Metaphase data.
173
174    Args:
175        path: Filepath to a folder where the downloaded data will be saved.
176        names: The name(s) of the sub-dataset(s). Either 'control' or 'reep3_4_kd', or a list of them.
177            By default, both are used.
178        download: Whether to download the data if it is not present.
179
180    Returns:
181        List of filepaths for the data.
182    """
183    if names is None:
184        names = NAMES
185    elif isinstance(names, str):
186        names = [names]
187
188    return [get_hela_metaphase_data(path, name, download) for name in names]

Get paths to the HeLa Metaphase data.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • names: The name(s) of the sub-dataset(s). Either 'control' or 'reep3_4_kd', or a list of them. By default, both are used.
  • download: Whether to download the data if it is not present.
Returns:

List of filepaths for the data.

def get_hela_metaphase_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, int, int], names: Union[Literal['control', 'reep3_4_kd'], List[str], NoneType] = None, label_type: Literal['organelles', 'cell_outline'] = 'organelles', download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
191def get_hela_metaphase_dataset(
192    path: Union[os.PathLike, str],
193    patch_shape: Tuple[int, int, int],
194    names: Union[Literal["control", "reep3_4_kd"], List[str], None] = None,
195    label_type: Literal["organelles", "cell_outline"] = "organelles",
196    download: bool = False,
197    **kwargs
198) -> Dataset:
199    """Get the HeLa Metaphase dataset for organelle segmentation in SBF-SEM.
200
201    The 'organelles' labels are semantic (multi-class) labels, see the module description for the label indices.
202    The 'cell_outline' labels are binary and only available for the 'control' sub-dataset.
203
204    Args:
205        path: Filepath to a folder where the downloaded data will be saved.
206        patch_shape: The patch shape to use for training.
207        names: The name(s) of the sub-dataset(s). Either 'control' or 'reep3_4_kd', or a list of them.
208            By default, both are used (only possible for 'organelles' labels).
209        label_type: The type of labels. Either 'organelles' or 'cell_outline'.
210        download: Whether to download the data if it is not present.
211        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
212
213    Returns:
214        The segmentation dataset.
215    """
216    if label_type not in ("organelles", "cell_outline"):
217        raise ValueError(f"'{label_type}' is not a valid label type. Please choose 'organelles' or 'cell_outline'.")
218    if label_type == "cell_outline":
219        if names is None:
220            names = "control"
221        if names not in ("control", ["control"]):
222            raise ValueError("The 'cell_outline' labels are only available for the 'control' sub-dataset.")
223
224    paths = get_hela_metaphase_paths(path, names, download)
225
226    return torch_em.default_segmentation_dataset(
227        raw_paths=paths,
228        raw_key="raw",
229        label_paths=paths,
230        label_key=f"labels/{label_type}",
231        patch_shape=patch_shape,
232        is_seg_dataset=True,
233        **kwargs
234    )

Get the HeLa Metaphase dataset for organelle segmentation in SBF-SEM.

The 'organelles' labels are semantic (multi-class) labels, see the module description for the label indices. The 'cell_outline' labels are binary and only available for the 'control' sub-dataset.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • patch_shape: The patch shape to use for training.
  • names: The name(s) of the sub-dataset(s). Either 'control' or 'reep3_4_kd', or a list of them. By default, both are used (only possible for 'organelles' labels).
  • label_type: The type of labels. Either 'organelles' or 'cell_outline'.
  • 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_hela_metaphase_loader( path: Union[os.PathLike, str], patch_shape: Tuple[int, int, int], batch_size: int, names: Union[Literal['control', 'reep3_4_kd'], List[str], NoneType] = None, label_type: Literal['organelles', 'cell_outline'] = 'organelles', download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
237def get_hela_metaphase_loader(
238    path: Union[os.PathLike, str],
239    patch_shape: Tuple[int, int, int],
240    batch_size: int,
241    names: Union[Literal["control", "reep3_4_kd"], List[str], None] = None,
242    label_type: Literal["organelles", "cell_outline"] = "organelles",
243    download: bool = False,
244    **kwargs
245) -> DataLoader:
246    """Get the DataLoader for organelle segmentation in the HeLa Metaphase dataset.
247
248    Args:
249        path: Filepath to a folder where the downloaded data will be saved.
250        patch_shape: The patch shape to use for training.
251        batch_size: The batch size for training.
252        names: The name(s) of the sub-dataset(s). Either 'control' or 'reep3_4_kd', or a list of them.
253            By default, both are used (only possible for 'organelles' labels).
254        label_type: The type of labels. Either 'organelles' or 'cell_outline'.
255        download: Whether to download the data if it is not present.
256        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
257
258    Returns:
259        The DataLoader.
260    """
261    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
262    dataset = get_hela_metaphase_dataset(path, patch_shape, names, label_type, download, **ds_kwargs)
263    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the DataLoader for organelle segmentation in the HeLa Metaphase dataset.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • patch_shape: The patch shape to use for training.
  • batch_size: The batch size for training.
  • names: The name(s) of the sub-dataset(s). Either 'control' or 'reep3_4_kd', or a list of them. By default, both are used (only possible for 'organelles' labels).
  • label_type: The type of labels. Either 'organelles' or 'cell_outline'.
  • download: Whether to download the data if it is not present.
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset or for the PyTorch DataLoader.
Returns:

The DataLoader.