torch_em.data.datasets.medical.nih_pancreas

The NIH Pancreas-CT dataset contains annotations for pancreas segmentation in contrast-enhanced abdominal CT.

It consists of 80 CT volumes (the original release had 82 volumes, cases 25 and 70 were removed by TCIA, as they are duplicates of case 2) with binary pancreas labels. The CT scans are distributed as DICOM series, which are stacked into volumes and stored together with the corresponding labels in hdf5 files by this module.

NOTE: This requires the pydicom python package.

The dataset is located at https://www.cancerimagingarchive.net/collection/pancreas-ct/.

This dataset is from the publication https://doi.org/10.1007/978-3-319-24553-9_68. The data was released at https://doi.org/10.7937/K9/TCIA.2016.tNB1kqBU. Please cite it if you use this dataset in your research.

  1"""The NIH Pancreas-CT dataset contains annotations for pancreas segmentation in contrast-enhanced abdominal CT.
  2
  3It consists of 80 CT volumes (the original release had 82 volumes, cases 25 and 70 were removed by TCIA,
  4as they are duplicates of case 2) with binary pancreas labels. The CT scans are distributed as DICOM series,
  5which are stacked into volumes and stored together with the corresponding labels in hdf5 files by this module.
  6
  7NOTE: This requires the pydicom python package.
  8
  9The dataset is located at https://www.cancerimagingarchive.net/collection/pancreas-ct/.
 10
 11This dataset is from the publication https://doi.org/10.1007/978-3-319-24553-9_68.
 12The data was released at https://doi.org/10.7937/K9/TCIA.2016.tNB1kqBU.
 13Please cite it if you use this dataset in your research.
 14"""
 15
 16import os
 17import csv
 18from glob import glob
 19from tqdm import tqdm
 20from natsort import natsorted
 21from typing import Union, Tuple, List
 22
 23import numpy as np
 24
 25from torch.utils.data import Dataset, DataLoader
 26
 27import torch_em
 28
 29from .. import util
 30
 31
 32URLS = {
 33    "images": "https://www.cancerimagingarchive.net/wp-content/uploads/Pancreas-CT-20200910.tcia",
 34    "labels": "https://www.cancerimagingarchive.net/wp-content/uploads/TCIA_pancreas_labels-02-05-2017-1.zip",
 35}
 36
 37CHECKSUMS = {
 38    "images": None,  # The DICOM series are downloaded individually from TCIA.
 39    "labels": "cf8a553c37c80e3840ce7392987b308f7a98cd5f50019511b2ac0f3a54b0934b",
 40}
 41
 42
 43def _load_dicom_volume(series_dir):
 44    """Stack a DICOM series into a volume with axes (z, y, x) and slices sorted by ascending patient z position.
 45
 46    Returns the volume in Hounsfield units and the image orientation (DICOM 'ImageOrientationPatient').
 47    """
 48    import pydicom
 49
 50    slices = [pydicom.dcmread(dcm_path) for dcm_path in natsorted(glob(os.path.join(series_dir, "*.dcm")))]
 51    slices.sort(key=lambda dcm: float(dcm.ImagePositionPatient[2]))
 52
 53    volume = np.stack([dcm.pixel_array for dcm in slices]).astype("float32")
 54    volume = volume * float(slices[0].RescaleSlope) + float(slices[0].RescaleIntercept)
 55    volume = np.round(volume).astype("int16")
 56
 57    orientation = np.round([float(v) for v in slices[0].ImageOrientationPatient]).astype("int").tolist()
 58    return volume, orientation
 59
 60
 61def _preprocess_nih_pancreas(dicom_dir, label_dir, csv_path, preprocessed_dir):
 62    import h5py
 63    import nibabel as nib
 64
 65    with open(csv_path, "r") as f:
 66        subject_ids = {row["Series UID"]: row["Subject ID"] for row in csv.DictReader(f)}
 67
 68    os.makedirs(preprocessed_dir, exist_ok=True)
 69    for series_dir in tqdm(natsorted(glob(os.path.join(dicom_dir, "*"))), desc="Preprocess NIH Pancreas-CT"):
 70        subject_id = subject_ids[os.path.basename(series_dir)]
 71        out_path = os.path.join(preprocessed_dir, f"{subject_id}.h5")
 72        if os.path.exists(out_path):
 73            continue
 74
 75        volume, orientation = _load_dicom_volume(series_dir)
 76        # The volume has axes (z, y, x) with x pointing to the patient's left and y to the anterior (DICOM LPS
 77        # convention with 'ImageOrientationPatient' [1, 0, 0, 0, -1, 0]). The labels are stored with the axis
 78        # orientation (L, A, I), so they only have to be transposed and flipped along z to match the volume.
 79        assert orientation == [1, 0, 0, 0, -1, 0], f"Unexpected image orientation for {subject_id}: {orientation}"
 80
 81        label_path = os.path.join(label_dir, f"label{subject_id.split('_')[-1]}.nii.gz")
 82        label_nifti = nib.load(label_path)
 83        assert nib.aff2axcodes(label_nifti.affine) == ("L", "A", "I"), f"Unexpected label axes for {subject_id}"
 84        labels = np.asarray(label_nifti.dataobj).astype("uint8").transpose(2, 1, 0)[::-1]
 85        assert labels.shape == volume.shape, f"Shape mismatch for {subject_id}: {labels.shape} vs {volume.shape}"
 86
 87        with h5py.File(out_path, "w") as f:
 88            f.create_dataset("raw", data=volume, compression="gzip")
 89            f.create_dataset("labels", data=labels, compression="gzip")
 90
 91
 92def get_nih_pancreas_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 93    """Download the NIH Pancreas-CT dataset.
 94
 95    Args:
 96        path: Filepath to a folder where the data is downloaded for further processing.
 97        download: Whether to download the data if it is not present.
 98
 99    Returns:
100        Filepath where the preprocessed data is stored.
101    """
102    preprocessed_dir = os.path.join(path, "preprocessed")
103    if os.path.exists(preprocessed_dir):
104        return preprocessed_dir
105
106    os.makedirs(path, exist_ok=True)
107
108    # Download the labels.
109    label_dir = os.path.join(path, "TCIA_pancreas_labels-02-05-2017")
110    if not os.path.exists(label_dir):
111        zip_path = os.path.join(path, "TCIA_pancreas_labels-02-05-2017-1.zip")
112        util.download_source(path=zip_path, url=URLS["labels"], download=download, checksum=CHECKSUMS["labels"])
113        util.unzip(zip_path=zip_path, dst=path)
114
115    # Download the DICOM series from the TCIA manifest.
116    dicom_dir = os.path.join(path, "dicom")
117    csv_path = os.path.join(path, "nih_pancreas_series")
118    util.download_source_tcia(
119        path=os.path.join(path, "Pancreas-CT-20200910.tcia"), url=URLS["images"], dst=dicom_dir,
120        csv_filename=csv_path, download=download,
121    )
122
123    _preprocess_nih_pancreas(dicom_dir, label_dir, f"{csv_path}.csv", preprocessed_dir)
124    return preprocessed_dir
125
126
127def get_nih_pancreas_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
128    """Get paths to the NIH Pancreas-CT data.
129
130    Args:
131        path: Filepath to a folder where the data is downloaded for further processing.
132        download: Whether to download the data if it is not present.
133
134    Returns:
135        List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels').
136    """
137    data_dir = get_nih_pancreas_data(path, download)
138    volume_paths = natsorted(glob(os.path.join(data_dir, "*.h5")))
139    return volume_paths
140
141
142def get_nih_pancreas_dataset(
143    path: Union[os.PathLike, str],
144    patch_shape: Tuple[int, ...],
145    resize_inputs: bool = False,
146    download: bool = False,
147    **kwargs
148) -> Dataset:
149    """Get the NIH Pancreas-CT dataset for pancreas segmentation.
150
151    Args:
152        path: Filepath to a folder where the data is downloaded for further processing.
153        patch_shape: The patch shape to use for training.
154        resize_inputs: Whether to resize inputs to the desired patch shape.
155        download: Whether to download the data if it is not present.
156        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
157
158    Returns:
159        The segmentation dataset.
160    """
161    volume_paths = get_nih_pancreas_paths(path, download)
162
163    if resize_inputs:
164        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
165        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
166            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
167        )
168
169    return torch_em.default_segmentation_dataset(
170        raw_paths=volume_paths,
171        raw_key="raw",
172        label_paths=volume_paths,
173        label_key="labels",
174        patch_shape=patch_shape,
175        is_seg_dataset=True,
176        **kwargs
177    )
178
179
180def get_nih_pancreas_loader(
181    path: Union[os.PathLike, str],
182    batch_size: int,
183    patch_shape: Tuple[int, ...],
184    resize_inputs: bool = False,
185    download: bool = False,
186    **kwargs
187) -> DataLoader:
188    """Get the NIH Pancreas-CT dataloader for pancreas segmentation.
189
190    Args:
191        path: Filepath to a folder where the data is downloaded for further processing.
192        batch_size: The batch size for training.
193        patch_shape: The patch shape to use for training.
194        resize_inputs: Whether to resize inputs to the desired patch shape.
195        download: Whether to download the data if it is not present.
196        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
197
198    Returns:
199        The DataLoader.
200    """
201    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
202    dataset = get_nih_pancreas_dataset(path, patch_shape, resize_inputs, download, **ds_kwargs)
203    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URLS = {'images': 'https://www.cancerimagingarchive.net/wp-content/uploads/Pancreas-CT-20200910.tcia', 'labels': 'https://www.cancerimagingarchive.net/wp-content/uploads/TCIA_pancreas_labels-02-05-2017-1.zip'}
CHECKSUMS = {'images': None, 'labels': 'cf8a553c37c80e3840ce7392987b308f7a98cd5f50019511b2ac0f3a54b0934b'}
def get_nih_pancreas_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 93def get_nih_pancreas_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 94    """Download the NIH Pancreas-CT dataset.
 95
 96    Args:
 97        path: Filepath to a folder where the data is downloaded for further processing.
 98        download: Whether to download the data if it is not present.
 99
100    Returns:
101        Filepath where the preprocessed data is stored.
102    """
103    preprocessed_dir = os.path.join(path, "preprocessed")
104    if os.path.exists(preprocessed_dir):
105        return preprocessed_dir
106
107    os.makedirs(path, exist_ok=True)
108
109    # Download the labels.
110    label_dir = os.path.join(path, "TCIA_pancreas_labels-02-05-2017")
111    if not os.path.exists(label_dir):
112        zip_path = os.path.join(path, "TCIA_pancreas_labels-02-05-2017-1.zip")
113        util.download_source(path=zip_path, url=URLS["labels"], download=download, checksum=CHECKSUMS["labels"])
114        util.unzip(zip_path=zip_path, dst=path)
115
116    # Download the DICOM series from the TCIA manifest.
117    dicom_dir = os.path.join(path, "dicom")
118    csv_path = os.path.join(path, "nih_pancreas_series")
119    util.download_source_tcia(
120        path=os.path.join(path, "Pancreas-CT-20200910.tcia"), url=URLS["images"], dst=dicom_dir,
121        csv_filename=csv_path, download=download,
122    )
123
124    _preprocess_nih_pancreas(dicom_dir, label_dir, f"{csv_path}.csv", preprocessed_dir)
125    return preprocessed_dir

