torch_em.data.datasets.medical.totalsegmentator

The TotalSegmentator dataset contains annotations for 117 anatomical structures in CT scans.

The dataset (v2.0.1) consists of 1228 CT volumes with an official train / val / test split (see 'meta.csv'). Each anatomical structure is provided as a separate binary mask. get_totalsegmentator_data merges these masks into a single semantic label volume per case, where the label id of each structure is its (1-based) position in CLASS_NAMES (see CLASS_IDS for the name -> id mapping). This is the class order of the 'total' task in the TotalSegmentator repository (https://github.com/wasserth/TotalSegmentator). The masks of a few structures may overlap, in this case the structure with the higher label id takes precedence.

The dataset is located at https://doi.org/10.5281/zenodo.10047292.

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

  1"""The TotalSegmentator dataset contains annotations for 117 anatomical structures in CT scans.
  2
  3The dataset (v2.0.1) consists of 1228 CT volumes with an official train / val / test split (see 'meta.csv').
  4Each anatomical structure is provided as a separate binary mask. `get_totalsegmentator_data` merges these masks
  5into a single semantic label volume per case, where the label id of each structure is its (1-based) position in
  6`CLASS_NAMES` (see `CLASS_IDS` for the name -> id mapping). This is the class order of the 'total' task in the
  7TotalSegmentator repository (https://github.com/wasserth/TotalSegmentator). The masks of a few structures may
  8overlap, in this case the structure with the higher label id takes precedence.
  9
 10The dataset is located at https://doi.org/10.5281/zenodo.10047292.
 11
 12This dataset is from the publication https://doi.org/10.1148/ryai.230024.
 13Please cite it if you use this dataset in your research.
 14"""
 15
 16import os
 17from glob import glob
 18from concurrent import futures
 19from typing import Union, Tuple, Literal, List, Optional
 20
 21import numpy as np
 22from tqdm import tqdm
 23
 24from torch.utils.data import Dataset, DataLoader
 25
 26import torch_em
 27
 28from .. import util
 29
 30
 31URL = "https://zenodo.org/records/10047292/files/Totalsegmentator_dataset_v201.zip"
 32CHECKSUM = "741dbc911a768e2ac2671c66d55332f7302ad624c915a57d08b142d8bdf0ca26"
 33
 34CLASS_NAMES = [
 35    "spleen", "kidney_right", "kidney_left", "gallbladder", "liver", "stomach", "pancreas", "adrenal_gland_right",
 36    "adrenal_gland_left", "lung_upper_lobe_left", "lung_lower_lobe_left", "lung_upper_lobe_right",
 37    "lung_middle_lobe_right", "lung_lower_lobe_right", "esophagus", "trachea", "thyroid_gland", "small_bowel",
 38    "duodenum", "colon", "urinary_bladder", "prostate", "kidney_cyst_left", "kidney_cyst_right", "sacrum",
 39    "vertebrae_S1", "vertebrae_L5", "vertebrae_L4", "vertebrae_L3", "vertebrae_L2", "vertebrae_L1", "vertebrae_T12",
 40    "vertebrae_T11", "vertebrae_T10", "vertebrae_T9", "vertebrae_T8", "vertebrae_T7", "vertebrae_T6", "vertebrae_T5",
 41    "vertebrae_T4", "vertebrae_T3", "vertebrae_T2", "vertebrae_T1", "vertebrae_C7", "vertebrae_C6", "vertebrae_C5",
 42    "vertebrae_C4", "vertebrae_C3", "vertebrae_C2", "vertebrae_C1", "heart", "aorta", "pulmonary_vein",
 43    "brachiocephalic_trunk", "subclavian_artery_right", "subclavian_artery_left", "common_carotid_artery_right",
 44    "common_carotid_artery_left", "brachiocephalic_vein_left", "brachiocephalic_vein_right", "atrial_appendage_left",
 45    "superior_vena_cava", "inferior_vena_cava", "portal_vein_and_splenic_vein", "iliac_artery_left",
 46    "iliac_artery_right", "iliac_vena_left", "iliac_vena_right", "humerus_left", "humerus_right", "scapula_left",
 47    "scapula_right", "clavicula_left", "clavicula_right", "femur_left", "femur_right", "hip_left", "hip_right",
 48    "spinal_cord", "gluteus_maximus_left", "gluteus_maximus_right", "gluteus_medius_left", "gluteus_medius_right",
 49    "gluteus_minimus_left", "gluteus_minimus_right", "autochthon_left", "autochthon_right", "iliopsoas_left",
 50    "iliopsoas_right", "brain", "skull", "rib_left_1", "rib_left_2", "rib_left_3", "rib_left_4", "rib_left_5",
 51    "rib_left_6", "rib_left_7", "rib_left_8", "rib_left_9", "rib_left_10", "rib_left_11", "rib_left_12", "rib_right_1",
 52    "rib_right_2", "rib_right_3", "rib_right_4", "rib_right_5", "rib_right_6", "rib_right_7", "rib_right_8",
 53    "rib_right_9", "rib_right_10", "rib_right_11", "rib_right_12", "sternum", "costal_cartilages",
 54]
 55"""The anatomical structures of the TotalSegmentator CT dataset. The label id of a structure is its 1-based index."""
 56
 57CLASS_IDS = {name: i + 1 for i, name in enumerate(CLASS_NAMES)}
 58"""Mapping from the name of an anatomical structure to its label id in the merged label volumes."""
 59
 60
 61def merge_segmentations(case_dir: str, class_names: List[str], label_name: str = "labels.nii.gz") -> str:
 62    """Merge the per-class binary masks of one TotalSegmentator case into a single semantic label volume.
 63
 64    The merged volume is stored as nifti next to the image. If it already exists it is not recomputed,
 65    so that a partially finished conversion can be resumed.
 66
 67    Args:
 68        case_dir: The folder of the case, which contains the 'segmentations' sub-folder.
 69        class_names: The class names in label id order (the first class gets id 1).
 70        label_name: The filename of the merged label volume.
 71
 72    Returns:
 73        The filepath to the merged label volume.
 74    """
 75    import nibabel as nib
 76
 77    label_path = os.path.join(case_dir, label_name)
 78    if os.path.exists(label_path):
 79        return label_path
 80
 81    labels, affine, header = None, None, None
 82    for class_id, class_name in enumerate(class_names, start=1):
 83        mask_nii = nib.load(os.path.join(case_dir, "segmentations", f"{class_name}.nii.gz"))
 84        mask = np.asarray(mask_nii.dataobj) > 0
 85        if labels is None:
 86            labels = np.zeros(mask.shape, dtype="uint8")
 87            affine, header = mask_nii.affine, mask_nii.header
 88        labels[mask] = class_id
 89
 90    # Write to a temporary path first, so that an interrupted conversion is not mistaken for a complete one.
 91    tmp_path = os.path.join(case_dir, f"{label_name}.incomplete.nii.gz")
 92    nib.save(nib.Nifti1Image(labels, affine, header), tmp_path)
 93    os.replace(tmp_path, label_path)
 94    return label_path
 95
 96
 97def merge_all_segmentations(case_dirs: List[str], class_names: List[str], n_workers: Optional[int] = None) -> None:
 98    """Merge the per-class binary masks of all cases into semantic label volumes.
 99
100    Args:
101        case_dirs: The case folders to process.
102        class_names: The class names in label id order.
103        n_workers: The number of parallel workers. By default the number of CPUs (at most 16) is used.
104    """
105    if all(os.path.exists(os.path.join(case_dir, "labels.nii.gz")) for case_dir in case_dirs):
106        return
107
108    if n_workers is None:
109        n_cpus = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else (os.cpu_count() or 1)
110        n_workers = min(16, n_cpus)
111
112    with futures.ProcessPoolExecutor(n_workers) as pool:
113        tasks = [pool.submit(merge_segmentations, case_dir, class_names) for case_dir in case_dirs]
114        for task in tqdm(futures.as_completed(tasks), total=len(tasks), desc="Merge the per-class segmentations"):
115            task.result()
116
117
118def read_split(meta_csv: str, split: str, valid_splits: Tuple[str, ...] = ("train", "val", "test")) -> List[str]:
119    """Read the case ids of a split from the TotalSegmentator 'meta.csv'.
120
121    Args:
122        meta_csv: The path to the 'meta.csv' file.
123        split: The choice of data split.
124        valid_splits: The splits available in this dataset.
125
126    Returns:
127        The case ids of the split.
128    """
129    import pandas as pd
130
131    if split not in valid_splits:
132        raise ValueError(f"'{split}' is not a valid split. Choose one of {valid_splits}.")
133
134    meta = pd.read_csv(meta_csv, sep=";", encoding="utf-8-sig")
135    return sorted(meta[meta["split"] == split]["image_id"].tolist())
136
137
138def get_totalsegmentator_data(
139    path: Union[os.PathLike, str], download: bool = False, n_workers: Optional[int] = None
140) -> str:
141    """Download the TotalSegmentator CT dataset and merge the per-class masks into semantic label volumes.
142
143    Args:
144        path: Filepath to a folder where the data is downloaded for further processing.
145        download: Whether to download the data if it is not present.
146        n_workers: The number of parallel workers for merging the per-class masks.
147
148    Returns:
149        Filepath where the data is downloaded.
150    """
151    data_dir = os.path.join(path, "Totalsegmentator_dataset_v201")
152    if not os.path.exists(os.path.join(data_dir, "meta.csv")):
153        os.makedirs(path, exist_ok=True)
154        zip_path = os.path.join(path, "Totalsegmentator_dataset_v201.zip")
155        util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM)
156        # The archive has no top-level folder, hence we extract it into the data folder.
157        util.unzip(zip_path=zip_path, dst=data_dir)
158
159    case_dirs = sorted(glob(os.path.join(data_dir, "s*")))
160    merge_all_segmentations(case_dirs, CLASS_NAMES, n_workers)
161
162    return data_dir
163
164
165def get_totalsegmentator_paths(
166    path: Union[os.PathLike, str], split: Literal['train', 'val', 'test'], download: bool = False
167) -> Tuple[List[str], List[str]]:
168    """Get paths to the TotalSegmentator CT data.
169
170    Args:
171        path: Filepath to a folder where the data is downloaded for further processing.
172        split: The choice of data split.
173        download: Whether to download the data if it is not present.
174
175    Returns:
176        List of filepaths for the image data.
177        List of filepaths for the label data.
178    """
179    data_dir = get_totalsegmentator_data(path, download)
180    case_ids = read_split(os.path.join(data_dir, "meta.csv"), split)
181
182    raw_paths = [os.path.join(data_dir, case_id, "ct.nii.gz") for case_id in case_ids]
183    label_paths = [os.path.join(data_dir, case_id, "labels.nii.gz") for case_id in case_ids]
184    assert all(os.path.exists(p) for p in raw_paths + label_paths)
185
186    return raw_paths, label_paths
187
188
189def get_totalsegmentator_dataset(
190    path: Union[os.PathLike, str],
191    patch_shape: Tuple[int, ...],
192    split: Literal['train', 'val', 'test'],
193    resize_inputs: bool = False,
194    download: bool = False,
195    **kwargs
196) -> Dataset:
197    """Get the TotalSegmentator dataset for segmentation of anatomical structures in CT.
198
199    Args:
200        path: Filepath to a folder where the data is downloaded for further processing.
201        patch_shape: The patch shape to use for training.
202        split: The choice of data split.
203        resize_inputs: Whether to resize inputs to the desired patch shape.
204        download: Whether to download the data if it is not present.
205        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
206
207    Returns:
208        The segmentation dataset.
209    """
210    raw_paths, label_paths = get_totalsegmentator_paths(path, split, download)
211
212    if resize_inputs:
213        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
214        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
215            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
216        )
217
218    return torch_em.default_segmentation_dataset(
219        raw_paths=raw_paths,
220        raw_key="data",
221        label_paths=label_paths,
222        label_key="data",
223        patch_shape=patch_shape,
224        is_seg_dataset=True,
225        **kwargs
226    )
227
228
229def get_totalsegmentator_loader(
230    path: Union[os.PathLike, str],
231    batch_size: int,
232    patch_shape: Tuple[int, ...],
233    split: Literal['train', 'val', 'test'],
234    resize_inputs: bool = False,
235    download: bool = False,
236    **kwargs
237) -> DataLoader:
238    """Get the TotalSegmentator dataloader for segmentation of anatomical structures in CT.
239
240    Args:
241        path: Filepath to a folder where the data is downloaded for further processing.
242        batch_size: The batch size for training.
243        patch_shape: The patch shape to use for training.
244        split: The choice of data split.
245        resize_inputs: Whether to resize inputs to the desired patch shape.
246        download: Whether to download the data if it is not present.
247        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
248
249    Returns:
250        The DataLoader.
251    """
252    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
253    dataset = get_totalsegmentator_dataset(path, patch_shape, split, resize_inputs, download, **ds_kwargs)
254    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URL = 'https://zenodo.org/records/10047292/files/Totalsegmentator_dataset_v201.zip'
CHECKSUM = '741dbc911a768e2ac2671c66d55332f7302ad624c915a57d08b142d8bdf0ca26'
CLASS_NAMES = ['spleen', 'kidney_right', 'kidney_left', 'gallbladder', 'liver', 'stomach', 'pancreas', 'adrenal_gland_right', 'adrenal_gland_left', 'lung_upper_lobe_left', 'lung_lower_lobe_left', 'lung_upper_lobe_right', 'lung_middle_lobe_right', 'lung_lower_lobe_right', 'esophagus', 'trachea', 'thyroid_gland', 'small_bowel', 'duodenum', 'colon', 'urinary_bladder', 'prostate', 'kidney_cyst_left', 'kidney_cyst_right', 'sacrum', 'vertebrae_S1', 'vertebrae_L5', 'vertebrae_L4', 'vertebrae_L3', 'vertebrae_L2', 'vertebrae_L1', 'vertebrae_T12', 'vertebrae_T11', 'vertebrae_T10', 'vertebrae_T9', 'vertebrae_T8', 'vertebrae_T7', 'vertebrae_T6', 'vertebrae_T5', 'vertebrae_T4', 'vertebrae_T3', 'vertebrae_T2', 'vertebrae_T1', 'vertebrae_C7', 'vertebrae_C6', 'vertebrae_C5', 'vertebrae_C4', 'vertebrae_C3', 'vertebrae_C2', 'vertebrae_C1', 'heart', 'aorta', 'pulmonary_vein', 'brachiocephalic_trunk', 'subclavian_artery_right', 'subclavian_artery_left', 'common_carotid_artery_right', 'common_carotid_artery_left', 'brachiocephalic_vein_left', 'brachiocephalic_vein_right', 'atrial_appendage_left', 'superior_vena_cava', 'inferior_vena_cava', 'portal_vein_and_splenic_vein', 'iliac_artery_left', 'iliac_artery_right', 'iliac_vena_left', 'iliac_vena_right', 'humerus_left', 'humerus_right', 'scapula_left', 'scapula_right', 'clavicula_left', 'clavicula_right', 'femur_left', 'femur_right', 'hip_left', 'hip_right', 'spinal_cord', 'gluteus_maximus_left', 'gluteus_maximus_right', 'gluteus_medius_left', 'gluteus_medius_right', 'gluteus_minimus_left', 'gluteus_minimus_right', 'autochthon_left', 'autochthon_right', 'iliopsoas_left', 'iliopsoas_right', 'brain', 'skull', 'rib_left_1', 'rib_left_2', 'rib_left_3', 'rib_left_4', 'rib_left_5', 'rib_left_6', 'rib_left_7', 'rib_left_8', 'rib_left_9', 'rib_left_10', 'rib_left_11', 'rib_left_12', 'rib_right_1', 'rib_right_2', 'rib_right_3', 'rib_right_4', 'rib_right_5', 'rib_right_6', 'rib_right_7', 'rib_right_8', 'rib_right_9', 'rib_right_10', 'rib_right_11', 'rib_right_12', 'sternum', 'costal_cartilages']

