torch_em.data.datasets.electron_microscopy.mitoemv2

MitoEM v2 is a benchmark collection for 3D mitochondria instance segmentation in electron microscopy.

It contains eight challenging datasets with expert-verified labels, covering biologically complex scenarios such as dense mitochondrial packing, hyperfused networks, thin-necked morphologies, and ultrastructurally ambiguous boundaries.

The data is located at https://doi.org/10.5281/zenodo.17635006. The dataset is from the publication https://doi.org/10.1101/2025.11.12.687478. Please cite it if you use this dataset in your research.

  1"""MitoEM v2 is a benchmark collection for 3D mitochondria instance segmentation in electron microscopy.
  2
  3It contains eight challenging datasets with expert-verified labels, covering biologically complex
  4scenarios such as dense mitochondrial packing, hyperfused networks, thin-necked morphologies,
  5and ultrastructurally ambiguous boundaries.
  6
  7The data is located at https://doi.org/10.5281/zenodo.17635006.
  8The dataset is from the publication https://doi.org/10.1101/2025.11.12.687478.
  9Please cite it if you use this dataset in your research.
 10"""
 11
 12import os
 13import json
 14from glob import glob
 15from typing import Union, Literal, Optional, Tuple, List
 16
 17import numpy as np
 18
 19from torch.utils.data import Dataset, DataLoader
 20
 21import torch_em
 22
 23from .. import util
 24
 25
 26BASE_URL = "https://zenodo.org/records/17635006/files"
 27
 28DATASETS = {
 29    "beta": "Dataset001_ME2-Beta",
 30    "jurkat": "Dataset002_ME2-Jurkat",
 31    "macro": "Dataset003_ME2-Macro",
 32    "mossy": "Dataset004_ME2-Mossy",
 33    "podo": "Dataset005_ME2-Podo",
 34    "pyra": "Dataset006_ME2-Pyra",
 35    "sperm": "Dataset007_ME2-Sperm",
 36    "stem": "Dataset008_ME2-Stem",
 37}
 38
 39DATASET_NAMES = list(DATASETS.keys())
 40
 41
 42CHUNKS = (32, 256, 256)
 43
 44
 45def _read_split_info(dataset_dir):
 46    # The split file is called 'split.json' in most datasets, but 'splits.json' in the pyra dataset.
 47    for name in ("split.json", "splits.json"):
 48        split_path = os.path.join(dataset_dir, name)
 49        if os.path.exists(split_path):
 50            with open(split_path) as f:
 51                return json.load(f)[0]
 52    raise FileNotFoundError(f"Cannot find the split file in {dataset_dir}.")
 53
 54
 55def _write_nifti(f, key, nifti_path, transpose):
 56    """Write a NIfTI volume into the n5 file as (z, y, x).
 57
 58    NIfTI stores (x, y, z) in Fortran order, so a slab along the last axis is contiguous on disk and the volume can
 59    be converted slab by slab, without loading it into memory as a whole.
 60    """
 61    import nibabel as nib
 62
 63    nii = nib.load(nifti_path)
 64    dtype = nii.get_data_dtype()
 65    if not transpose:
 66        f.create_dataset(key, data=np.asarray(nii.dataobj).astype(dtype), chunks=CHUNKS, compression="gzip")
 67        return
 68
 69    shape = nii.shape[::-1]
 70    ds = f.create_dataset(key, shape=shape, dtype=dtype, chunks=CHUNKS, compression="gzip")
 71    for z0 in range(0, shape[0], CHUNKS[0]):
 72        z1 = min(z0 + CHUNKS[0], shape[0])
 73        ds[z0:z1] = np.asarray(nii.dataobj[:, :, z0:z1]).astype(dtype).transpose(2, 1, 0)
 74
 75
 76def _preprocess_dataset(path, dataset_name, dataset_dir):
 77    """Preprocess a single dataset: convert NIfTI to n5."""
 78    import shutil
 79    import z5py
 80
 81    n5_dir = os.path.join(path, "n5_data", dataset_name)
 82    os.makedirs(n5_dir, exist_ok=True)
 83    split_info = _read_split_info(dataset_dir)
 84
 85    processed = {}
 86    for split_name, split_key in [("train", "train"), ("val", "val"), ("test", "test")]:
 87        samples = split_info.get(split_key, [])
 88        if not samples:
 89            continue
 90
 91        for sample in samples:
 92            # Determine source directories based on split
 93            if split_name == "test":
 94                img_dir = "imagesTs"
 95                lbl_dir = "labelsTs"
 96            else:
 97                img_dir = "imagesTr"
 98                lbl_dir = "labelsTr"
 99
