torch_em.data.datasets.medical.emidec

The EMIDEC dataset contains annotations for the left ventricle and for myocardial infarction in delayed-enhancement (late gadolinium enhanced) cardiac MRI.

The data was curated for the EMIDEC challenge (https://emidec.com), which was held together with the STACOM workshop at MICCAI 2020. The public release of the segmentation contest consists of 100 training cases, 33 normal cases (case ids with the letter 'N') and 67 pathological cases (case ids with the letter 'P'). The 50 cases of the test set are distributed without ground truth and are therefore not exposed here, so that this module provides 100 annotated volumes out of the 150 exams of the database.

The label ids are described in LABEL_IDS: 0 = background, 1 = cavity, 2 = normal myocardium, 3 = myocardial infarction, 4 = no-reflow (permanent microvascular obstruction). The whole myocardium is the union of the ids 2, 3 and 4.

The DE-MRI are stored as nifti volumes with the slice axis last, so they are converted to hdf5 volumes with the slice axis first (the keys are 'raw' and 'labels') by this module.

The data is located at https://emidec.com/dataset and is licensed under CC BY-NC-SA 4.0.

This dataset is from the publication https://doi.org/10.3390/data5040089. Please cite it if you use this dataset in your research.

  1"""The EMIDEC dataset contains annotations for the left ventricle and for myocardial infarction
  2in delayed-enhancement (late gadolinium enhanced) cardiac MRI.
  3
  4The data was curated for the EMIDEC challenge (https://emidec.com), which was held together with the STACOM
  5workshop at MICCAI 2020. The public release of the segmentation contest consists of 100 training cases,
  633 normal cases (case ids with the letter 'N') and 67 pathological cases (case ids with the letter 'P').
  7The 50 cases of the test set are distributed without ground truth and are therefore not exposed here,
  8so that this module provides 100 annotated volumes out of the 150 exams of the database.
  9
 10The label ids are described in `LABEL_IDS`: 0 = background, 1 = cavity, 2 = normal myocardium,
 113 = myocardial infarction, 4 = no-reflow (permanent microvascular obstruction). The whole myocardium is
 12the union of the ids 2, 3 and 4.
 13
 14The DE-MRI are stored as nifti volumes with the slice axis last, so they are converted to hdf5 volumes
 15with the slice axis first (the keys are 'raw' and 'labels') by this module.
 16
 17The data is located at https://emidec.com/dataset and is licensed under CC BY-NC-SA 4.0.
 18
 19This dataset is from the publication https://doi.org/10.3390/data5040089.
 20Please cite it if you use this dataset in your research.
 21"""
 22
 23import os
 24from glob import glob
 25from tqdm import tqdm
 26from natsort import natsorted
 27from typing import Union, Tuple, List, Optional, Literal
 28
 29import numpy as np
 30
 31from torch.utils.data import Dataset, DataLoader
 32
 33import torch_em
 34
 35from .. import util
 36
 37
 38URL = "https://emidec.com/dataset/download"
 39CHECKSUM = "9270495a800d717092a6c21feba4b4f20fe90c61be85ac2a0cfc35570e321528"
 40
 41LABEL_IDS = {"background": 0, "cavity": 1, "myocardium": 2, "infarction": 3, "no_reflow": 4}
 42
 43PATHOLOGIES = {"normal": "N", "pathological": "P"}
 44
 45
 46def _preprocess_inputs(data_dir, preprocessed_dir):
 47    import h5py
 48    import nibabel as nib
 49
 50    case_dirs = [p for p in natsorted(glob(os.path.join(data_dir, "Case_*"))) if os.path.isdir(p)]
 51    os.makedirs(preprocessed_dir, exist_ok=True)
 52
 53    for case_dir in tqdm(case_dirs, desc="Preprocessing the EMIDEC cases"):
 54        case_id = os.path.basename(case_dir)
 55        volume_path = os.path.join(preprocessed_dir, f"{case_id}.h5")
 56        if os.path.exists(volume_path):
 57            continue
 58
 59        # The transpose maps the nifti axis order (X, Y, Z) to the (Z, Y, X) order used for the volumes.
 60        raw = np.asarray(nib.load(os.path.join(case_dir, "Images", f"{case_id}.nii.gz")).dataobj).T
 61        labels = np.asarray(nib.load(os.path.join(case_dir, "Contours", f"{case_id}.nii.gz")).dataobj).T
 62
 63        # The file is written to a temporary path first, so that an interrupted run leaves no corrupt file.
 64        with h5py.File(f"{volume_path}.tmp", "w") as f:
 65            f.create_dataset("raw", data=raw, compression="gzip")
 66            f.create_dataset("labels", data=labels.astype("uint8"), compression="gzip")
 67
 68        os.rename(f"{volume_path}.tmp", volume_path)
 69
 70
 71def get_emidec_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 72    """Download the EMIDEC dataset.
 73
 74    Args:
 75        path: Filepath to a folder where the data is downloaded for further processing.
 76        download: Whether to download the data if it is not present.
 77
 78    Returns:
 79        Filepath where the preprocessed data is stored.
 80    """
 81    preprocessed_dir = os.path.join(path, "preprocessed")
 82    if len(glob(os.path.join(preprocessed_dir, "*.h5"))) == 100:
 83        return preprocessed_dir
 84
 85    os.makedirs(path, exist_ok=True)
 86
 87    data_dir = os.path.join(path, "emidec-dataset-1.0.1")
 88    if not os.path.exists(data_dir):
 89        zip_path = os.path.join(path, "emidec-dataset-1.0.1.zip")
 90        util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM)
 91        util.unzip(zip_path=zip_path, dst=path)
 92
 93    _preprocess_inputs(data_dir, preprocessed_dir)
 94    return preprocessed_dir
 95
 96
 97def get_emidec_paths(
 98    path: Union[os.PathLike, str],
 99    pathology: Optional[Literal["normal", "pathological"]] = None,
100    download: bool = False,
101) -> List[str]:
102    """Get paths to the EMIDEC data.
103
104    Args:
105        path: Filepath to a folder where the data is downloaded for further processing.
106        pathology: The choice of cases. Either 'normal' or 'pathological'. If None, all cases are returned.
107        download: Whether to download the data if it is not present.
108
109    Returns:
110        List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels').
111    """
112    data_dir = get_emidec_data(path, download)
113
114    if pathology is not None and pathology not in PATHOLOGIES:
115        raise ValueError(f"'{pathology}' is not a valid pathology. Please choose one of {list(PATHOLOGIES.keys())}.")
116
117    prefix = "*" if pathology is None else PATHOLOGIES[pathology]
118    volume_paths = natsorted(glob(os.path.join(data_dir, f"Case_{prefix}*.h5")))
119    assert len(volume_paths) > 0, f"Could not find any preprocessed volumes in '{data_dir}'."
120
121    return volume_paths
122
123
124def get_emidec_dataset(
125    path: Union[os.PathLike, str],
126    patch_shape: Tuple[int, ...],
127    pathology: Optional[Literal["normal", "pathological"]] = None,
128    resize_inputs: bool = False,
129    download: bool = False,
130    **kwargs
131) -> Dataset:
132    """Get the EMIDEC dataset for myocardial infarction segmentation.
133
134    Args:
135        path: Filepath to a folder where the data is downloaded for further processing.
136        patch_shape: The patch shape to use for training.
137        pathology: The choice of cases. Either 'normal' or 'pathological'. If None, all cases are returned.
138        resize_inputs: Whether to resize inputs to the desired patch shape.
139        download: Whether to download the data if it is not present.
140        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
141
142    Returns:
143        The segmentation dataset.
144    """
145    volume_paths = get_emidec_paths(path, pathology, download)
146
147    if resize_inputs:
148        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
149        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
150            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
151        )
152
153    return torch_em.default_segmentation_dataset(
154        raw_paths=volume_paths,
155        raw_key="raw",
156        label_paths=volume_paths,
157        label_key="labels",
158        patch_shape=patch_shape,
159        is_seg_dataset=True,
160        **kwargs
161    )
162
163
164def get_emidec_loader(
165    path: Union[os.PathLike, str],
166    batch_size: int,
167    patch_shape: Tuple[int, ...],
168    pathology: Optional[Literal["normal", "pathological"]] = None,
169    resize_inputs: bool = False,
170    download: bool = False,
171    **kwargs
172) -> DataLoader:
173    """Get the EMIDEC dataloader for myocardial infarction segmentation.
174
175    Args:
176        path: Filepath to a folder where the data is downloaded for further processing.
177        batch_size: The batch size for training.
178        patch_shape: The patch shape to use for training.
179        pathology: The choice of cases. Either 'normal' or 'pathological'. If None, all cases are returned.
180        resize_inputs: Whether to resize inputs to the desired patch shape.
181        download: Whether to download the data if it is not present.
182        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
183
184    Returns:
185        The DataLoader.
186    """
187    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
188    dataset = get_emidec_dataset(path, patch_shape, pathology, resize_inputs, download, **ds_kwargs)
189    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URL = 'https://emidec.com/dataset/download'
CHECKSUM = '9270495a800d717092a6c21feba4b4f20fe90c61be85ac2a0cfc35570e321528'
LABEL_IDS = {'background': 0, 'cavity': 1, 'myocardium': 2, 'infarction': 3, 'no_reflow': 4}
PATHOLOGIES = {'normal': 'N', 'pathological': 'P'}
def get_emidec_data(path: Union[os.PathLike, str], download: bool = False) -> str:
72def get_emidec_data(path: Union[os.PathLike, str], download: bool = False) -> str:
73    """Download the EMIDEC dataset.
74
75    Args:
76        path: Filepath to a folder where the data is downloaded for further processing.
77        download: Whether to download the data if it is not present.
78
79    Returns:
80        Filepath where the preprocessed data is stored.
81    """
82    preprocessed_dir = os.path.join(path, "preprocessed")
83    if len(glob(os.path.join(preprocessed_dir, "*.h5"))) == 100:
84        return preprocessed_dir
85
86    os.makedirs(path, exist_ok=True)
87
88    data_dir = os.path.join(path, "emidec-dataset-1.0.1")
89    if not os.path.exists(data_dir):
90        zip_path = os.path.join(path, "emidec-dataset-1.0.1.zip")
91        util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM)
92        util.unzip(zip_path=zip_path, dst=path)
93
94    _preprocess_inputs(data_dir, preprocessed_dir)
95    return preprocessed_dir