The anatomical structures of the TotalSegmentator CT dataset. The label id of a structure is its 1-based index.

CLASS_IDS = {'spleen': 1, 'kidney_right': 2, 'kidney_left': 3, 'gallbladder': 4, 'liver': 5, 'stomach': 6, 'pancreas': 7, 'adrenal_gland_right': 8, 'adrenal_gland_left': 9, 'lung_upper_lobe_left': 10, 'lung_lower_lobe_left': 11, 'lung_upper_lobe_right': 12, 'lung_middle_lobe_right': 13, 'lung_lower_lobe_right': 14, 'esophagus': 15, 'trachea': 16, 'thyroid_gland': 17, 'small_bowel': 18, 'duodenum': 19, 'colon': 20, 'urinary_bladder': 21, 'prostate': 22, 'kidney_cyst_left': 23, 'kidney_cyst_right': 24, 'sacrum': 25, 'vertebrae_S1': 26, 'vertebrae_L5': 27, 'vertebrae_L4': 28, 'vertebrae_L3': 29, 'vertebrae_L2': 30, 'vertebrae_L1': 31, 'vertebrae_T12': 32, 'vertebrae_T11': 33, 'vertebrae_T10': 34, 'vertebrae_T9': 35, 'vertebrae_T8': 36, 'vertebrae_T7': 37, 'vertebrae_T6': 38, 'vertebrae_T5': 39, 'vertebrae_T4': 40, 'vertebrae_T3': 41, 'vertebrae_T2': 42, 'vertebrae_T1': 43, 'vertebrae_C7': 44, 'vertebrae_C6': 45, 'vertebrae_C5': 46, 'vertebrae_C4': 47, 'vertebrae_C3': 48, 'vertebrae_C2': 49, 'vertebrae_C1': 50, 'heart': 51, 'aorta': 52, 'pulmonary_vein': 53, 'brachiocephalic_trunk': 54, 'subclavian_artery_right': 55, 'subclavian_artery_left': 56, 'common_carotid_artery_right': 57, 'common_carotid_artery_left': 58, 'brachiocephalic_vein_left': 59, 'brachiocephalic_vein_right': 60, 'atrial_appendage_left': 61, 'superior_vena_cava': 62, 'inferior_vena_cava': 63, 'portal_vein_and_splenic_vein': 64, 'iliac_artery_left': 65, 'iliac_artery_right': 66, 'iliac_vena_left': 67, 'iliac_vena_right': 68, 'humerus_left': 69, 'humerus_right': 70, 'scapula_left': 71, 'scapula_right': 72, 'clavicula_left': 73, 'clavicula_right': 74, 'femur_left': 75, 'femur_right': 76, 'hip_left': 77, 'hip_right': 78, 'spinal_cord': 79, 'gluteus_maximus_left': 80, 'gluteus_maximus_right': 81, 'gluteus_medius_left': 82, 'gluteus_medius_right': 83, 'gluteus_minimus_left': 84, 'gluteus_minimus_right': 85, 'autochthon_left': 86, 'autochthon_right': 87, 'iliopsoas_left': 88, 'iliopsoas_right': 89, 'brain': 90, 'skull': 91, 'rib_left_1': 92, 'rib_left_2': 93, 'rib_left_3': 94, 'rib_left_4': 95, 'rib_left_5': 96, 'rib_left_6': 97, 'rib_left_7': 98, 'rib_left_8': 99, 'rib_left_9': 100, 'rib_left_10': 101, 'rib_left_11': 102, 'rib_left_12': 103, 'rib_right_1': 104, 'rib_right_2': 105, 'rib_right_3': 106, 'rib_right_4': 107, 'rib_right_5': 108, 'rib_right_6': 109, 'rib_right_7': 110, 'rib_right_8': 111, 'rib_right_9': 112, 'rib_right_10': 113, 'rib_right_11': 114, 'rib_right_12': 115, 'sternum': 116, 'costal_cartilages': 117}