100            img_nifti = os.path.join(dataset_dir, img_dir, f"{sample}_0000.nii.gz")
101            lbl_nifti = os.path.join(dataset_dir, lbl_dir, f"{sample}.nii.gz")
102
103            if not os.path.exists(img_nifti) or not os.path.exists(lbl_nifti):
104                continue
105
106            n5_path = os.path.join(n5_dir, f"{sample}.n5")
107
108            if not os.path.exists(n5_path):
109                print(f"Converting {sample} to n5...")
110                # The volume is written under a temporary name and moved in place once it is complete,
111                # so that an interrupted conversion is not mistaken for a complete one.
112                tmp_path = os.path.join(n5_dir, f"{sample}_incomplete.n5")
113                if os.path.exists(tmp_path):
114                    shutil.rmtree(tmp_path)
115
116                # The labels of this sample are stored in (z, y, x) order in the NIfTI file already.
117                # NOTE: Even then they do not match the raw data (the mitochondria are drawn in empty resin).
118                transpose_labels = sample != "me2-jurkat_train02"
119                with z5py.File(tmp_path, "a") as f:
120                    _write_nifti(f, "raw", img_nifti, transpose=True)
121                    _write_nifti(f, "labels", lbl_nifti, transpose=transpose_labels)
122                    if f["raw"].shape != f["labels"].shape:
123                        raise RuntimeError(f"There is a shape mismatch between raw and labels for {sample}.")
124                os.rename(tmp_path, n5_path)
125
126            if split_name not in processed:
127                processed[split_name] = []
128            processed[split_name].append(n5_path)
129
130    return processed
131
132
133def get_mitoemv2_data(
134    path: Union[os.PathLike, str],
135    dataset: str,
136    download: bool = False,
137) -> str:
138    """Download and preprocess a MitoEM v2 dataset.
139
140    Args:
141        path: Filepath to a folder where the downloaded data will be saved.
142        dataset: The dataset to download. One of 'beta', 'jurkat', 'macro', 'mossy',
143            'podo', 'pyra', 'sperm', or 'stem'.
144        download: Whether to download the data if it is not present.
145
146    Returns:
147        The filepath to the preprocessed n5 data directory.
148    """
149    assert dataset in DATASETS, f"'{dataset}' is not valid. Choose from {DATASET_NAMES}."
150
151    dataset_folder = DATASETS[dataset]
152    n5_dir = os.path.join(path, "n5_data", dataset)
153    zip_path = os.path.join(path, f"{dataset_folder}.zip")
154    dataset_dir = os.path.join(path, dataset_folder)
155
156    # Without the NIfTI data there is nothing left to convert, so the converted data is used as it is.
157    if not os.path.exists(dataset_dir) and len(glob(os.path.join(n5_dir, "*.n5"))) > 0:
158        return n5_dir
159
160    # Download if needed
161    if not os.path.exists(dataset_dir):
162        os.makedirs(path, exist_ok=True)
163        url = f"{BASE_URL}/{dataset_folder}.zip"
164        util.download_source(path=zip_path, url=url, download=download, checksum=None)
165        util.unzip(zip_path=zip_path, dst=path)
166
167    # Preprocess
168    _preprocess_dataset(path, dataset, dataset_dir)
169
170    return n5_dir
171
172
173def get_mitoemv2_paths(
174    path: Union[os.PathLike, str],
175    dataset: Optional[Union[str, List[str]]] = None,
176    split: Literal["train", "val", "test"] = "train",
177    download: bool = False,
178) -> List[str]:
179    """Get paths to the MitoEM v2 data.
180
181    Args:
182        path: Filepath to a folder where the downloaded data will be saved.
183        dataset: The dataset(s) to use. One of 'beta', 'jurkat', 'macro', 'mossy',
184            'podo', 'pyra', 'sperm', or 'stem'. Can also be a list of dataset names.
185            If None, all datasets will be used.
186        split: The data split to use. One of 'train', 'val', or 'test'.
187        download: Whether to download the data if it is not present.
188
189    Returns:
190        List of filepaths for the n5 data.
191    """
192    from natsort import natsorted
193
194    assert split in ("train", "val", "test"), f"'{split}' is not a valid split."
195
196    if dataset is None:
197        dataset = DATASET_NAMES
198    elif isinstance(dataset, str):
199        dataset = [dataset]
200
201    all_n5_paths = []
202    for ds in dataset:
203        n5_dir = get_mitoemv2_data(path, ds, download)
204
205        # Read split info to get correct samples
206        dataset_folder = DATASETS[ds]
207        dataset_dir = os.path.join(path, dataset_folder)
208        split_info = _read_split_info(dataset_dir)
209
210        samples = split_info.get(split, [])
211        n5_paths = [os.path.join(n5_dir, f"{sample}.n5") for sample in samples]
212        n5_paths = [p for p in n5_paths if os.path.exists(p)]
213        all_n5_paths.extend(n5_paths)
214
215    assert len(all_n5_paths) > 0, f"No data found for {dataset}/{split}"
216
217    return natsorted(all_n5_paths)
218
219
220def get_mitoemv2_dataset(
221    path: Union[os.PathLike, str],
222    patch_shape: Tuple[int, int, int],
223    dataset: Optional[Union[str, List[str]]] = None,
224    split: Literal["train", "val", "test"] = "train",
225    download: bool = False,
226    offsets: Optional[List[List[int]]] = None,
227    boundaries: bool = False,
228    binary: bool = False,
229    **kwargs
230) -> Dataset:
231    """Get the MitoEM v2 dataset for mitochondria segmentation in EM.
232
233    Args:
234        path: Filepath to a folder where the downloaded data will be saved.
235        patch_shape: The patch shape to use for training.
236        dataset: The dataset(s) to use. One of 'beta', 'jurkat', 'macro', 'mossy',
237            'podo', 'pyra', 'sperm', or 'stem'. Can also be a list of dataset names.
238            If None, all datasets will be used.
239        split: The data split to use. One of 'train', 'val', or 'test'.
240        download: Whether to download the data if it is not present.
241        offsets: Offset values for affinity computation used as target.
242        boundaries: Whether to compute boundaries as the target.
243        binary: Whether to return a binary segmentation target.
244        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
245
246    Returns:
247        The segmentation dataset.
248    """
249    assert len(patch_shape) == 3
250
251    n5_paths = get_mitoemv2_paths(path, dataset, split, download)
252
253    kwargs, _ = util.add_instance_label_transform(
254        kwargs, add_binary_target=True, binary=binary, boundaries=boundaries, offsets=offsets
255    )
256
257    return torch_em.default_segmentation_dataset(
258        raw_paths=n5_paths,
259        raw_key="raw",
260        label_paths=n5_paths,
261        label_key="labels",
262        patch_shape=patch_shape,
263        **kwargs
264    )
265
266
267def get_mitoemv2_loader(
268    path: Union[os.PathLike, str],
269    batch_size: int,
270    patch_shape: Tuple[int, int, int],
271    dataset: Optional[Union[str, List[str]]] = None,
272    split: Literal["train", "val", "test"] = "train",
273    download: bool = False,
274    offsets: Optional[List[List[int]]] = None,
275    boundaries: bool = False,
276    binary: bool = False,
277    **kwargs
278) -> DataLoader:
279    """Get the MitoEM v2 dataloader for mitochondria segmentation in EM.
280
281    Args:
282        path: Filepath to a folder where the downloaded data will be saved.
283        batch_size: The batch size for training.
284        patch_shape: The patch shape to use for training.
285        dataset: The dataset(s) to use. One of 'beta', 'jurkat', 'macro', 'mossy',
286            'podo', 'pyra', 'sperm', or 'stem'. Can also be a list of dataset names.
287            If None, all datasets will be used.
288        split: The data split to use. One of 'train', 'val', or 'test'.
289        download: Whether to download the data if it is not present.
290        offsets: Offset values for affinity computation used as target.
291        boundaries: Whether to compute boundaries as the target.
292        binary: Whether to return a binary segmentation target.
293        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
294
295    Returns:
296        The DataLoader.
297    """
298    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
299    dataset_obj = get_mitoemv2_dataset(
300        path=path,
301        patch_shape=patch_shape,
302        dataset=dataset,
303        split=split,
304        download=download,
305        offsets=offsets,
306        boundaries=boundaries,
307        binary=binary,
308        **ds_kwargs,
309    )
310    return torch_em.get_data_loader(dataset=dataset_obj, batch_size=batch_size, **loader_kwargs)
BASE_URL = 'https://zenodo.org/records/17635006/files'
DATASETS = {'beta': 'Dataset001_ME2-Beta', 'jurkat': 'Dataset002_ME2-Jurkat', 'macro': 'Dataset003_ME2-Macro', 'mossy': 'Dataset004_ME2-Mossy', 'podo': 'Dataset005_ME2-Podo', 'pyra': 'Dataset006_ME2-Pyra', 'sperm': 'Dataset007_ME2-Sperm', 'stem': 'Dataset008_ME2-Stem'}
DATASET_NAMES = ['beta', 'jurkat', 'macro', 'mossy', 'podo', 'pyra', 'sperm', 'stem']
CHUNKS = (32, 256, 256)
def get_mitoemv2_data( path: Union[os.PathLike, str], dataset: str, download: bool = False) -> str:
134def get_mitoemv2_data(
135    path: Union[os.PathLike, str],
136    dataset: str,
137    download: bool = False,
138) -> str:
139    """Download and preprocess a MitoEM v2 dataset.
140
141    Args:
142        path: Filepath to a folder where the downloaded data will be saved.
143        dataset: The dataset to download. One of 'beta', 'jurkat', 'macro', 'mossy',
144            'podo', 'pyra', 'sperm', or 'stem'.
145        download: Whether to download the data if it is not present.
146
147    Returns:
148        The filepath to the preprocessed n5 data directory.
149    """
150    assert dataset in DATASETS, f"'{dataset}' is not valid. Choose from {DATASET_NAMES}."
151
152    dataset_folder = DATASETS[dataset]
153    n5_dir = os.path.join(path, "n5_data", dataset)
154    zip_path = os.path.join(path, f"{dataset_folder}.zip")
155    dataset_dir = os.path.join(path, dataset_folder)
156
157    # Without the NIfTI data there is nothing left to convert, so the converted data is used as it is.
158    if not os.path.exists(dataset_dir) and len(glob(os.path.join(n5_dir, "*.n5"))) > 0:
159        return n5_dir
160
161    # Download if needed
162    if not os.path.exists(dataset_dir):
163        os.makedirs(path, exist_ok=True)
164        url = f"{BASE_URL}/{dataset_folder}.zip"
165        util.download_source(path=zip_path, url=url, download=download, checksum=None)
166        util.unzip(zip_path=zip_path, dst=path)
167
168    # Preprocess
169    _preprocess_dataset(path, dataset, dataset_dir)
170
171    return n5_dir

