torch_em.data.datasets.medical.valdo

The VALDO dataset contains annotations for cerebral microbleed segmentation in brain MRI.

The data was curated for Task 2 of the VALDO challenge ('Where is VALDO? VAscular Lesions DetectiOn and segmentation', https://valdo.grand-challenge.org/Task2), which was held at MICCAI 2021 and targets the detection and segmentation of small vessel disease markers. The public training release of Task 2 consists of 72 subjects pooled from three cohorts (the subject ids 'sub-1xx', 'sub-2xx' and 'sub-3xx' correspond to 11, 34 and 27 subjects respectively), so this module provides 72 annotated volumes. The test data of the challenge was never released.

Three co-registered sequences are available per subject and can be selected with the 'modality' argument: the T2* scan ('t2s'), a T2-weighted scan ('t2') and a T1-weighted scan ('t1'). All of them are resampled to the T2* space, in which the microbleeds are annotated, and the voxel size differs between the three cohorts. NOTE: The T1 and T2 volumes have NaNs in the masked-out background, which are set to zero by this module.

The annotations are binary, see LABEL_IDS: 1 = cerebral microbleed. The challenge evaluation derives individual lesions from them with a connected component analysis with a neighbourhood of 6, so instance labels can be obtained with scipy.ndimage.label.

The three sequences and the annotations of a subject are bundled into one hdf5 file per subject by this module, with the slice axis first (the keys are 'raw/t2s', 'raw/t2', 'raw/t1' and 'labels').

The data is located at https://doi.org/10.5281/zenodo.4687995 and is licensed under CC BY-NC-SA 4.0.

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

  1"""The VALDO dataset contains annotations for cerebral microbleed segmentation in brain MRI.
  2
  3The data was curated for Task 2 of the VALDO challenge ('Where is VALDO? VAscular Lesions DetectiOn and
  4segmentation', https://valdo.grand-challenge.org/Task2), which was held at MICCAI 2021 and targets the
  5detection and segmentation of small vessel disease markers. The public training release of Task 2 consists
  6of 72 subjects pooled from three cohorts (the subject ids 'sub-1xx', 'sub-2xx' and 'sub-3xx' correspond to
  711, 34 and 27 subjects respectively), so this module provides 72 annotated volumes. The test data of the
  8challenge was never released.
  9
 10Three co-registered sequences are available per subject and can be selected with the 'modality' argument:
 11the T2* scan ('t2s'), a T2-weighted scan ('t2') and a T1-weighted scan ('t1'). All of them are resampled to
 12the T2* space, in which the microbleeds are annotated, and the voxel size differs between the three cohorts.
 13NOTE: The T1 and T2 volumes have NaNs in the masked-out background, which are set to zero by this module.
 14
 15The annotations are binary, see `LABEL_IDS`: 1 = cerebral microbleed. The challenge evaluation derives
 16individual lesions from them with a connected component analysis with a neighbourhood of 6, so instance
 17labels can be obtained with `scipy.ndimage.label`.
 18
 19The three sequences and the annotations of a subject are bundled into one hdf5 file per subject by this
 20module, with the slice axis first (the keys are 'raw/t2s', 'raw/t2', 'raw/t1' and 'labels').
 21
 22The data is located at https://doi.org/10.5281/zenodo.4687995 and is licensed under CC BY-NC-SA 4.0.
 23
 24This dataset is from the publication https://doi.org/10.48550/arXiv.2208.07167.
 25Please cite it if you use this dataset in your research.
 26"""
 27
 28import os
 29from glob import glob
 30from tqdm import tqdm
 31from natsort import natsorted
 32from typing import Union, Tuple, List, Literal
 33
 34import numpy as np
 35
 36from torch.utils.data import Dataset, DataLoader
 37
 38import torch_em
 39
 40from .. import util
 41
 42
 43URL = "https://zenodo.org/records/4687995/files/Task2_v2.tar.gz"
 44CHECKSUM = "d47a9104deffd6a3a813ae53bf61209f734cce2818aa11611c1daf71188806b4"
 45
 46LABEL_IDS = {"background": 0, "microbleed": 1}
 47
 48MODALITIES = {"t2s": "T2S", "t2": "T2", "t1": "T1"}
 49
 50
 51def _preprocess_inputs(data_dir, preprocessed_dir):
 52    import h5py
 53    import nibabel as nib
 54
 55    subject_dirs = natsorted(glob(os.path.join(data_dir, "sub-*")))
 56    os.makedirs(preprocessed_dir, exist_ok=True)
 57
 58    for subject_dir in tqdm(subject_dirs, desc="Preprocessing the VALDO subjects"):
 59        subject_id = os.path.basename(subject_dir)
 60        volume_path = os.path.join(preprocessed_dir, f"{subject_id}.h5")
 61        if os.path.exists(volume_path):
 62            continue
 63
 64        # The transpose maps the nifti axis order (X, Y, Z) to the (Z, Y, X) order used for the volumes.
 65        label_path = os.path.join(subject_dir, f"{subject_id}_space-T2S_CMB.nii.gz")
 66        labels = np.asarray(nib.load(label_path).dataobj).T > 0
 67
 68        # The file is written to a temporary path first, so that an interrupted run leaves no corrupt file.
 69        with h5py.File(f"{volume_path}.tmp", "w") as f:
 70            for modality, suffix in MODALITIES.items():
 71                raw_path = os.path.join(subject_dir, f"{subject_id}_space-T2S_desc-masked_{suffix}.nii.gz")
 72                # The background of the T1 and T2 scans is NaN, which would propagate into the normalization.
 73                raw = np.nan_to_num(np.asarray(nib.load(raw_path).dataobj, dtype="float32").T)
 74                f.create_dataset(f"raw/{modality}", data=raw, compression="gzip")
 75
 76            f.create_dataset("labels", data=labels.astype("uint8"), compression="gzip")
 77
 78        os.rename(f"{volume_path}.tmp", volume_path)
 79
 80
 81def get_valdo_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 82    """Download the VALDO Task 2 dataset.
 83
 84    Args:
 85        path: Filepath to a folder where the data is downloaded for further processing.
 86        download: Whether to download the data if it is not present.
 87
 88    Returns:
 89        Filepath where the preprocessed data is stored.
 90    """
 91    preprocessed_dir = os.path.join(path, "preprocessed")
 92    if len(glob(os.path.join(preprocessed_dir, "*.h5"))) == 72:
 93        return preprocessed_dir
 94
 95    os.makedirs(path, exist_ok=True)
 96
 97    data_dir = os.path.join(path, "Task2")
 98    if not os.path.exists(data_dir):
 99        tar_path = os.path.join(path, "Task2_v2.tar.gz")
100        util.download_source(path=tar_path, url=URL, download=download, checksum=CHECKSUM)
101        util.unzip_tarfile(tar_path=tar_path, dst=path, remove=False)
102
103    _preprocess_inputs(data_dir, preprocessed_dir)
104    return preprocessed_dir
105
106
107def get_valdo_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
108    """Get paths to the VALDO Task 2 data.
109
110    Args:
111        path: Filepath to a folder where the data is downloaded for further processing.
112        download: Whether to download the data if it is not present.
113
114    Returns:
115        List of filepaths for the hdf5 files, which contain the image data ('raw/<modality>')
116        and the label data ('labels').
117    """
118    data_dir = get_valdo_data(path, download)
119    volume_paths = natsorted(glob(os.path.join(data_dir, "sub-*.h5")))
120    assert len(volume_paths) > 0, f"Could not find any preprocessed volumes in '{data_dir}'."
121    return volume_paths
122
123
124def get_valdo_dataset(
125    path: Union[os.PathLike, str],
126    patch_shape: Tuple[int, ...],
127    modality: Literal["t2s", "t2", "t1"] = "t2s",
128    resize_inputs: bool = False,
129    download: bool = False,
130    **kwargs
131) -> Dataset:
132    """Get the VALDO Task 2 dataset for cerebral microbleed 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        modality: The MRI sequence. Either 't2s', 't2' or 't1'.
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    if modality not in MODALITIES:
146        raise ValueError(f"'{modality}' is not a valid modality. Please choose one of {list(MODALITIES.keys())}.")
147
148    volume_paths = get_valdo_paths(path, download)
149
150    if resize_inputs:
151        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
152        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
153            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
154        )
155
156    return torch_em.default_segmentation_dataset(
157        raw_paths=volume_paths,
158        raw_key=f"raw/{modality}",
159        label_paths=volume_paths,
160        label_key="labels",
161        patch_shape=patch_shape,
162        is_seg_dataset=True,
163        **kwargs
164    )
165
166
167def get_valdo_loader(
168    path: Union[os.PathLike, str],
169    batch_size: int,
170    patch_shape: Tuple[int, ...],
171    modality: Literal["t2s", "t2", "t1"] = "t2s",
172    resize_inputs: bool = False,
173    download: bool = False,
174    **kwargs
175) -> DataLoader:
176    """Get the VALDO Task 2 dataloader for cerebral microbleed segmentation.
177
178    Args:
179        path: Filepath to a folder where the data is downloaded for further processing.
180        batch_size: The batch size for training.
181        patch_shape: The patch shape to use for training.
182        modality: The MRI sequence. Either 't2s', 't2' or 't1'.
183        resize_inputs: Whether to resize inputs to the desired patch shape.
184        download: Whether to download the data if it is not present.
185        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
186
187    Returns:
188        The DataLoader.
189    """
190    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
191    dataset = get_valdo_dataset(path, patch_shape, modality, resize_inputs, download, **ds_kwargs)
192    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URL = 'https://zenodo.org/records/4687995/files/Task2_v2.tar.gz'
CHECKSUM = 'd47a9104deffd6a3a813ae53bf61209f734cce2818aa11611c1daf71188806b4'
LABEL_IDS = {'background': 0, 'microbleed': 1}
MODALITIES = {'t2s': 'T2S', 't2': 'T2', 't1': 'T1'}
def get_valdo_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 82def get_valdo_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 83    """Download the VALDO Task 2 dataset.
 84
 85    Args:
 86        path: Filepath to a folder where the data is downloaded for further processing.
 87        download: Whether to download the data if it is not present.
 88
 89    Returns:
 90        Filepath where the preprocessed data is stored.
 91    """
 92    preprocessed_dir = os.path.join(path, "preprocessed")
 93    if len(glob(os.path.join(preprocessed_dir, "*.h5"))) == 72:
 94        return preprocessed_dir
 95
 96    os.makedirs(path, exist_ok=True)
 97
 98    data_dir = os.path.join(path, "Task2")
 99    if not os.path.exists(data_dir):
100        tar_path = os.path.join(path, "Task2_v2.tar.gz")
101        util.download_source(path=tar_path, url=URL, download=download, checksum=CHECKSUM)
102        util.unzip_tarfile(tar_path=tar_path, dst=path, remove=False)
103
104    _preprocess_inputs(data_dir, preprocessed_dir)
105    return preprocessed_dir

Download the VALDO Task 2 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_valdo_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
108def get_valdo_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
109    """Get paths to the VALDO Task 2 data.
110
111    Args:
112        path: Filepath to a folder where the data is downloaded for further processing.
113        download: Whether to download the data if it is not present.
114
115    Returns:
116        List of filepaths for the hdf5 files, which contain the image data ('raw/<modality>')
117        and the label data ('labels').
118    """
119    data_dir = get_valdo_data(path, download)
120    volume_paths = natsorted(glob(os.path.join(data_dir, "sub-*.h5")))
121    assert len(volume_paths) > 0, f"Could not find any preprocessed volumes in '{data_dir}'."
122    return volume_paths

Get paths to the VALDO Task 2 data.

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:

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

def get_valdo_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, ...], modality: Literal['t2s', 't2', 't1'] = 't2s', resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
125def get_valdo_dataset(
126    path: Union[os.PathLike, str],
127    patch_shape: Tuple[int, ...],
128    modality: Literal["t2s", "t2", "t1"] = "t2s",
129    resize_inputs: bool = False,
130    download: bool = False,
131    **kwargs
132) -> Dataset:
133    """Get the VALDO Task 2 dataset for cerebral microbleed 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        modality: The MRI sequence. Either 't2s', 't2' or 't1'.
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    if modality not in MODALITIES:
147        raise ValueError(f"'{modality}' is not a valid modality. Please choose one of {list(MODALITIES.keys())}.")
148
149    volume_paths = get_valdo_paths(path, download)
150
151    if resize_inputs:
152        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
153        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
154            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
155        )
156
157    return torch_em.default_segmentation_dataset(
158        raw_paths=volume_paths,
159        raw_key=f"raw/{modality}",
160        label_paths=volume_paths,
161        label_key="labels",
162        patch_shape=patch_shape,
163        is_seg_dataset=True,
164        **kwargs
165    )

