torch_em.data.datasets.medical.word

The WORD dataset contains annotations for 16 abdominal organs in CT scans.

The dataset consists of 150 abdominal CT volumes with an official split into 100 training, 20 validation and 30 test volumes. The label ids are: 1: liver, 2: spleen, 3: kidney (left), 4: kidney (right), 5: stomach, 6: gallbladder, 7: esophagus, 8: pancreas, 9: duodenum, 10: colon, 11: intestine, 12: adrenal, 13: rectum, 14: bladder, 15: head of femur (left), 16: head of femur (right). See also CLASS_IDS.

The dataset is located at https://github.com/HiLab-git/WORD. It is distributed as a password protected zip archive via Google Drive (the password 'word@uestc' is given in the repository).

NOTE: The Google Drive download is quota limited, so it intermittently fails with 'Too many users have viewed or downloaded this file recently'. Retrying later usually works. Otherwise download 'WORD-V0.1.0.zip' manually from https://drive.google.com/drive/folders/16qwlCxH7XtJD9MyPnAbmY4ATxu2mKu67 and place it in path.

This dataset is from the publication https://doi.org/10.1016/j.media.2022.102642. Please cite it if you use this dataset in your research.

  1"""The WORD dataset contains annotations for 16 abdominal organs in CT scans.
  2
  3The dataset consists of 150 abdominal CT volumes with an official split into 100 training, 20 validation
  4and 30 test volumes. The label ids are: 1: liver, 2: spleen, 3: kidney (left), 4: kidney (right), 5: stomach,
  56: gallbladder, 7: esophagus, 8: pancreas, 9: duodenum, 10: colon, 11: intestine, 12: adrenal, 13: rectum,
  614: bladder, 15: head of femur (left), 16: head of femur (right). See also `CLASS_IDS`.
  7
  8The dataset is located at https://github.com/HiLab-git/WORD. It is distributed as a password protected zip archive
  9via Google Drive (the password 'word@uestc' is given in the repository).
 10
 11NOTE: The Google Drive download is quota limited, so it intermittently fails with
 12'Too many users have viewed or downloaded this file recently'. Retrying later usually works. Otherwise download
 13'WORD-V0.1.0.zip' manually from https://drive.google.com/drive/folders/16qwlCxH7XtJD9MyPnAbmY4ATxu2mKu67
 14and place it in `path`.
 15
 16This dataset is from the publication https://doi.org/10.1016/j.media.2022.102642.
 17Please cite it if you use this dataset in your research.
 18"""
 19
 20import os
 21import zipfile
 22from glob import glob
 23from shutil import which
 24from subprocess import run
 25from natsort import natsorted
 26from typing import Union, Tuple, Literal, List
 27
 28from torch.utils.data import Dataset, DataLoader
 29
 30import torch_em
 31
 32from .. import util
 33
 34
 35URLS = {
 36    "data": "https://drive.google.com/uc?id=19OWCXZGrimafREhXm8O8w2HBHZTfxEgU",
 37    "test_labels": "https://github.com/HiLab-git/WORD/raw/main/WORD_V0.1.0_labelsTs.zip",
 38}
 39
 40CHECKSUMS = {
 41    "data": "1ef1e48f8b41d72d733c5cdd91e1238d9b345d88c0de0c902e356310901f3115",
 42    "test_labels": "0552b1e208a8a5345a1c12003d4f1d010621c6ef65bdd62bff85a32b2976a46d",
 43}
 44
 45PASSWORD = "word@uestc"
 46
 47CLASS_NAMES = [
 48    "liver", "spleen", "kidney_left", "kidney_right", "stomach", "gallbladder", "esophagus", "pancreas", "duodenum",
 49    "colon", "intestine", "adrenal", "rectum", "bladder", "head_of_femur_left", "head_of_femur_right",
 50]
 51"""The organs of the WORD dataset. The label id of an organ is its 1-based index."""
 52
 53CLASS_IDS = {name: i + 1 for i, name in enumerate(CLASS_NAMES)}
 54"""Mapping from the organ name to its label id."""
 55
 56SPLIT_DIRS = {"train": "Tr", "val": "Val", "test": "Ts"}
 57
 58
 59def _unzip_with_password(zip_path, dst, password):
 60    try:
 61        with zipfile.ZipFile(zip_path) as f:
 62            f.extractall(dst, pwd=password.encode())
 63    except (NotImplementedError, RuntimeError) as e:
 64        # The python zipfile module does not support AES encryption, so we fall back to the 7z CLI.
 65        if which("7z") is None:
 66            raise RuntimeError(
 67                f"Could not extract '{zip_path}' with the zipfile module ({e}). Please install the '7z' CLI "
 68                "('conda install -c conda-forge p7zip') or extract the archive manually."
 69            )
 70        run(["7z", "x", f"-o{dst}", f"-p{password}", "-y", zip_path], check=True)
 71
 72
 73def _find_data_dir(path):
 74    candidates = glob(os.path.join(path, "imagesTr")) + glob(os.path.join(path, "*", "imagesTr"))
 75    return os.path.dirname(candidates[0]) if candidates else None
 76
 77
 78def get_word_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 79    """Download the WORD dataset.
 80
 81    Args:
 82        path: Filepath to a folder where the data is downloaded for further processing.
 83        download: Whether to download the data if it is not present.
 84
 85    Returns:
 86        Filepath where the data is downloaded.
 87    """
 88    data_dir = _find_data_dir(path)
 89    if data_dir is None:
 90        os.makedirs(path, exist_ok=True)
 91        zip_path = os.path.join(path, "WORD-V0.1.0.zip")
 92        util.download_source_gdrive(path=zip_path, url=URLS["data"], download=download, checksum=CHECKSUMS["data"])
 93        if not os.path.exists(zip_path):
 94            raise RuntimeError(
 95                "The automatic download of the WORD dataset failed, most likely because the Google Drive download "
 96                "quota of the archive is exceeded. Please download 'WORD-V0.1.0.zip' manually from "
 97                f"https://drive.google.com/drive/folders/16qwlCxH7XtJD9MyPnAbmY4ATxu2mKu67 and place it at "
 98                f"'{zip_path}'."
 99            )