Download and preprocess a MitoEM v2 dataset.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • dataset: The dataset to download. One of 'beta', 'jurkat', 'macro', 'mossy', 'podo', 'pyra', 'sperm', or 'stem'.
  • download: Whether to download the data if it is not present.
Returns:

The filepath to the preprocessed n5 data directory.

def get_mitoemv2_paths( path: Union[os.PathLike, str], dataset: Union[List[str], str, NoneType] = None, split: Literal['train', 'val', 'test'] = 'train', download: bool = False) -> List[str]:
174def get_mitoemv2_paths(
175    path: Union[os.PathLike, str],
176    dataset: Optional[Union[str, List[str]]] = None,
177    split: Literal["train", "val", "test"] = "train",
178    download: bool = False,
179) -> List[str]:
180    """Get paths to the MitoEM v2 data.
181
182    Args:
183        path: Filepath to a folder where the downloaded data will be saved.
184        dataset: The dataset(s) to use. One of 'beta', 'jurkat', 'macro', 'mossy',
185            'podo', 'pyra', 'sperm', or 'stem'. Can also be a list of dataset names.
186            If None, all datasets will be used.
187        split: The data split to use. One of 'train', 'val', or 'test'.
188        download: Whether to download the data if it is not present.
189
190    Returns:
191        List of filepaths for the n5 data.
192    """
193    from natsort import natsorted
194
195    assert split in ("train", "val", "test"), f"'{split}' is not a valid split."
196
197    if dataset is None:
198        dataset = DATASET_NAMES
199    elif isinstance(dataset, str):
200        dataset = [dataset]
201
202    all_n5_paths = []
203    for ds in dataset:
204        n5_dir = get_mitoemv2_data(path, ds, download)
205
206        # Read split info to get correct samples
207        dataset_folder = DATASETS[ds]
208        dataset_dir = os.path.join(path, dataset_folder)
209        split_info = _read_split_info(dataset_dir)
210
211        samples = split_info.get(split, [])
212        n5_paths = [os.path.join(n5_dir, f"{sample}.n5") for sample in samples]
213        n5_paths = [p for p in n5_paths if os.path.exists(p)]
214        all_n5_paths.extend(n5_paths)
215
216    assert len(all_n5_paths) > 0, f"No data found for {dataset}/{split}"
217
218    return natsorted(all_n5_paths)