Get the VALDO Task 2 dataset for cerebral microbleed segmentation.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • patch_shape: The patch shape to use for training.
  • modality: The MRI sequence. Either 't2s', 't2' or 't1'.
  • 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_valdo_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, ...], modality: Literal['t2s', 't2', 't1'] = 't2s', resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
168def get_valdo_loader(
169    path: Union[os.PathLike, str],
170    batch_size: int,
171    patch_shape: Tuple[int, ...],
172    modality: Literal["t2s", "t2", "t1"] = "t2s",
173    resize_inputs: bool = False,
174    download: bool = False,
175    **kwargs
176) -> DataLoader:
177    """Get the VALDO Task 2 dataloader for cerebral microbleed segmentation.
178
179    Args:
180        path: Filepath to a folder where the data is downloaded for further processing.
181        batch_size: The batch size for training.
182        patch_shape: The patch shape to use for training.
183        modality: The MRI sequence. Either 't2s', 't2' or 't1'.
184        resize_inputs: Whether to resize inputs to the desired patch shape.
185        download: Whether to download the data if it is not present.
186        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
187
188    Returns:
189        The DataLoader.
190    """
191    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
192    dataset = get_valdo_dataset(path, patch_shape, modality, resize_inputs, download, **ds_kwargs)
193    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the VALDO Task 2 dataloader for cerebral microbleed 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.
  • modality: The MRI sequence. Either 't2s', 't2' or 't1'.
  • 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.