Download the NIH Pancreas-CT 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_nih_pancreas_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
128def get_nih_pancreas_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
129    """Get paths to the NIH Pancreas-CT data.
130
131    Args:
132        path: Filepath to a folder where the data is downloaded for further processing.
133        download: Whether to download the data if it is not present.
134
135    Returns:
136        List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels').
137    """
138    data_dir = get_nih_pancreas_data(path, download)
139    volume_paths = natsorted(glob(os.path.join(data_dir, "*.h5")))
140    return volume_paths

Get paths to the NIH Pancreas-CT 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_nih_pancreas_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, ...], resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
143def get_nih_pancreas_dataset(
144    path: Union[os.PathLike, str],
145    patch_shape: Tuple[int, ...],
146    resize_inputs: bool = False,
147    download: bool = False,
148    **kwargs
149) -> Dataset:
150    """Get the NIH Pancreas-CT dataset for pancreas segmentation.
151
152    Args:
153        path: Filepath to a folder where the data is downloaded for further processing.
154        patch_shape: The patch shape to use for training.
155        resize_inputs: Whether to resize inputs to the desired patch shape.
156        download: Whether to download the data if it is not present.
157        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
158
159    Returns:
160        The segmentation dataset.
161    """
162    volume_paths = get_nih_pancreas_paths(path, download)
163
164    if resize_inputs:
165        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
166        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
167            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
168        )
169
170    return torch_em.default_segmentation_dataset(
171        raw_paths=volume_paths,
172        raw_key="raw",
173        label_paths=volume_paths,
174        label_key="labels",
175        patch_shape=patch_shape,
176        is_seg_dataset=True,
177        **kwargs
178    )

Get the NIH Pancreas-CT dataset for pancreas segmentation.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • patch_shape: The patch shape to use for training.
  • 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_nih_pancreas_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, ...], resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
181def get_nih_pancreas_loader(
182    path: Union[os.PathLike, str],
183    batch_size: int,
184    patch_shape: Tuple[int, ...],
185    resize_inputs: bool = False,
186    download: bool = False,
187    **kwargs
188) -> DataLoader:
189    """Get the NIH Pancreas-CT dataloader for pancreas segmentation.
190
191    Args:
192        path: Filepath to a folder where the data is downloaded for further processing.
193        batch_size: The batch size for training.
194        patch_shape: The patch shape to use for training.
195        resize_inputs: Whether to resize inputs to the desired patch shape.
196        download: Whether to download the data if it is not present.
197        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
198
199    Returns:
200        The DataLoader.
201    """
202    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
203    dataset = get_nih_pancreas_dataset(path, patch_shape, resize_inputs, download, **ds_kwargs)
204    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the NIH Pancreas-CT dataloader for pancreas 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.
  • 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.