Mapping from the name of an anatomical structure to its label id in the merged label volumes.

def merge_segmentations( case_dir: str, class_names: List[str], label_name: str = 'labels.nii.gz') -> str:
62def merge_segmentations(case_dir: str, class_names: List[str], label_name: str = "labels.nii.gz") -> str:
63    """Merge the per-class binary masks of one TotalSegmentator case into a single semantic label volume.
64
65    The merged volume is stored as nifti next to the image. If it already exists it is not recomputed,
66    so that a partially finished conversion can be resumed.
67
68    Args:
69        case_dir: The folder of the case, which contains the 'segmentations' sub-folder.
70        class_names: The class names in label id order (the first class gets id 1).
71        label_name: The filename of the merged label volume.
72
73    Returns:
74        The filepath to the merged label volume.
75    """
76    import nibabel as nib
77
78    label_path = os.path.join(case_dir, label_name)
79    if os.path.exists(label_path):
80        return label_path
81
82    labels, affine, header = None, None, None
83    for class_id, class_name in enumerate(class_names, start=1):
84        mask_nii = nib.load(os.path.join(case_dir, "segmentations", f"{class_name}.nii.gz"))
85        mask = np.asarray(mask_nii.dataobj) > 0
86        if labels is None:
87            labels = np.zeros(mask.shape, dtype="uint8")
88            affine, header = mask_nii.affine, mask_nii.header
89        labels[mask] = class_id
90
91    # Write to a temporary path first, so that an interrupted conversion is not mistaken for a complete one.
92    tmp_path = os.path.join(case_dir, f"{label_name}.incomplete.nii.gz")
93    nib.save(nib.Nifti1Image(labels, affine, header), tmp_path)
94    os.replace(tmp_path, label_path)
95    return label_path