100        _unzip_with_password(zip_path, path, PASSWORD)
101        data_dir = _find_data_dir(path)
102        if data_dir is None:
103            raise RuntimeError(f"Could not find the 'imagesTr' folder of the WORD dataset in '{path}'.")
104
105    # The test labels were released separately via the GitHub repository.
106    if not os.path.exists(os.path.join(data_dir, "labelsTs")):
107        zip_path = os.path.join(path, "WORD_V0.1.0_labelsTs.zip")
108        util.download_source(
109            path=zip_path, url=URLS["test_labels"], download=download, checksum=CHECKSUMS["test_labels"]
110        )
111        util.unzip(zip_path=zip_path, dst=data_dir, remove=False)
112
113    return data_dir
114
115
116def get_word_paths(
117    path: Union[os.PathLike, str], split: Literal["train", "val", "test"], download: bool = False
118) -> Tuple[List[str], List[str]]:
119    """Get paths to the WORD data.
120
121    Args:
122        path: Filepath to a folder where the data is downloaded for further processing.
123        split: The choice of data split. Either 'train', 'val' or 'test'.
124        download: Whether to download the data if it is not present.
125
126    Returns:
127        List of filepaths for the image data.
128        List of filepaths for the label data.
129    """
130    if split not in SPLIT_DIRS:
131        raise ValueError(f"'{split}' is not a valid split. Choose from {list(SPLIT_DIRS.keys())}.")
132
133    data_dir = get_word_data(path, download)
134
135    raw_paths = natsorted(glob(os.path.join(data_dir, f"images{SPLIT_DIRS[split]}", "*.nii.gz")))
136    label_paths = [p.replace(f"images{SPLIT_DIRS[split]}", f"labels{SPLIT_DIRS[split]}") for p in raw_paths]
137
138    assert len(raw_paths) == len(label_paths) and len(raw_paths) > 0
139    assert all(os.path.exists(p) for p in label_paths)
140
141    return raw_paths, label_paths
142
143
144def get_word_dataset(
145    path: Union[os.PathLike, str],
146    patch_shape: Tuple[int, ...],
147    split: Literal["train", "val", "test"],
148    resize_inputs: bool = False,
149    download: bool = False,
150    **kwargs
151) -> Dataset:
152    """Get the WORD dataset for abdominal organ segmentation.
153
154    Args:
155        path: Filepath to a folder where the data is downloaded for further processing.
156        patch_shape: The patch shape to use for training.
157        split: The choice of data split. Either 'train', 'val' or 'test'.
158        resize_inputs: Whether to resize inputs to the desired patch shape.
159        download: Whether to download the data if it is not present.
160        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
161
162    Returns:
163        The segmentation dataset.
164    """
165    raw_paths, label_paths = get_word_paths(path, split, download)
166
167    if resize_inputs:
168        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
169        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
170            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
171        )
172
173    return torch_em.default_segmentation_dataset(
174        raw_paths=raw_paths,
175        raw_key="data",
176        label_paths=label_paths,
177        label_key="data",
178        patch_shape=patch_shape,
179        is_seg_dataset=True,
180        **kwargs
181    )
182
183
184def get_word_loader(
185    path: Union[os.PathLike, str],
186    batch_size: int,
187    patch_shape: Tuple[int, ...],
188    split: Literal["train", "val", "test"],
189    resize_inputs: bool = False,
190    download: bool = False,
191    **kwargs
192) -> DataLoader:
193    """Get the WORD dataloader for abdominal organ segmentation.
194
195    Args:
196        path: Filepath to a folder where the data is downloaded for further processing.
197        batch_size: The batch size for training.
198        patch_shape: The patch shape to use for training.
199        split: The choice of data split. Either 'train', 'val' or 'test'.
200        resize_inputs: Whether to resize inputs to the desired patch shape.
201        download: Whether to download the data if it is not present.
202        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
203
204    Returns:
205        The DataLoader.
206    """
207    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
208    dataset = get_word_dataset(path, patch_shape, split, resize_inputs, download, **ds_kwargs)
209    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URLS = {'data': 'https://drive.google.com/uc?id=19OWCXZGrimafREhXm8O8w2HBHZTfxEgU', 'test_labels': 'https://github.com/HiLab-git/WORD/raw/main/WORD_V0.1.0_labelsTs.zip'}
CHECKSUMS = {'data': '1ef1e48f8b41d72d733c5cdd91e1238d9b345d88c0de0c902e356310901f3115', 'test_labels': '0552b1e208a8a5345a1c12003d4f1d010621c6ef65bdd62bff85a32b2976a46d'}
PASSWORD = 'word@uestc'
CLASS_NAMES = ['liver', 'spleen', 'kidney_left', 'kidney_right', 'stomach', 'gallbladder', 'esophagus', 'pancreas', 'duodenum', 'colon', 'intestine', 'adrenal', 'rectum', 'bladder', 'head_of_femur_left', 'head_of_femur_right']