Get paths to the MitoEM v2 data.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • dataset: The dataset(s) to use. One of 'beta', 'jurkat', 'macro', 'mossy', 'podo', 'pyra', 'sperm', or 'stem'. Can also be a list of dataset names. If None, all datasets will be used.
  • split: The data split to use. One of 'train', 'val', or 'test'.
  • download: Whether to download the data if it is not present.
Returns:

List of filepaths for the n5 data.

def get_mitoemv2_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, int, int], dataset: Union[List[str], str, NoneType] = None, split: Literal['train', 'val', 'test'] = 'train', download: bool = False, offsets: Optional[List[List[int]]] = None, boundaries: bool = False, binary: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
221def get_mitoemv2_dataset(
222    path: Union[os.PathLike, str],
223    patch_shape: Tuple[int, int, int],
224    dataset: Optional[Union[str, List[str]]] = None,
225    split: Literal["train", "val", "test"] = "train",
226    download: bool = False,
227    offsets: Optional[List[List[int]]] = None,
228    boundaries: bool = False,
229    binary: bool = False,
230    **kwargs
231) -> Dataset:
232    """Get the MitoEM v2 dataset for mitochondria segmentation in EM.
233
234    Args:
235        path: Filepath to a folder where the downloaded data will be saved.
236        patch_shape: The patch shape to use for training.
237        dataset: The dataset(s) to use. One of 'beta', 'jurkat', 'macro', 'mossy',
238            'podo', 'pyra', 'sperm', or 'stem'. Can also be a list of dataset names.
239            If None, all datasets will be used.
240        split: The data split to use. One of 'train', 'val', or 'test'.
241        download: Whether to download the data if it is not present.
242        offsets: Offset values for affinity computation used as target.
243        boundaries: Whether to compute boundaries as the target.
244        binary: Whether to return a binary segmentation target.
245        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
246
247    Returns:
248        The segmentation dataset.
249    """
250    assert len(patch_shape) == 3
251
252    n5_paths = get_mitoemv2_paths(path, dataset, split, download)
253
254    kwargs, _ = util.add_instance_label_transform(
255        kwargs, add_binary_target=True, binary=binary, boundaries=boundaries, offsets=offsets
256    )
257
258    return torch_em.default_segmentation_dataset(
259        raw_paths=n5_paths,
260        raw_key="raw",
261        label_paths=n5_paths,
262        label_key="labels",
263        patch_shape=patch_shape,
264        **kwargs
265    )