Merge the per-class binary masks of one TotalSegmentator case into a single semantic label volume.

The merged volume is stored as nifti next to the image. If it already exists it is not recomputed, so that a partially finished conversion can be resumed.

Arguments:
  • case_dir: The folder of the case, which contains the 'segmentations' sub-folder.
  • class_names: The class names in label id order (the first class gets id 1).
  • label_name: The filename of the merged label volume.
Returns:

The filepath to the merged label volume.

def merge_all_segmentations( case_dirs: List[str], class_names: List[str], n_workers: Optional[int] = None) -> None:
 98def merge_all_segmentations(case_dirs: List[str], class_names: List[str], n_workers: Optional[int] = None) -> None:
 99    """Merge the per-class binary masks of all cases into semantic label volumes.
100
101    Args:
102        case_dirs: The case folders to process.
103        class_names: The class names in label id order.
104        n_workers: The number of parallel workers. By default the number of CPUs (at most 16) is used.
105    """
106    if all(os.path.exists(os.path.join(case_dir, "labels.nii.gz")) for case_dir in case_dirs):
107        return
108
109    if n_workers is None:
110        n_cpus = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else (os.cpu_count() or 1)
111        n_workers = min(16, n_cpus)
112
113    with futures.ProcessPoolExecutor(n_workers) as pool:
114        tasks = [pool.submit(merge_segmentations, case_dir, class_names) for case_dir in case_dirs]
115        for task in tqdm(futures.as_completed(tasks), total=len(tasks), desc="Merge the per-class segmentations"):
116            task.result()