The organs of the WORD dataset. The label id of an organ is its 1-based index.

CLASS_IDS = {'liver': 1, 'spleen': 2, 'kidney_left': 3, 'kidney_right': 4, 'stomach': 5, 'gallbladder': 6, 'esophagus': 7, 'pancreas': 8, 'duodenum': 9, 'colon': 10, 'intestine': 11, 'adrenal': 12, 'rectum': 13, 'bladder': 14, 'head_of_femur_left': 15, 'head_of_femur_right': 16}

Mapping from the organ name to its label id.

SPLIT_DIRS = {'train': 'Tr', 'val': 'Val', 'test': 'Ts'}
def get_word_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 79def get_word_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 80    """Download the WORD dataset.
 81
 82    Args:
 83        path: Filepath to a folder where the data is downloaded for further processing.
 84        download: Whether to download the data if it is not present.
 85
 86    Returns:
 87        Filepath where the data is downloaded.
 88    """
 89    data_dir = _find_data_dir(path)
 90    if data_dir is None:
 91        os.makedirs(path, exist_ok=True)
 92        zip_path = os.path.join(path, "WORD-V0.1.0.zip")
 93        util.download_source_gdrive(path=zip_path, url=URLS["data"], download=download, checksum=CHECKSUMS["data"])
 94        if not os.path.exists(zip_path):
 95            raise RuntimeError(
 96                "The automatic download of the WORD dataset failed, most likely because the Google Drive download "
 97                "quota of the archive is exceeded. Please download 'WORD-V0.1.0.zip' manually from "
 98                f"https://drive.google.com/drive/folders/16qwlCxH7XtJD9MyPnAbmY4ATxu2mKu67 and place it at "
 99                f"'{zip_path}'."
100            )
101        _unzip_with_password(zip_path, path, PASSWORD)
102        data_dir = _find_data_dir(path)
103        if data_dir is None:
104            raise RuntimeError(f"Could not find the 'imagesTr' folder of the WORD dataset in '{path}'.")
105
106    # The test labels were released separately via the GitHub repository.
107    if not os.path.exists(os.path.join(data_dir, "labelsTs")):
108        zip_path = os.path.join(path, "WORD_V0.1.0_labelsTs.zip")
109        util.download_source(
110            path=zip_path, url=URLS["test_labels"], download=download, checksum=CHECKSUMS["test_labels"]
111        )
112        util.unzip(zip_path=zip_path, dst=data_dir, remove=False)
113
114    return data_dir