Get the MitoEM v2 dataset for mitochondria segmentation in EM.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • patch_shape: The patch shape to use for training.
  • dataset: The dataset(s) to use. One of 'beta', 'jurkat', 'macro', 'mossy', 'podo', 'pyra', 'sperm', or 'stem'. Can also be a list of dataset names. If None, all datasets will be used.
  • split: The data split to use. One of 'train', 'val', or 'test'.
  • download: Whether to download the data if it is not present.
  • offsets: Offset values for affinity computation used as target.
  • boundaries: Whether to compute boundaries as the target.
  • binary: Whether to return a binary segmentation target.
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset.
Returns:

The segmentation dataset.

def get_mitoemv2_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, int, int], dataset: Union[List[str], str, NoneType] = None, split: Literal['train', 'val', 'test'] = 'train', download: bool = False, offsets: Optional[List[List[int]]] = None, boundaries: bool = False, binary: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
268def get_mitoemv2_loader(
269    path: Union[os.PathLike, str],
270    batch_size: int,
271    patch_shape: Tuple[int, int, int],
272    dataset: Optional[Union[str, List[str]]] = None,
273    split: Literal["train", "val", "test"] = "train",
274    download: bool = False,
275    offsets: Optional[List[List[int]]] = None,
276    boundaries: bool = False,
277    binary: bool = False,
278    **kwargs
279) -> DataLoader:
280    """Get the MitoEM v2 dataloader for mitochondria segmentation in EM.
281
282    Args:
283        path: Filepath to a folder where the downloaded data will be saved.
284        batch_size: The batch size for training.
285        patch_shape: The patch shape to use for training.
286        dataset: The dataset(s) to use. One of 'beta', 'jurkat', 'macro', 'mossy',
287            'podo', 'pyra', 'sperm', or 'stem'. Can also be a list of dataset names.
288            If None, all datasets will be used.
289        split: The data split to use. One of 'train', 'val', or 'test'.
290        download: Whether to download the data if it is not present.
291        offsets: Offset values for affinity computation used as target.
292        boundaries: Whether to compute boundaries as the target.
293        binary: Whether to return a binary segmentation target.
294        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
295
296    Returns:
297        The DataLoader.
298    """
299    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
300    dataset_obj = get_mitoemv2_dataset(
301        path=path,
302        patch_shape=patch_shape,
303        dataset=dataset,
304        split=split,
305        download=download,
306        offsets=offsets,
307        boundaries=boundaries,
308        binary=binary,
309        **ds_kwargs,
310    )
311    return torch_em.get_data_loader(dataset=dataset_obj, batch_size=batch_size, **loader_kwargs)

Get the MitoEM v2 dataloader for mitochondria segmentation in EM.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • batch_size: The batch size for training.
  • patch_shape: The patch shape to use for training.
  • dataset: The dataset(s) to use. One of 'beta', 'jurkat', 'macro', 'mossy', 'podo', 'pyra', 'sperm', or 'stem'. Can also be a list of dataset names. If None, all datasets will be used.
  • split: The data split to use. One of 'train', 'val', or 'test'.
  • download: Whether to download the data if it is not present.
  • offsets: Offset values for affinity computation used as target.
  • boundaries: Whether to compute boundaries as the target.
  • binary: Whether to return a binary segmentation target.
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset or for the PyTorch DataLoader.
Returns:

The DataLoader.