Merge the per-class binary masks of all cases into semantic label volumes.

Arguments:
  • case_dirs: The case folders to process.
  • class_names: The class names in label id order.
  • n_workers: The number of parallel workers. By default the number of CPUs (at most 16) is used.
def read_split( meta_csv: str, split: str, valid_splits: Tuple[str, ...] = ('train', 'val', 'test')) -> List[str]:
119def read_split(meta_csv: str, split: str, valid_splits: Tuple[str, ...] = ("train", "val", "test")) -> List[str]:
120    """Read the case ids of a split from the TotalSegmentator 'meta.csv'.
121
122    Args:
123        meta_csv: The path to the 'meta.csv' file.
124        split: The choice of data split.
125        valid_splits: The splits available in this dataset.
126
127    Returns:
128        The case ids of the split.
129    """
130    import pandas as pd
131
132    if split not in valid_splits:
133        raise ValueError(f"'{split}' is not a valid split. Choose one of {valid_splits}.")
134
135    meta = pd.read_csv(meta_csv, sep=";", encoding="utf-8-sig")
136    return sorted(meta[meta["split"] == split]["image_id"].tolist())

Read the case ids of a split from the TotalSegmentator 'meta.csv'.

Arguments:
  • meta_csv: The path to the 'meta.csv' file.
  • split: The choice of data split.
  • valid_splits: The splits available in this dataset.