Download the EMIDEC dataset.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • download: Whether to download the data if it is not present.
Returns:

Filepath where the preprocessed data is stored.

def get_emidec_paths( path: Union[os.PathLike, str], pathology: Optional[Literal['normal', 'pathological']] = None, download: bool = False) -> List[str]:
 98def get_emidec_paths(
 99    path: Union[os.PathLike, str],
100    pathology: Optional[Literal["normal", "pathological"]] = None,
101    download: bool = False,
102) -> List[str]:
103    """Get paths to the EMIDEC data.
104
105    Args:
106        path: Filepath to a folder where the data is downloaded for further processing.
107        pathology: The choice of cases. Either 'normal' or 'pathological'. If None, all cases are returned.
108        download: Whether to download the data if it is not present.
109
110    Returns:
111        List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels').
112    """
113    data_dir = get_emidec_data(path, download)
114
115    if pathology is not None and pathology not in PATHOLOGIES:
116        raise ValueError(f"'{pathology}' is not a valid pathology. Please choose one of {list(PATHOLOGIES.keys())}.")
117
118    prefix = "*" if pathology is None else PATHOLOGIES[pathology]
119    volume_paths = natsorted(glob(os.path.join(data_dir, f"Case_{prefix}*.h5")))
120    assert len(volume_paths) > 0, f"Could not find any preprocessed volumes in '{data_dir}'."
121
122    return volume_paths