Download the WORD 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 data is downloaded.

def get_word_paths( path: Union[os.PathLike, str], split: Literal['train', 'val', 'test'], download: bool = False) -> Tuple[List[str], List[str]]:
117def get_word_paths(
118    path: Union[os.PathLike, str], split: Literal["train", "val", "test"], download: bool = False
119) -> Tuple[List[str], List[str]]:
120    """Get paths to the WORD data.
121
122    Args:
123        path: Filepath to a folder where the data is downloaded for further processing.
124        split: The choice of data split. Either 'train', 'val' or 'test'.
125        download: Whether to download the data if it is not present.
126
127    Returns:
128        List of filepaths for the image data.
129        List of filepaths for the label data.
130    """
131    if split not in SPLIT_DIRS:
132        raise ValueError(f"'{split}' is not a valid split. Choose from {list(SPLIT_DIRS.keys())}.")
133
134    data_dir = get_word_data(path, download)
135
136    raw_paths = natsorted(glob(os.path.join(data_dir, f"images{SPLIT_DIRS[split]}", "*.nii.gz")))
137    label_paths = [p.replace(f"images{SPLIT_DIRS[split]}", f"labels{SPLIT_DIRS[split]}") for p in raw_paths]
138
139    assert len(raw_paths) == len(label_paths) and len(raw_paths) > 0
140    assert all(os.path.exists(p) for p in label_paths)
141
142    return raw_paths, label_paths

Get paths to the WORD data.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • split: The choice of data split. Either 'train', 'val' or 'test'.
  • download: Whether to download the data if it is not present.
Returns:

List of filepaths for the image data. List of filepaths for the label data.

def get_word_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, ...], split: Literal['train', 'val', 'test'], resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
145def get_word_dataset(
146    path: Union[os.PathLike, str],
147    patch_shape: Tuple[int, ...],
148    split: Literal["train", "val", "test"],
149    resize_inputs: bool = False,
150    download: bool = False,
151    **kwargs
152) -> Dataset:
153    """Get the WORD dataset for abdominal organ segmentation.
154
155    Args:
156        path: Filepath to a folder where the data is downloaded for further processing.
157        patch_shape: The patch shape to use for training.
158        split: The choice of data split. Either 'train', 'val' or 'test'.
159        resize_inputs: Whether to resize inputs to the desired patch shape.
160        download: Whether to download the data if it is not present.
161        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
162
163    Returns:
164        The segmentation dataset.
165    """
166    raw_paths, label_paths = get_word_paths(path, split, download)
167
168    if resize_inputs:
169        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
170        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
171            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
172        )
173
174    return torch_em.default_segmentation_dataset(
175        raw_paths=raw_paths,
176        raw_key="data",
177        label_paths=label_paths,
178        label_key="data",
179        patch_shape=patch_shape,
180        is_seg_dataset=True,
181        **kwargs
182    )

Get the WORD dataset for abdominal organ segmentation.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • patch_shape: The patch shape to use for training.
  • split: The choice of data split. Either 'train', 'val' or 'test'.
  • 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_word_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, ...], split: Literal['train', 'val', 'test'], resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
185def get_word_loader(
186    path: Union[os.PathLike, str],
187    batch_size: int,
188    patch_shape: Tuple[int, ...],
189    split: Literal["train", "val", "test"],
190    resize_inputs: bool = False,
191    download: bool = False,
192    **kwargs
193) -> DataLoader:
194    """Get the WORD dataloader for abdominal organ segmentation.
195
196    Args:
197        path: Filepath to a folder where the data is downloaded for further processing.
198        batch_size: The batch size for training.
199        patch_shape: The patch shape to use for training.
200        split: The choice of data split. Either 'train', 'val' or 'test'.
201        resize_inputs: Whether to resize inputs to the desired patch shape.
202        download: Whether to download the data if it is not present.
203        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
204
205    Returns:
206        The DataLoader.
207    """
208    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
209    dataset = get_word_dataset(path, patch_shape, split, resize_inputs, download, **ds_kwargs)
210    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the WORD dataloader for abdominal organ 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.
  • split: The choice of data split. Either 'train', 'val' or 'test'.
  • 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.