Returns:

The case ids of the split.

def get_totalsegmentator_data( path: Union[os.PathLike, str], download: bool = False, n_workers: Optional[int] = None) -> str:
139def get_totalsegmentator_data(
140    path: Union[os.PathLike, str], download: bool = False, n_workers: Optional[int] = None
141) -> str:
142    """Download the TotalSegmentator CT dataset and merge the per-class masks into semantic label volumes.
143
144    Args:
145        path: Filepath to a folder where the data is downloaded for further processing.
146        download: Whether to download the data if it is not present.
147        n_workers: The number of parallel workers for merging the per-class masks.
148
149    Returns:
150        Filepath where the data is downloaded.
151    """
152    data_dir = os.path.join(path, "Totalsegmentator_dataset_v201")
153    if not os.path.exists(os.path.join(data_dir, "meta.csv")):
154        os.makedirs(path, exist_ok=True)
155        zip_path = os.path.join(path, "Totalsegmentator_dataset_v201.zip")
156        util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM)
157        # The archive has no top-level folder, hence we extract it into the data folder.
158        util.unzip(zip_path=zip_path, dst=data_dir)
159
160    case_dirs = sorted(glob(os.path.join(data_dir, "s*")))
161    merge_all_segmentations(case_dirs, CLASS_NAMES, n_workers)
162
163    return data_dir