Get paths to the EMIDEC data.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • pathology: The choice of cases. Either 'normal' or 'pathological'. If None, all cases are returned.
  • download: Whether to download the data if it is not present.
Returns:

List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels').

def get_emidec_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, ...], pathology: Optional[Literal['normal', 'pathological']] = None, resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
125def get_emidec_dataset(
126    path: Union[os.PathLike, str],
127    patch_shape: Tuple[int, ...],
128    pathology: Optional[Literal["normal", "pathological"]] = None,
129    resize_inputs: bool = False,
130    download: bool = False,
131    **kwargs
132) -> Dataset:
133    """Get the EMIDEC dataset for myocardial infarction segmentation.
134
135    Args:
136        path: Filepath to a folder where the data is downloaded for further processing.
137        patch_shape: The patch shape to use for training.
138        pathology: The choice of cases. Either 'normal' or 'pathological'. If None, all cases are returned.
139        resize_inputs: Whether to resize inputs to the desired patch shape.
140        download: Whether to download the data if it is not present.
141        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
142
143    Returns:
144        The segmentation dataset.
145    """
146    volume_paths = get_emidec_paths(path, pathology, download)
147
148    if resize_inputs:
149        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
150        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
151            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
152        )
153
154    return torch_em.default_segmentation_dataset(
155        raw_paths=volume_paths,
156        raw_key="raw",
157        label_paths=volume_paths,
158        label_key="labels",
159        patch_shape=patch_shape,
160        is_seg_dataset=True,
161        **kwargs
162    )

Get the EMIDEC dataset for myocardial infarction segmentation.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • patch_shape: The patch shape to use for training.
  • pathology: The choice of cases. Either 'normal' or 'pathological'. If None, all cases are returned.
  • resize_inputs: Whether to resize inputs to the desired patch shape.
  • 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_emidec_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, ...], pathology: Optional[Literal['normal', 'pathological']] = None, resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
165def get_emidec_loader(
166    path: Union[os.PathLike, str],
167    batch_size: int,
168    patch_shape: Tuple[int, ...],
169    pathology: Optional[Literal["normal", "pathological"]] = None,
170    resize_inputs: bool = False,
171    download: bool = False,
172    **kwargs
173) -> DataLoader:
174    """Get the EMIDEC dataloader for myocardial infarction segmentation.
175
176    Args:
177        path: Filepath to a folder where the data is downloaded for further processing.
178        batch_size: The batch size for training.
179        patch_shape: The patch shape to use for training.
180        pathology: The choice of cases. Either 'normal' or 'pathological'. If None, all cases are returned.
181        resize_inputs: Whether to resize inputs to the desired patch shape.
182        download: Whether to download the data if it is not present.
183        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
184
185    Returns:
186        The DataLoader.
187    """
188    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
189    dataset = get_emidec_dataset(path, patch_shape, pathology, resize_inputs, download, **ds_kwargs)
190    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the EMIDEC dataloader for myocardial infarction segmentation.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • batch_size: The batch size for training.
  • patch_shape: The patch shape to use for training.
  • pathology: The choice of cases. Either 'normal' or 'pathological'. If None, all cases are returned.
  • resize_inputs: Whether to resize inputs to the desired patch shape.
  • 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.