Download the TotalSegmentator CT dataset and merge the per-class masks into semantic label volumes.

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.
  • n_workers: The number of parallel workers for merging the per-class masks.
Returns:

Filepath where the data is downloaded.

def get_totalsegmentator_paths( path: Union[os.PathLike, str], split: Literal['train', 'val', 'test'], download: bool = False) -> Tuple[List[str], List[str]]:
166def get_totalsegmentator_paths(
167    path: Union[os.PathLike, str], split: Literal['train', 'val', 'test'], download: bool = False
168) -> Tuple[List[str], List[str]]:
169    """Get paths to the TotalSegmentator CT data.
170
171    Args:
172        path: Filepath to a folder where the data is downloaded for further processing.
173        split: The choice of data split.
174        download: Whether to download the data if it is not present.
175
176    Returns:
177        List of filepaths for the image data.
178        List of filepaths for the label data.
179    """
180    data_dir = get_totalsegmentator_data(path, download)
181    case_ids = read_split(os.path.join(data_dir, "meta.csv"), split)
182
183    raw_paths = [os.path.join(data_dir, case_id, "ct.nii.gz") for case_id in case_ids]
184    label_paths = [os.path.join(data_dir, case_id, "labels.nii.gz") for case_id in case_ids]
185    assert all(os.path.exists(p) for p in raw_paths + label_paths)
186
187    return raw_paths, label_paths

Get paths to the TotalSegmentator CT data.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • split: The choice of data split.
  • 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_totalsegmentator_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:
190def get_totalsegmentator_dataset(
191    path: Union[os.PathLike, str],
192    patch_shape: Tuple[int, ...],
193    split: Literal['train', 'val', 'test'],
194    resize_inputs: bool = False,
195    download: bool = False,
196    **kwargs
197) -> Dataset:
198    """Get the TotalSegmentator dataset for segmentation of anatomical structures in CT.
199
200    Args:
201        path: Filepath to a folder where the data is downloaded for further processing.
202        patch_shape: The patch shape to use for training.
203        split: The choice of data split.
204        resize_inputs: Whether to resize inputs to the desired patch shape.
205        download: Whether to download the data if it is not present.
206        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
207
208    Returns:
209        The segmentation dataset.
210    """
211    raw_paths, label_paths = get_totalsegmentator_paths(path, split, download)
212
213    if resize_inputs:
214        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
215        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
216            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
217        )
218
219    return torch_em.default_segmentation_dataset(
220        raw_paths=raw_paths,
221        raw_key="data",
222        label_paths=label_paths,
223        label_key="data",
224        patch_shape=patch_shape,
225        is_seg_dataset=True,
226        **kwargs
227    )

Get the TotalSegmentator dataset for segmentation of anatomical structures in CT.

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.
  • 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_totalsegmentator_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:
230def get_totalsegmentator_loader(
231    path: Union[os.PathLike, str],
232    batch_size: int,
233    patch_shape: Tuple[int, ...],
234    split: Literal['train', 'val', 'test'],
235    resize_inputs: bool = False,
236    download: bool = False,
237    **kwargs
238) -> DataLoader:
239    """Get the TotalSegmentator dataloader for segmentation of anatomical structures in CT.
240
241    Args:
242        path: Filepath to a folder where the data is downloaded for further processing.
243        batch_size: The batch size for training.
244        patch_shape: The patch shape to use for training.
245        split: The choice of data split.
246        resize_inputs: Whether to resize inputs to the desired patch shape.
247        download: Whether to download the data if it is not present.
248        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
249
250    Returns:
251        The DataLoader.
252    """
253    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
254    dataset = get_totalsegmentator_dataset(path, patch_shape, split, resize_inputs, download, **ds_kwargs)
255    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the TotalSegmentator dataloader for segmentation of anatomical structures in CT.

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.
  • 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.